Skip to main content

YTMD SDK

Python SDK for controlling YouTube Music Desktop App (YTMD) v2+ via its Companion Server API.

Requirements

  • Python 3.8+
  • YTMD v2.0.0 or later (Companion Server must be enabled in YTMD settings)

Installation

pip install ytmd-sdk

Quick Start

Your application is responsible for persisting and loading the token. Pass it to the SDK via update_token().

import os
from ytmd_sdk import Events, YTMD, Parser
from time import sleep

TOKEN_PATH = "/path/to/token.txt"

# --- Your application loads the token ---
token = None
if os.path.exists(TOKEN_PATH):
    token = open(TOKEN_PATH).read().strip() or None

# --- Pass it to the SDK ---
ytmd = YTMD("my-app-id", "My App Name", "1.0.0")

if token:
    ytmd.update_token(token)

if not token or not ytmd.is_token_valid():
    # Opens an approval popup in YTMD. The user has ~30 seconds to approve.
    token = ytmd.authenticate()
    # --- Your application persists the new token ---
    with open(TOKEN_PATH, "w") as f:
        f.write(token)

def on_connect():
    print("Connected to YTMD")

def on_disconnect():
    print("Disconnected from YTMD")

def on_state_update(data):
    parser = Parser(data)
    print(f"Track:    {parser.video_state.title} by {parser.video_state.author}")
    print(f"State:    {parser.player_state.state}")
    print(f"Progress: {parser.player_state.video_progress}s")
    print(f"Volume:   {parser.player_state.volume}")
    print(f"Repeat:   {parser.player_state.repeatMode}")

ytmd.register_event(Events.connect, on_connect)
ytmd.register_event(Events.disconnect, on_disconnect)
ytmd.register_event(Events.state_update, on_state_update)

ytmd.connect()
sleep(60)

If YTMD rejects the token (e.g. after a server restart), call revoke_token() to clear it in-memory, then handle the file and re-authenticate in your application:

ytmd.revoke_token()
os.remove(TOKEN_PATH)          # your application cleans up the stale file
token = ytmd.authenticate()
with open(TOKEN_PATH, "w") as f:
    f.write(token)

API Reference

YTMD(app_id, app_name, app_version, host="127.0.0.1", port=9863, token=None, logging=False)

Constructs the client. All network calls use a shared requests.Session for connection pooling.

Parameter Type Description
app_id str Unique identifier for your application (e.g. "my-app")
app_name str Human-readable application name shown in YTMD's approval popup
app_version str Application version string
host str YTMD host address (default "127.0.0.1")
port int YTMD Companion Server port (default 9863)
token str Pre-existing token; skips authenticate() if provided
logging bool Enable python-socketio debug logging (default False)

Authentication

Method Returns Description
authenticate() str Full auth handshake — requests a code, waits for user approval in YTMD (~30s), returns token
update_token(token) None Register a token on this instance and set the Authorization header
revoke_token() None Clear the in-memory token and Authorization header; does not touch the filesystem
is_token_valid() bool Returns False only on a definitive HTTP 401; treats network errors as valid to avoid unnecessary re-auth

Token persistence (reading and writing token files) is the caller's responsibility. The SDK provides update_token() to register a loaded token and revoke_token() to clear it; file I/O should be implemented in your application layer.

Connection

Method Description
register_event(event, callback) Bind a callback to a Socket.IO event (use Events.* constants)
connect() Open a WebSocket connection to the YTMD realtime namespace
update_endpoint(host, port=9863) Change the target host/port after construction (e.g. from user settings)

Player Commands

Method Description
toggle_playback() Play or pause
play() Resume playback
pause() Pause playback
next() Skip to the next track
previous() Go back to the previous track
volume_up() Increase volume by 10%
volume_down() Decrease volume by 10%
set_volume(volume) Set volume (0-100)
mute() Mute the player
unmute() Unmute the player
seek_to(time) Seek to a position in seconds
shuffle() Toggle shuffle
repeat_mode(mode) Set repeat mode: 0 = None, 1 = All, 2 = One
play_index(index) Play a specific item in the queue by index
toggle_like() Toggle the like status of the current track
toggle_dislike() Toggle the dislike status of the current track
change_video(video_id=None, playlist_id=None) Play a specific YouTube video or playlist by ID

State and Playlists

Method Returns Description
get_state() Response Current player and video state as a JSON response
get_playlists() Response User's playlists as a JSON response
get_version() list[str] API versions supported by the connected YTMD instance

Cover Art

Method Returns Description
fetch_cover_art(url, timeout=5) bytes Fetch raw image bytes from a thumbnail URL provided in the state update. Encoding (e.g. base64) is left to the caller. Raises requests.HTTPError on non-2xx responses.

Events

Use the Events class constants when calling register_event():

Constant Socket.IO Event Fired when
Events.connect connect WebSocket connection established
Events.disconnect disconnect WebSocket connection lost
Events.connect_error connect_error Connection attempt failed
Events.error error Server-side error
Events.state_update state-update Player state changes (track, volume, position, etc.)
Events.playlist_created playlist-created A new playlist was created
Events.playlist_deleted playlist-deleted A playlist was deleted

Parser

Parser(data) wraps the raw JSON dict received from a state-update event into typed objects.

