Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 138 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# Contributing

## What this library is

- A headless Spotify client, allowing you to **authenticate and retrieve a decrypted audio stream for any track**.
- *Not* a standalone audio player: the **provided stream must be piped to another application** (like `ffplay`) or handled by a server to be played.

## Environment setup

### Prerequisites
- Python 3.10+

### Install runtime packages

```sh
pip install -r requirements.txt
```

### Install protoc

> This step is **only needed if you're changing any `.proto` serialization schema files**,
> which will subsequently require using the protoc compiler to generate updated versions of
> the `*_pb2.py` Python stubs that implement serialization/deserialization for those schemas.

- Go to the [protobuf release matching the version pinned in `requirements.txt`](https://github.com/protocolbuffers/protobuf/releases/tag/v3.20.1).
- Download and install the `protoc-*.zip` file meant for your platform.

After modifying the `.proto` files you need to, **make sure to follow [these steps](#protocol-buffer-generation) to regenerate the Python stubs**.

## Protocol buffer generation

> These steps are only necessary after changing `.proto` files.

- From the repository root, conveniently recompile all `.proto` schema files with this command:

```bash
find proto -name "*.proto" | xargs protoc -I=proto --python_out=librespot/proto
```

- Alternatively, to recompile a single file (e.g. `proto/metadata.proto`), run:

```bash
protoc -I=proto --python_out=librespot/proto proto/metadata.proto
```

- Commit both the source `.proto` and the regenerated Python output **together** so they can
be compared easily.

## Architecture

The main components are:

- **`Session` class** *(entrypoint)*

- `Session.Builder` is used to configure and create a session, via one of:

- username/password
- stored credentials
- OAuth

- An active session is **required** for all other operations.

- **`ApiClient` class**

- A high-level client for making standard HTTPS requests to Spotify's Web API endpoints (e.g., `https://spclient.wg.spotify.com`).
- Accessed via `session.api()`, it provides convenient methods like `get_metadata_4_track()` and handles client tokens automatically.

- **`MercuryClient` class**

- The low-level client for Spotify's proprietary `mercury` protocol, which uses `hm://` URIs.
- Accessed via `session.mercury()`, it handles sending and receiving messages over the main session connection for metadata lookups and subscriptions that are not available via the standard Web API.

- **`DealerClient` class**

- Manages the persistent WebSocket (`wss://`) connection to Spotify's `dealer` service.
- Accessed via `session.dealer()`, it listens for and dispatches real-time, asynchronous JSON-based events, such as remote player state changes or notifications from other connected devices.

- **`Session.Receiver` thread**

- Spawned after authentication to read every encrypted packet coming from the access point.
- Routes decoded commands to subsystems (`MercuryClient`, `AudioKeyManager`, `ChannelManager`, etc.) and responds to keep-alive pings to hold the session open.

- **Metadata types**

- The `librespot.metadata` module provides typed identifiers (`TrackId`, `AlbumId`, `PlaylistId`, `EpisodeId`, etc.) used to reference Spotify content throughout the API.
- They are constructed from Spotify identifiers, typically using one of the following methods:

- `from_uri()`: For all ID types.
- `from_base62()`: For most ID types (e.g., tracks, albums, artists).

- **`PlayableContentFeeder` class**

- Retrieves audio streams; is accessed via `session.content_feeder()`.
- `load(playable_id, audio_quality_picker, preload, halt_listener)`:

- Accepts:

- a `TrackId` or `EpisodeId` (any `PlayableId`)
- an `AudioQualityPicker`
- a `preload` flag
- an optional `HaltListener` callback (pass `None` if unneeded).

- Returns a `LoadedStream` that contains the decrypted stream together with:

- track/episode metadata
- normalization data
- transfer metrics

- **`audio` module**

- Contains tools for format selection, quality management, streaming, and decryption.
- `VorbisOnlyAudioQuality` and `LosslessOnlyAudioQuality` choose the best matching `Metadata.AudioFile` for a preferred container/quality combination.
- `CdnManager` acquires and refreshes signed CDN URLs, feeding a `Streamer` that decrypts chunks on the fly while staying seekable.

- **`AudioKeyManager` and `ChannelManager`**

- Handle the low-level transport for protected audio: `AudioKeyManager` requests AES keys, and `ChannelManager` can stream encrypted chunks directly from the access point when CDN delivery is unavailable.
- Both are driven transparently by `PlayableContentFeeder`/`CdnManager`, so callers only interact with the decrypted `LoadedStream`.

- **`EventService` class**

- Asynchronous publisher that emits telemetry (e.g., fetch metrics, playback events) to `hm://event-service/v1/events` via Mercury.
- Accessible through `session.event_service()` for consumers that need to forward custom events.

- **`TokenProvider` class**

- Caches Login5 access tokens per scope, refreshing them proactively as they near expiry.
- Used by `ApiClient` to supply the correct `Authorization` headers for Spotify Web API calls.

- **`SearchManager` class**

- High-level wrapper around `hm://searchview/km/v4/search/...` requests sent over Mercury.
- Fills in username, locale, and country defaults from the current session before dispatching the call.

- **OAuth tokens for Spotify Web API**

- Can be obtained via `session.tokens().get(scope)`
- Enable authenticated API calls for operations like search, playlist management, and user data access
Loading