librespot

repository·dev·Indexed 27 days ago

https://github.com/librespot-org/librespot

An open source client library for Spotify, version 0.8.0, featuring support for Spotify Connect. It allows developers to create custom Spotify Connect devices using the Spirc entrypoint, handle audio stream decryption via AudioDecrypt, and manage audio fetching and streaming through AudioFile and StreamLoaderController.

Tokens
19.2K
Snippets
29
Records
128
Agent score
92%

What's inside librespot

  1. Understand the Dealer WebSocket interface

    dev

    The Dealer is a WebSocket that represents the player as a Spotify Connect device. It is primarily used to receive updates rather than to update the state.

    Communication via the Dealer consists of two types of interactions:

    • Messages: Fire-and-forget updates that do not require a response. These are used for informational updates (e.g., changes to playlists, liked songs) or fire-and-forget commands (e.g., volume updates, logout requests).
    • Requests: Commands that modify the player state. These expect a reply indicating whether the request was processed successfully or failed.

    Note on Encoding: Because the device supports gzip, payloads may be BASE64 encoded and gzip compressed. If compressed, the headers will include Transfer-Encoding: gzip.

  2. Install Rust and development tools

    dev

    To compile librespot, you must install Rust using rustup. It is also recommended to install rustfmt and clippy to ensure code consistency, as the CI enforces these rules.

    Install Rust: Use rustup.rs to install the Rust toolchain.

    Install formatting and linting tools:

    rustup component add rustfmt
    rustup component add clippy
  3. Publish the protocol crate with --no-verify

    dev
    The protocol package requires a special flag when publishing to crates.io because its build script modifies the source during compile time. Use cargo publish --no-verify within the protocol crate directory to avoid verification errors.
  4. Create a GitHub release to trigger automated publishing

    dev

    You can trigger the automated publishing workflow (which handles crate and binary publishing) by creating a new release on GitHub as a draft.

    Steps:

    1. Create a new release via the GitHub UI.
    2. Set the tag and release name to v<version> (e.g., v0.8.0), where <version> matches the binary version.
    3. Copy the entries from the changelog into the release notes.
    4. Save the release as a draft.

    The workflow will automatically determine which crates changed, publish them in the correct order, and publish the binary. Once the workflow completes successfully, you can publish the version.

  5. Resolve Spotify Access Points

    dev

    To connect to Spotify's servers, you must first locate an Access Point (AP).

    1. Perform an HTTP GET request to http://apresolve.spotify.com to retrieve a JSON list of hostname and port combinations.
    2. Randomly select one AP from the returned list.
    3. If http://apresolve.spotify.com is unresponsive, use ap.spotify.com:443 as a fallback.

    Note: Connections are established via a bare TCP socket. Even if the AP uses ports 80 or 443, do not use HTTP or TLS for the connection itself.

  6. Manual publishing order for crates.io

    dev

    Due to internal dependencies between local packages, publishing to crates.io must follow a specific sequence. If you are publishing crates manually, use the following order:

    1. protocol
    2. core
    3. audio
    4. metadata
    5. playback
    6. connect
    7. librespot
  7. Run librespot

    dev

    After compiling, you can run the binary from the target directory.

    Running a debug build:

    ./target/debug/librespot

    Note on Audio Quality: Debug builds may cause buffer underruns and choppy audio when dithering is enabled (enabled by default). To mitigate this, you can disable dithering using the --dither none flag.

    To view all available runtime options, run:

    ./target/debug/librespot -h
  8. Compile librespot

    dev

    Build the project using cargo. By default, librespot compiles with native-tls, rodio-backend, and with-libmdns.

    Important: If you use --no-default-features, you must specify at least one TLS backend, one audio backend, and one discovery backend, otherwise compilation will fail.

    Standard Builds

    • Debug build: cargo build (faster, more verbose, recommended for development/bug reports).
    • Release build: cargo build --release.

    Custom Feature Builds

    To build with specific backends (e.g., ALSA audio, libmdns discovery, and native-tls):

    cargo build --no-default-features --features "native-tls alsa-backend with-libmdns"

    Apple Silicon (M1+) Cross-Compilation

    To build for x86_64 on an ARM-based Mac:

    1. Install the target: rustup target install x86_64-apple-darwin
    2. Build: cargo build --target=x86_64-apple-darwin --release
    3. Create a universal binary using lipo.
  9. Configure TLS backends

    dev

    Librespot requires a TLS implementation. You must choose exactly one of the following mutually exclusive options. Note: Attempting to enable both will cause a compile-time error.

    native-tls (Default)

    Uses the system's native TLS (OpenSSL on Linux, Security.framework on macOS, SChannel on Windows). Best for maximum compatibility and system certificate integration.

    Dependencies:

    • Debian/Ubuntu: sudo apt-get install libssl-dev pkg-config
    • Fedora: sudo dnf install openssl-devel pkg-config

    rustls-tls

    A Rust-based implementation. No additional system dependencies required. Choose one of two certificate store options:

    • rustls-tls-native-roots: Uses system certificate stores.
    • rustls-tls-webpki-roots: Uses Mozilla's webpki certificate store (best for containers/embedded/reproducible builds).

    Build Commands for TLS

    # Default (native-tls)
    cargo build
    
    # Explicitly use native-tls
    cargo build --no-default-features --features "native-tls rodio-backend with-libmdns"
    
    # Use rustls-tls with native certificate stores
    cargo build --no-default-features --features "rustls-tls-native-roots rodio-backend with-libmdns"
    
    # Use rustls-tls with Mozilla's webpki certificate store
    cargo build --no-default-features --features "rustls-tls-webpki-roots rodio-backend with-libmdns"
  10. Authenticate with the Access Point (AP)

    dev

    Once a connection is established, the client authenticates with the Access Point (AP) by sending a ClientResponseEncrypted message (packet type 0xab).

    Upon receiving the authentication attempt, the AP will respond with one of the following:

    • APWelcome (packet type 0xac): Authentication successful.
    • APLoginFailed (packet type 0xad): Authentication failed.
  11. Use Zeroconf-based Authentication

    dev

    Zeroconf-based authentication allows headless Spotify Connect devices to be authenticated by a controller (like a phone or computer) on the local network.

    1. The receiver exposes an HTTP server with service type _spotify-connect._tcp.
    2. The controller calls getInfo on the HTTP server to retrieve receiver information, including its DH public key.
    3. The controller calls addUser to send the username, the controller's DH public key, and an encrypted blob.

    The receiver must decrypt the encrypted_blob (which is base64 decoded) using the following logic involving the shared_secret from the DH key exchange:

    # encrypted_blob is the blob sent by the controller, decoded using base64
    # shared_secret is the result of the DH key exchange
    
    IV = encrypted_blob[:0x10]
    expected_mac = encrypted_blob[-0x14:]
    encrypted = encrypted_blob[0x10:-0x14]
    
    base_key       = SHA1(shared_secret)
    checksum_key   = HMAC-SHA1(base_key, "checksum")
    encryption_key = HMAC-SHA1(base_key, "encryption")[:0x10]
    
    mac = HMAC-SHA1(checksum_key, encrypted)
    assert mac == expected_mac
    
    blob = AES128-CTR-DECRYPT(encryption_key, IV, encrypted)