Skip to main content

ytlounge

Client for the YouTube Lounge API: the protocol behind the "Play on TV" button. Pair with a screen once, then play videos on it and manage its queue from Python. Synchronous, one dependency (httpx), no device code.

from ytlounge import Lounge

with Lounge(name="my-remote") as lounge:
    screen = lounge.pair("123 456 789")     # code from Settings › Link with TV code
    lounge.play(screen, ["dQw4w9WgXcQ"])    # play now
    lounge.add(screen, ["jNQXAC9IVRw"])     # append to the queue

Screen is a small frozen dataclass; keep screen_id and you can always get a fresh token with lounge.refresh(screen). Tokens live about two weeks, Screen.is_expired() tells you when.

Install

pip install ytlounge

Python 3.11 or newer.

What it does and does not do

  • Pair with a TV code, refresh a token from a screen id, open a session, play a list of videos (with a start index and start time), append videos to the queue.
  • ytlounge.video.parse_video turns an id or any of the usual YouTube URL forms into a Video with an optional start time.
  • It does not find TVs, launch apps or store anything. That is the job of a tool built on top, such as yttv, which adds Cast, Apple TV and DIAL backends, a cache and a command line.

The Lounge API is not documented by Google and can change at any time. Everything below was verified on the wire in September 2026 and is kept as assertions in the tests; when something breaks, that is where to look.

The protocol, as verified

Field Lives Comes from
screen_id for good, survives a cold start of the TV one pairing with a TV code
lounge_token about 13 days get_lounge_token_batch from the screen id
SID, gsessionid one session a bind at the start of every play/add
  • Pair: POST /api/lounge/pairing/get_screen with pairing_code=. The expiration here is a string, on the token endpoint a number.
  • Refresh: POST /api/lounge/pairing/get_lounge_token_batch with screen_ids=. A refresh does not revoke earlier tokens.
  • Session: POST /api/lounge/bc/bind?CVER=1&RID=1&VER=8&app=youtube-desktop&device=REMOTE_CONTROL&id=remote&loungeIdToken=…&name=… with an empty body. The endpoint insists on a Content-Length header even then (httpx always sends one). The reply is framed: a decimal length on its own line, then that many characters of a JSON array of numbered messages; ["c", SID, …] and ["S", gsessionid] are in the first frame, followed by the screen's status and current queue.
  • Play: POST bind?CVER=1&RID=2&SID=…&VER=8&gsessionid=…&loungeIdToken=…, form body count=1&req0__sc=setPlaylist&req0_currentIndex=0&req0_currentTime=0&req0_videoId=…&req0_videoIds=a,b,c.
  • Add: same query, req0__sc=addVideo&req0_videoId=…, one request per video with a random 2–5 s pause before each. The pause is not cosmetic: without it consecutive adds race on the screen and the queue comes out incomplete or reordered.

Development

pixi run test                                   # fixture tests
YTLOUNGE_SCREEN_ID= pixi run test-device       # against the real API, no TV changes
pixi run check                                  # build and validate the artifacts

Origins

The Lounge protocol was reverse-engineered independently by several people. This client contains none of their code, but learned the protocol from Marco Lucidi's ytcast, whose requests were the reference for verifying this implementation on the wire, and through it from the sources ytcast credits:

The YouTube URL forms in the tests come from this gist.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ytlounge-0.1.0.tar.gz (16.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ytlounge-0.1.0-py3-none-any.whl (11.6 kB view details)

Uploaded Python 3

File details

Details for the file ytlounge-0.1.0.tar.gz.

File metadata

  • Download URL: ytlounge-0.1.0.tar.gz
  • Upload date:
  • Size: 16.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.14.7

File hashes

Hashes for ytlounge-0.1.0.tar.gz
Algorithm Hash digest
SHA256 bf38392f8d8691d242249357bcf110a36de050b242023ce9d533e04a884c56e7
MD5 cbaff68a85963036f2847696dea4c7f8
BLAKE2b-256 d8db41f1b44c871bdcafc430c0f32eea07f8cf448c20ccaa1bdb63af72d41746

See more details on using hashes here.

File details

Details for the file ytlounge-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: ytlounge-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 11.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.14.7

File hashes

Hashes for ytlounge-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 e385e67e53ec2fb764eb9fc104c3c1aad814d4fd2daf3aaa26d80bc0755b7196
MD5 627e01a4ea70c57c7be253391cf7ac9e
BLAKE2b-256 48dc95ee1c5409e0edc911f0fe1b749f7600104762fc62577515af40196d2a21

See more details on using hashes here.

Release history Release notifications | RSS feed

0.2.0

2 files

This release

0.1.0 This release

2 files

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