parser = Parser(data)

# PlayerState fields
parser.player_state.state            # "Playing", "Paused", "Buffering", or "Unknown"
parser.player_state.video_progress   # float, seconds elapsed
parser.player_state.volume           # int, 0-100
parser.player_state.muted            # bool
parser.player_state.adPlaying        # bool
parser.player_state.repeatMode       # "None", "All", "One", or "Unknown"
parser.player_state.auto_play        # bool
parser.player_state.isGenerating     # bool
parser.player_state.isInfinite       # bool
parser.player_state.selectedItemIndex  # int, index of the active item in the queue
parser.player_state.queue            # list[queueItem]

# VideoState fields
parser.video_state.id                # str, YouTube video ID
parser.video_state.title             # str
parser.video_state.author            # str, channel/artist name
parser.video_state.album             # str
parser.video_state.album_id          # str
parser.video_state.channel_id        # str
parser.video_state.like_status       # str: "Like", "Dislike", "Indifferent", or "Unknown"
parser.video_state.duration_seconds  # int
parser.video_state.thumbnails        # list[Thumbnail], ordered smallest to largest

Each Thumbnail has .url (str), .width (int), and .height (int). The last item in thumbnails is always the highest-resolution image available.

Change Log

1.2.1

Token persistence moved to the application layer

save_token(path), load_token(path), and clear_token(path) have been removed from the YTMD class. Token file I/O is an application-level concern and does not belong in a generic client library.

Callers are responsible for reading and writing the token file. Use update_token(token) to register a loaded token on the SDK instance, and revoke_token() to clear it when a token is rejected.

  • revoke_token() replaces clear_token(path) for the in-memory side: it sets self.token = None and removes the Authorization header from the session. It does not touch the filesystem.

See the Quick Start section for a complete token persistence pattern.

1.2.0

Token lifecycle management (removed in 1.2.1 — see above)

The following methods were added in 1.2.0 and removed in 1.2.1 when token persistence was moved to the application layer:

  • save_token(path), load_token(path), clear_token(path)

Still present from this release:

  • is_token_valid() makes a lightweight GET /state call to verify the current token is accepted by YTMD. It returns False only on a definitive HTTP 401; transient network failures return True so a temporary connection drop does not force unnecessary re-authentication.

Cover art retrieval

  • fetch_cover_art(url, timeout=5) fetches raw image bytes from any thumbnail URL provided in the state-update payload. The SDK's shared HTTP session is reused for connection pooling. Encoding (e.g. converting to base64 for embedding in a UI) is intentionally left to the caller so the SDK remains format-agnostic. Raises requests.HTTPError on non-2xx responses and requests.RequestException on network failures.

    Note: YTMD does provide the asset URI so you can simply use the URI for retrieving cover art, pulling it as a byte array may improve performance in some applications.

Video and playlist control

  • change_video(video_id=None, playlist_id=None) sends a changeVideo command to YTMD, allowing the caller to start a specific YouTube video or playlist by ID. Either argument may be None if not needed.

Runtime endpoint reconfiguration

  • update_endpoint(host, port=9863) allows the target YTMD host and port to be changed after the client is constructed. This is useful when the server address is read from user-facing settings at runtime rather than being fixed at startup.

Parser: VideoState thumbnails

The VideoState object now exposes a thumbnails field containing a list of Thumbnail objects parsed from the YTMD state payload. Each Thumbnail carries a .url, .width, and .height. The thumbnails are ordered from smallest to largest, so thumbnails[-1] is always the highest-resolution image available and is the recommended source for cover art display.

1.0.0

Initial release. Covered basic connection, authentication handshake, core playback commands, and the Parser class for PlayerState and VideoState.

Bugs and Suggestions

If you have any bugs or suggestions, feel free to open an issue or a pull request.

Metadata

Release files for ytmd-sdk 1.2.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ytmd-sdk 1.2.1
File Size Uploaded Provenance
ytmd_sdk-1.2.1.tar.gz 29.5 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for ytmd-sdk 1.2.1
File Interpreter ABI Platform
ytmd_sdk-1.2.1-py3-none-any.whl Python 3 none any Details

Total release size: 54.1 kB

Release files / ytmd_sdk-1.2.1.tar.gz

Download URL ytmd_sdk-1.2.1.tar.gz
Size 29.5 kB
Tags Source
SHA-256 checksum
How to use checksums
7bec845c815387275f389162bb4c01d27f2fc983e3d09267b46d751c12419640
BLAKE2b-256 checksum
How to use checksums
07f693319ff0d822fecfe76209389817a8ba13e5aca138a6e043c065d113a84c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on May 22, 2026.

Transparency log

Release files / ytmd_sdk-1.2.1-py3-none-any.whl

Download URL ytmd_sdk-1.2.1-py3-none-any.whl
Size 24.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a9e3027f25a33701b66935d0b9615e08622457c03c3c9c74909b4a8e02ecd4a9
BLAKE2b-256 checksum
How to use checksums
5bb9caadcdcdc138cb57699c57a38cb47973c24fbf80e8504d5364cceb3f773b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on May 22, 2026.

Transparency log

Security for ytmd-sdk 1.2.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for ytmd_sdk-1.2.1.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on May 22, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page