### Install PyYtLounge using pip Source: https://pyytlounge.readthedocs.io/en/latest/_sources/usage.rst.txt Install the PyYtLounge library using pip. This is the first step before using the library. ```console (.venv) $ pip install pyytlounge ``` -------------------------------- ### _subscribe Source: https://pyytlounge.readthedocs.io/en/latest/pyytlounge.html Starts listening for events from the screen device. Event updates will be sent to the provided event listener. ```APIDOC ## _subscribe ### Description Starts listening for events from the screen device. Event updates will be sent to the provided event listener. ### Method Asynchronous ### Raises - **NotSupportedException** – The screen does not allow the lounge client. - **NotConnectedException** – The client is not yet connected to the screen. ### Returns - **None** ``` -------------------------------- ### Get Now Playing Information Source: https://pyytlounge.readthedocs.io/en/latest/_modules/pyytlounge/wrapper.html Requests an update on the currently playing media from the screen. Use when the client is connected. ```python await self._command("getNowPlaying") ``` -------------------------------- ### Get Common Connection Parameters Source: https://pyytlounge.readthedocs.io/en/latest/_modules/pyytlounge/wrapper.html Returns a dictionary containing common parameters required for establishing and maintaining a connection to the YouTube lounge service. These include device name, tokens, session IDs, and API versions. ```python def _common_connection_parameters(self) -> Dict[str, Any]: return { "name": self.device_name, "loungeIdToken": self.auth.lounge_id_token, "SID": self._sid, "AID": self._last_event_id, "gsessionid": self._gsession, "device": "REMOTE_CONTROL", "app": "youtube-desktop", "VER": "8", "v": "2", } ``` -------------------------------- ### Subscribe to Events Source: https://pyytlounge.readthedocs.io/en/latest/_modules/pyytlounge/wrapper.html Starts listening for events from the YouTube lounge service. Event updates are sent to the event_listener provided during object creation. ```python async def subscribe(self) -> None: """Start listening for events. Updates will be sent to the event_listener passed when creating this object. """ ``` -------------------------------- ### subscribe Source: https://pyytlounge.readthedocs.io/en/latest/_modules/pyytlounge/wrapper.html Starts listening for events from the YouTube lounge service. Event updates are sent to the provided event listener. ```APIDOC ## subscribe ### Description Start listening for events. Updates will be sent to the event_listener passed when creating this object. ### Method GET ### Endpoint /events ### Parameters (No explicit parameters documented for this method in the source. Assumes internal session state is used.) ### Response (No explicit response schema documented. Assumes events are streamed.) ### Raises (No explicit exceptions documented for this method in the source.) ``` -------------------------------- ### Get Screen Device Name Source: https://pyytlounge.readthedocs.io/en/latest/_modules/pyytlounge/wrapper.html Constructs and returns the device name (e.g., 'Brand Model') from device info. Raises NotConnectedException if the screen is not yet connected or if device info is unavailable. ```python device_name = api.screen_device_name ``` -------------------------------- ### Get Screen Name Source: https://pyytlounge.readthedocs.io/en/latest/_modules/pyytlounge/wrapper.html Retrieves the screen name as returned by YouTube. Raises NotLinkedException if the screen is not yet linked. ```python screen_name = api.screen_name ``` -------------------------------- ### YtLoungeApi Initialization and Lifecycle Source: https://pyytlounge.readthedocs.io/en/latest/_modules/pyytlounge/wrapper.html Demonstrates how to initialize and manage the lifecycle of the YtLoungeApi instance using an asynchronous context manager. ```APIDOC ## YtLoungeApi Usage ### Description Initialize the API and use it within an async context manager to ensure proper connection and cleanup. ### Method Asynchronous context manager (`async with`) ### Example ```python import asyncio from pyytlounge.wrapper import YtLoungeApi async def main(): api = YtLoungeApi(device_name="MyDevice") async with api: # Use the api object here print(f"Is paired: {api.paired()}") await api.pair_with_screen_id("SCREEN_ID_HERE") print(f"Is paired after pairing: {api.paired()}") asyncio.run(main()) ``` ### Notes The `__aenter__` method sets up the aiohttp client session, and `__aexit__` ensures it is closed properly. ``` -------------------------------- ### Initialize YtLoungeApi Source: https://pyytlounge.readthedocs.io/en/latest/_modules/pyytlounge/wrapper.html Create an instance of the API to connect to a single YouTube screen. The device name is displayed on the screen. An event listener can be provided to receive updates, and a custom logger can be used. ```python api = YtLoungeApi("My Device Name", event_listener=my_listener, logger=my_logger) ``` -------------------------------- ### Initialize YtLoungeApi with async with Source: https://pyytlounge.readthedocs.io/en/latest/_sources/usage.rst.txt Create an instance of YtLoungeApi using an asynchronous context manager. This ensures proper cleanup of the aiohttp session upon exiting the block. ```python async with YtLoungeApi('Test client') as api: ... ``` -------------------------------- ### _play Source: https://pyytlounge.readthedocs.io/en/latest/pyytlounge.html Sends a play command to the currently connected screen device. ```APIDOC ## _play ### Description Sends a play command to the currently connected screen device. ### Method Asynchronous ### Returns - **bool** - True if the play command was sent successfully, False otherwise. ``` -------------------------------- ### Migrate to Event Listener (v3.x.x) Source: https://pyytlounge.readthedocs.io/en/latest/updating.html Illustrates the transition from the older subscribe() callback method to the new EventListener subclass approach for handling playback and now playing events. ```python async def receive_state(state: PlaybackState): print(f"New state: {state}") await api.subscribe(receive_state) ``` ```python from pyytlounge import EventListener class CustomListener(EventListener): def __init__(self): self.last_video_id: Optional[str] = None async def playback_state_changed(self, event: PlaybackStateEvent) -> None: """Called when playback state changes (position, play/pause)""" print( f"New state: {event.state} = id: {self.last_video_id} pos: {event.current_time} duration: {event.duration}" ) async def now_playing_changed(self, event: NowPlayingEvent) -> None: """Called when active video changes""" print( f"New state: {event.state} = id: {event.video_id} pos: {event.current_time} duration: {event.duration}" ) self.last_video_id = event.video_id with YtLoungeApi("Some device", CustomListener()) as api: ... await api.subscribe() ``` -------------------------------- ### Get YouTube Video Thumbnail URL Source: https://pyytlounge.readthedocs.io/en/latest/_modules/pyytlounge/api.html Generates a URL for a video's thumbnail. The thumbnail index can be used to select different thumbnail resolutions. ```python import aiohttp api_base = "https://www.youtube.com/api/lounge" [docs] def get_thumbnail_url(video_id: str, thumbnail_idx=0) -> str: """Returns thumbnail for given video. Use thumbnail idx to get different thumbnails.""" return f"https://img.youtube.com/vi/{video_id}/{thumbnail_idx}.jpg" ``` -------------------------------- ### _previous Source: https://pyytlounge.readthedocs.io/en/latest/pyytlounge.html Sends a command to the screen device to play the previous item in the playlist. ```APIDOC ## _previous ### Description Sends a command to the screen device to play the previous item in the playlist. ### Method Asynchronous ### Raises - **NotConnectedException** – The client is not connected to the screen. ### Returns - **bool** - True if the command was sent successfully, False otherwise. ``` -------------------------------- ### Get Screen ID from DIAL Endpoint Source: https://pyytlounge.readthedocs.io/en/latest/discovery.html Use this snippet after obtaining a DIAL endpoint URL. It retrieves the screen ID and name, then pairs with the PyYtLounge API. ```python from pyytlounge.dial import get_screen_id_from_dial dial_url = ... result = get_screen_id_from_dial(dial_url) async with YtLoungeApi('Test client') as api: paired = api.pair_with_screen_id(result.screen_id, result.screen_name) print(paired) ``` -------------------------------- ### previous Source: https://pyytlounge.readthedocs.io/en/latest/_modules/pyytlounge/wrapper.html Sends the previous command to the screen to go to the previous item in the playlist. ```APIDOC ## previous ### Description Sends previous command to screen ### Method POST ### Endpoint /bc/bind ### Parameters #### Request Body - **count** (integer) - Required - 1 - **ofs** (string) - Required - The command offset. - **req0__sc** (string) - Required - "previous" #### Query Parameters - **RID** (string) - Required - The command offset. ### Response #### Success Response (200) - **bool** (boolean) - True if the command was issued successfully. ### Raises - NotConnectedException: the client is not connected to the screen ``` -------------------------------- ### get_screen_id_from_dial Source: https://pyytlounge.readthedocs.io/en/latest/_modules/pyytlounge/dial.html Attempts to retrieve the YouTube screen ID and name from a DIAL endpoint. It sends GET requests to the provided URL and the YouTube application endpoint to fetch device information and screen details. ```APIDOC ## get_screen_id_from_dial(url: str) -> Optional[DialResult] ### Description Tries to get YouTube screen id from a DIAL endpoint. ### Parameters #### Path Parameters - **url** (str) - Required - The URL of the DIAL endpoint. ### Response #### Success Response (200) - **DialResult** (object) - Contains `screen_name` (str) and `screen_id` (str) if successful. #### Response Example ```json { "screen_name": "YouTube on Living Room TV", "screen_id": "some_unique_id" } ``` #### Error Response - **None** - Returned if the request fails or the screen ID cannot be found. ``` -------------------------------- ### NotSupportedException Class Source: https://pyytlounge.readthedocs.io/en/latest/_modules/pyytlounge/exceptions.html Indicates an operation failed due to connecting to an unsupported client, such as YouTube TV for Kids. ```python [docs] class NotSupportedException(Exception): """This exception indicates an operation that failed due to connecting to an unsupported client. Some YouTube clients (currently YouTube TV for Kids) are not supported by this library and will raise this exception when attempting to connect to them.""" ``` -------------------------------- ### Get Available YouTube Captions Source: https://pyytlounge.readthedocs.io/en/latest/_modules/pyytlounge/api.html Retrieves a list of available subtitle languages for a given YouTube video using the YouTube Data API v3. Requires an API key and video ID. ```python import aiohttp api_base = "https://www.youtube.com/api/lounge" [docs] async def get_available_captions(api_key: str, video_id: str): """Uses the traditional YouTube API to enumerate available subtitle tracks.""" yt_base_url = "https://www.googleapis.com/youtube/v3/captions" params = {"part": "snippet", "videoId": video_id, "key": api_key} async with aiohttp.ClientSession() as session: async with session.get(yt_base_url, params=params) as response: if response.status != 200: raise Exception(f"Request failed with status {response.status}") data = await response.json() languages = [] for item in data.get("items", []): snippet = item.get("snippet", {}) language = snippet.get("language") if language and language not in languages: languages.append(language) return languages ``` -------------------------------- ### Send Play Video Command Source: https://pyytlounge.readthedocs.io/en/latest/_modules/pyytlounge/wrapper.html Sends the 'setPlaylist' command with a specific video ID to play a video on the screen. Requires the client to be connected. ```python async def play_video(self, video_id: str) -> bool: """Sends setPlaylist command to screen to play a specific video :raises NotConnectedException: the client is not connected to the screen """ return await self._command("setPlaylist", {"videoId": video_id}) ``` -------------------------------- ### Get Screen ID from DIAL Endpoint Source: https://pyytlounge.readthedocs.io/en/latest/_modules/pyytlounge/dial.html Asynchronously retrieves the YouTube screen ID from a DIAL endpoint. Requires an active aiohttp ClientSession. Handles non-200 responses and missing screen IDs. ```python from aiohttp import ClientSession from xml.etree import ElementTree from typing import Optional DEVICE_NAMESPACE = "urn:schemas-upnp-org:device-1-0" SERVICE_NAMESPACE = "urn:dial-multiscreen-org:schemas:dial" def _get_optional_element_text(element: Optional[ElementTree.Element], default: str): if element: return element.text or default return default async def get_screen_id_from_dial(url: str) -> Optional[DialResult]: """Tries to get YouTube screen id from a DIAL endpoint""" async with ClientSession() as session: async with session.get(url) as response: if response.status != 200: return None headers = response.headers devices = ElementTree.fromstring(await response.text()) friendly_name = devices.find(".//device/friendlyName", {"ளுக்காக": DEVICE_NAMESPACE}) app_url = headers["application-url"] youtube_url = app_url + "YouTube" async with session.get(youtube_url) as response: if response.status != 200: return None service = ElementTree.fromstring(await response.text()) screen_id = service.find(".//additionalData/screenId", {"ளுக்காக": SERVICE_NAMESPACE}) if screen_id is not None and screen_id.text is not None: return DialResult( screen_name=_get_optional_element_text(friendly_name, ""), screen_id=screen_id.text, ) return None ``` -------------------------------- ### Issue Command with Parameters Source: https://pyytlounge.readthedocs.io/en/latest/_modules/pyytlounge/wrapper.html Issues a generic command with optional parameters to the screen via a POST request to the /bc/bind endpoint. Increments the command offset for each command issued. Raises NotConnectedException if the client is not connected. ```python async def _command(self, command: str, command_parameters: Optional[dict] = None) -> bool: """Issue given command with parameters. :raises NotConnectedException: the client is not connected to the screen """ if not self.connected(): raise NotConnectedException("Not connected") command_body = {"count": 1, "ofs": self._command_offset, "req0__sc": command} if command_parameters: for cmd_param in command_parameters: value = command_parameters[cmd_param] command_body[f"req0_{cmd_param}"] = value self._command_offset += 1 params = { **self._common_connection_parameters(), "RID": self._command_offset, } url = f"{api_base}/bc/bind" async with self._required_session.post(url=url, data=command_body, params=params) as resp: try: response_text = await resp.text() if not self._handle_session_result(resp.status, response_text): return False resp.raise_for_status() return True except: self._logger.exception("Command failed") raise ``` -------------------------------- ### next Source: https://pyytlounge.readthedocs.io/en/latest/_modules/pyytlounge/wrapper.html Sends the 'next' command to the screen to advance to the next item. ```APIDOC ## next ### Description Sends the 'next' command to the screen to advance to the next item. ### Method POST ### Endpoint /_command ### Parameters #### Request Body - **command** (string) - Required - The command to send, which is 'next'. ### Request Example ```json { "command": "next" } ``` ### Response #### Success Response (200) - **success** (boolean) - Indicates if the command was successful. #### Response Example ```json { "success": true } ``` ### Exceptions - **NotConnectedException**: The client is not connected to the screen. ``` -------------------------------- ### Subscribe to Lounge Events Source: https://pyytlounge.readthedocs.io/en/latest/_modules/pyytlounge/wrapper.html Subscribes to lounge events by making a GET request to the /bc/bind endpoint. Handles session results, parses event chunks, and processes events. Includes error handling for payload errors and general exceptions. ```python async def subscribe(self): """Subscribe to lounge events. :raises NotSupportedException: screen does not allow lounge client :raises NotConnectedException: the client is not yet connected to the screen """ if not self.connected(): raise NotConnectedException("Not connected") params = { **self._common_connection_parameters(), "RID": "rpc", "CI": "0", "TYPE": "xmlhttp", } url = f"{api_base}/bc/bind" self._logger.info("Subscribing to lounge id %s", self.auth.lounge_id_token) async with self._required_session.get( url=url, params=params, timeout=ClientTimeout() ) as resp: try: if not self._handle_session_result(resp.status, resp.reason or ""): return async for events in self._parse_event_chunks(iter_response_lines(resp.content)): await self._process_events(events) if not self.connected(): break self._logger.info("Subscribe completed, status %i %s", resp.status, resp.reason) except ClientPayloadError: self._logger.exception( "Handle subscribe payload error, status %s reason %s", resp.status, resp.reason, ) except asyncio.CancelledError: raise except: self._logger.exception( "Handle subscribe failed, status %s reason %s", resp.status, resp.reason, ) raise [docs] async def disconnect(self) -> bool: """Disconnect from the current session. :raises NotConnectedException: the client is already not connected """ if not self.connected(): raise NotConnectedException("Not connected") command_body = { "ui": "", "TYPE": "terminate", "clientDisconnectReason": "MDX_SESSION_DISCONNECT_REASON_DISCONNECTED_BY_USER", } params = { **self._common_connection_parameters(), "CVER": "1", "RID": self._command_offset, "auth_failure_option": "send_error", } url = f"{api_base}/bc/bind" async with self._required_session.post(url=url, data=command_body, params=params) as resp: try: response_text = await resp.text() if not self._handle_session_result(resp.status, response_text): return False resp.raise_for_status() return True except: self._logger.exception("Disconnect failed") raise async def _command(self, command: str, command_parameters: Optional[dict] = None) -> bool: """Issue given command with parameters. :raises NotConnectedException: the client is not connected to the screen """ if not self.connected(): raise NotConnectedException("Not connected") command_body = {"count": 1, "ofs": self._command_offset, "req0__sc": command} if command_parameters: for cmd_param in command_parameters: value = command_parameters[cmd_param] command_body[f"req0_{cmd_param}"] = value self._command_offset += 1 params = { **self._common_connection_parameters(), "RID": self._command_offset, } url = f"{api_base}/bc/bind" async with self._required_session.post(url=url, data=command_body, params=params) as resp: try: response_text = await resp.text() if not self._handle_session_result(resp.status, response_text): return False resp.raise_for_status() return True except: self._logger.exception("Command failed") raise [docs] async def play(self) -> bool: """Sends play command to screen""" return await self._command("play") [docs] async def play_video(self, video_id: str) -> bool: """Sends setPlaylist command to screen to play a specific video :raises NotConnectedException: the client is not connected to the screen """ return await self._command("setPlaylist", {"videoId": video_id}) [docs] async def pause(self) -> bool: """Sends pause command to screen :raises NotConnectedException: the client is not connected to the screen """ return await self._command("pause") [docs] async def previous(self) -> bool: """Sends previous command to screen :raises NotConnectedException: the client is not connected to the screen """ return await self._command("previous") ``` -------------------------------- ### play_video Source: https://pyytlounge.readthedocs.io/en/latest/_modules/pyytlounge/wrapper.html Sends the setPlaylist command to the screen to play a specific video. ```APIDOC ## play_video ### Description Sends setPlaylist command to screen to play a specific video ### Method POST ### Endpoint /bc/bind ### Parameters #### Request Body - **count** (integer) - Required - 1 - **ofs** (string) - Required - The command offset. - **req0__sc** (string) - Required - "setPlaylist" - **videoId** (string) - Required - The ID of the video to play. #### Query Parameters - **RID** (string) - Required - The command offset. ### Response #### Success Response (200) - **bool** (boolean) - True if the command was issued successfully. ### Raises - NotConnectedException: the client is not connected to the screen ``` -------------------------------- ### Connect to a screen Source: https://pyytlounge.readthedocs.io/en/latest/_sources/usage.rst.txt Establish a connection to the paired screen. This method should be called after the API is in a linked state. ```python connected = await api.connect() ``` -------------------------------- ### Process Event: Autoplay Up Next Source: https://pyytlounge.readthedocs.io/en/latest/_modules/pyytlounge/wrapper.html Handles the 'autoplayUpNext' event, triggering an autoplay up next changed listener if one is set and arguments are present. ```python elif event_type == "autoplayUpNext" and args: await self.event_listener.autoplay_up_next_changed(AutoplayUpNextEvent(args[0])) ``` -------------------------------- ### play Source: https://pyytlounge.readthedocs.io/en/latest/_modules/pyytlounge/wrapper.html Sends the play command to the screen to resume playback. ```APIDOC ## play ### Description Sends play command to screen ### Method POST ### Endpoint /bc/bind ### Parameters #### Request Body - **count** (integer) - Required - 1 - **ofs** (string) - Required - The command offset. - **req0__sc** (string) - Required - "play" #### Query Parameters - **RID** (string) - Required - The command offset. ### Response #### Success Response (200) - **bool** (boolean) - True if the command was issued successfully. ### Raises - NotConnectedException: the client is not connected to the screen ``` -------------------------------- ### Send Play Command Source: https://pyytlounge.readthedocs.io/en/latest/_modules/pyytlounge/wrapper.html Sends the 'play' command to the screen using the generic _command method. ```python async def play(self) -> bool: """Sends play command to screen""" return await self._command("play") ``` -------------------------------- ### NowPlayingEvent Methods Source: https://pyytlounge.readthedocs.io/en/latest/pyytlounge.html Details the methods available on the NowPlayingEvent class, which provides information about the currently playing media. ```APIDOC ## NowPlayingEvent ### Description This class encapsulates information related to the currently playing media on the YouTube Lounge screen. ### Methods - `get_thumbnail_url(_thumbnail_idx: int = 0) -> str | None` Retrieves the URL for a thumbnail of the current video. The `_thumbnail_idx` parameter can be used to fetch different available thumbnails. ``` -------------------------------- ### NowPlayingEvent Source: https://pyytlounge.readthedocs.io/en/latest/pyytlounge.html Provides information about the currently playing media. It includes details regarding the playback state. ```APIDOC ## NowPlayingEvent ### Description Contains information related to playback state. ### Class `_pyytlounge.events.NowPlayingEvent` ``` -------------------------------- ### Set Volume Source: https://pyytlounge.readthedocs.io/en/latest/_modules/pyytlounge/wrapper.html Sets the screen's volume to a specified integer value between 0 and 100. Requires the client to be connected. ```python await self._command("setVolume", {"volume": volume}) ``` -------------------------------- ### Send Next Command Source: https://pyytlounge.readthedocs.io/en/latest/_modules/pyytlounge/wrapper.html Sends the 'next' command to the screen. Use when the client is connected to the screen. ```python await self._command("next") ``` -------------------------------- ### Migrate to Event Listener (v3.x.x) Source: https://pyytlounge.readthedocs.io/en/latest/_sources/updating.rst.txt This snippet shows the old method of subscribing to events using a callback function. ```python async def receive_state(state: PlaybackState): print(f"New state: {state}") await api.subscribe(receive_state) ``` -------------------------------- ### Initialize YtLoungeApi and close manually Source: https://pyytlounge.readthedocs.io/en/latest/_sources/usage.rst.txt Instantiate YtLoungeApi and manually close the session later. This is an alternative to using an asynchronous context manager. ```python api = YtLoungeApi('test client') ... await api.close() ``` -------------------------------- ### Check Screen Availability Source: https://pyytlounge.readthedocs.io/en/latest/_modules/pyytlounge/wrapper.html Asynchronously checks if a YouTube screen is available for pairing. Raises NotLinkedException if the client is not linked. ```python async def is_available(self) -> bool: """Asks YouTube API if the screen is available. Must be linked prior to this. :raises NotLinkedException: client is not yet linked, first pair or refresh authorization """ if not self.linked(): raise NotLinkedException("Not linked") body = {"lounge_token": self.auth.lounge_id_token} url = f"{api_base}/pairing/get_screen_availability" result = await self._required_session.post(url=url, data=body) status = await result.json() if "screens" in status and len(status["screens"]) > 0: return status["screens"][0]["status"] == "online" return False ``` -------------------------------- ### Implement Custom Event Listener (v3.x.x) Source: https://pyytlounge.readthedocs.io/en/latest/_sources/updating.rst.txt This snippet demonstrates the new approach using a custom EventListener subclass to handle playback and now playing events. ```python from pyytlounge import EventListener class CustomListener(EventListener): def __init__(self): self.last_video_id: Optional[str] = None async def playback_state_changed(self, event: PlaybackStateEvent) -> None: """Called when playback state changes (position, play/pause)""" print( f"New state: {event.state} = id: {self.last_video_id} pos: {event.current_time} duration: {event.duration}" ) async def now_playing_changed(self, event: NowPlayingEvent) -> None: """Called when active video changes""" print( f"New state: {event.state} = id: {event.video_id} pos: {event.current_time} duration: {event.duration}" ) self.last_video_id = event.video_id with YtLoungeApi("Some device", CustomListener()) as api: ... await api.subscribe() ``` -------------------------------- ### Pair with a screen using a pairing code Source: https://pyytlounge.readthedocs.io/en/latest/_sources/usage.rst.txt Pair the API instance with a screen using a provided pairing code. This establishes a linked state, providing necessary identifiers and tokens for screen communication. ```python pairing_code = input("Enter pairing code: ") # or from another source paired_and_linked = await api.pair(pairing_code) ``` -------------------------------- ### Connect YtLoungeApi to a screen Source: https://pyytlounge.readthedocs.io/en/latest/usage.html Establish a connection to the screen after the API has been paired and authenticated. ```python connected = await api.connect() ``` -------------------------------- ### Send Previous Command Source: https://pyytlounge.readthedocs.io/en/latest/_modules/pyytlounge/wrapper.html Sends the 'previous' command to the screen to play the previous item in the playlist. Requires the client to be connected. ```python async def previous(self) -> bool: """Sends previous command to screen :raises NotConnectedException: the client is not connected to the screen """ return await self._command("previous") ``` -------------------------------- ### Async Context Manager for YtLoungeApi Source: https://pyytlounge.readthedocs.io/en/latest/_modules/pyytlounge/wrapper.html Use the API as an asynchronous context manager to ensure proper initialization and cleanup of the aiohttp session and connector. The session and connector are set up in __aenter__ and closed in __aexit__. ```python async with YtLoungeApi("My Device Name") as api: await api.connect() # ... use api ... ``` -------------------------------- ### _pause Source: https://pyytlounge.readthedocs.io/en/latest/pyytlounge.html Sends a pause command to the currently connected screen device. ```APIDOC ## _pause ### Description Sends a pause command to the currently connected screen device. ### Method Asynchronous ### Raises - **NotConnectedException** – The client is not connected to the screen. ### Returns - **bool** - True if the pause command was sent successfully, False otherwise. ``` -------------------------------- ### send_dpad_command Source: https://pyytlounge.readthedocs.io/en/latest/_modules/pyytlounge/wrapper.html Simulates a D-pad button press, similar to a remote control. ```APIDOC ## send_dpad_command ### Description Simulates a D-pad button press, similar to a remote control. ### Method POST ### Endpoint /_command ### Parameters #### Request Body - **command** (string) - Required - The command to send, which is 'dpadCommand'. - **data** (object) - Required - An object containing the D-pad key. - **key** (DpadCommand) - Required - The specific D-pad button to press (e.g., UP, DOWN, LEFT, RIGHT, SELECT). ### Request Example ```json { "command": "dpadCommand", "data": { "key": "UP" } } ``` ### Response #### Success Response (200) - **success** (boolean) - Indicates if the D-pad command was processed. #### Response Example ```json { "success": true } ``` ### Exceptions - **NotConnectedException**: The client is not connected to the screen. ``` -------------------------------- ### Set Playback Speed Source: https://pyytlounge.readthedocs.io/en/latest/_modules/pyytlounge/wrapper.html Sets the playback speed to a given float value between 0.25 and 2. Requires the client to be connected to the screen. ```python await self._command("setPlaybackSpeed", {"playbackSpeed": speed}) ``` -------------------------------- ### pyytlounge.models.DpadCommand Source: https://pyytlounge.readthedocs.io/en/latest/pyytlounge.html Enum representing directional pad commands for controlling YouTube interfaces. ```APIDOC ## DpadCommand Enum representing directional pad commands. ### Members - `BACK` - `DOWN` - `ENTER` - `LEFT` - `RIGHT` - `UP` ``` -------------------------------- ### Process Event: Now Playing Source: https://pyytlounge.readthedocs.io/en/latest/_modules/pyytlounge/wrapper.html Handles the 'nowPlaying' event, triggering a now playing changed listener if one is set. ```python elif event_type == "nowPlaying": await self.event_listener.now_playing_changed(NowPlayingEvent(args[0])) ``` -------------------------------- ### NowPlayingEvent Class Source: https://pyytlounge.readthedocs.io/en/latest/_modules/pyytlounge/events.html Holds information about the currently playing media, including video ID, current time, duration, and playback state. It also provides a method to retrieve thumbnail URLs. ```python class NowPlayingEvent: """Contains information related to playback state""" def __init__(self, data: _NowPlayingData): self.video_id: Optional[str] = data.get("videoId", None) self.current_time: Optional[float] = \ float(data["currentTime"]) if "currentTime" in data else None self.duration: Optional[float] = float(data["duration"]) if "duration" in data else None self.state = State.parse(data["state"]) if "state" in data else State.Stopped [docs] def get_thumbnail_url(self, thumbnail_idx: int = 0) -> Optional[str]: """Returns thumbnail for current video. Use thumbnail idx to get different thumbnails.""" if self.video_id: return get_thumbnail_url(self.video_id, thumbnail_idx=thumbnail_idx) return None ``` -------------------------------- ### pair Source: https://pyytlounge.readthedocs.io/en/latest/_modules/pyytlounge/wrapper.html Initiates the device pairing process using a provided pairing code. It sends the code to the API to obtain screen information and authentication tokens. ```APIDOC ## pair ### Description Pair with a device using a manual input pairing code. This method sends the pairing code to the API to establish a connection and retrieve necessary authentication details. ### Method POST ### Endpoint /pairing/get_screen ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **pairing_code** (str) - Required - The code obtained from the device to initiate pairing. ### Request Example ```json { "pairing_code": "YOUR_PAIRING_CODE" } ``` ### Response #### Success Response (200) - **screen** (dict) - Contains information about the paired screen, including its name, screenId, and loungeToken. #### Response Example ```json { "screen": { "name": "Device Name", "screenId": "screen_id_123", "loungeToken": "lounge_token_abc" } } ``` ### Exceptions - Raises an exception if the pairing process fails. ``` -------------------------------- ### Subscribe to screen status updates with a custom listener Source: https://pyytlounge.readthedocs.io/en/latest/usage.html Subscribe to real-time status updates from the screen using a custom event listener. The subscription will block until it ends. ```python class YtListener(EventListener): async def now_playing_changed(self, event: NowPlayingEvent) -> None: """Called when active video changes""" print( f"New state: {event.state} = id: {event.video_id} pos: {event.current_time} duration: {event.duration}" ) listener = YtListener() async with YtLoungeApi('Test client', listener) as api: # this will block until the subscription ends subscribed = await api.subscribe() ``` -------------------------------- ### Process Event: State Change Source: https://pyytlounge.readthedocs.io/en/latest/_modules/pyytlounge/wrapper.html Handles the 'onStateChange' event, triggering a playback state change listener if one is set. ```python if event_type == "onStateChange" and self.event_listener: await self.event_listener.playback_state_changed(PlaybackStateEvent(args[0])) ``` -------------------------------- ### get_now_playing Source: https://pyytlounge.readthedocs.io/en/latest/_modules/pyytlounge/wrapper.html Requests an update on the currently playing media from the screen. ```APIDOC ## get_now_playing ### Description Requests a 'now playing' update from the screen. ### Method POST ### Endpoint /_command ### Parameters #### Request Body - **command** (string) - Required - The command to send, which is 'getNowPlaying'. ### Request Example ```json { "command": "getNowPlaying" } ``` ### Response #### Success Response (200) - **success** (boolean) - Indicates if the request for 'now playing' information was sent. #### Response Example ```json { "success": true } ``` ### Exceptions - **NotConnectedException**: The client is not connected to the screen. ``` -------------------------------- ### YtLoungeApi Class Methods Source: https://pyytlounge.readthedocs.io/en/latest/pyytlounge.html This section outlines the various asynchronous and synchronous methods provided by the YtLoungeApi class for controlling YouTube playback and managing device connections. ```APIDOC ## YtLoungeApi ### Description Provides an interface for controlling YouTube Lounge. ### Methods #### `async close()` Closes the connection to the YouTube Lounge screen. #### `async connect()` Attempts to connect to the screen using previously set tokens. **Raises:** * **NotSupportedException**: If the screen does not allow a lounge client. * **NotLinkedException**: If the client is not yet linked. #### `connected()` Returns `True` if the screen's session is connected, `False` otherwise. #### `async disconnect()` Disconnects from the current session. **Raises:** * **NotConnectedException**: If the client is already not connected. #### `async get_now_playing()` Requests a 'now playing' update from the screen. **Raises:** * **NotConnectedException**: If the client is not connected to the screen. #### `async is_available()` Checks if the screen is available via the YouTube API. Must be linked prior to this call. **Raises:** * **NotLinkedException**: If the client is not yet linked. #### `linked()` Returns `True` if paired and the lounge ID token is known, `False` otherwise. #### `load_auth_state(data: dict)` Loads authentication parameters from a deserialized dictionary. #### `async next()` Sends the 'next' command to the screen. **Raises:** * **NotConnectedException**: If the client is not connected to the screen. #### `async pair(pairing_code: str)` Pairs with a device using a manual input pairing code. #### `async pair_with_screen_id(screen_id: str, screen_name: str | None = None)` Pairs with a device using a known screen ID. Optionally specifies the screen name if already known. #### `paired()` Returns `True` if the screen ID is known, `False` otherwise. #### `async pause()` Sends the 'pause' command to the screen. **Raises:** * **NotConnectedException**: If the client is not connected to the screen. #### `async play()` Sends the 'play' command to the screen. #### `async play_video(video_id: str)` Sends the `setPlaylist` command to the screen to play a specific video. **Raises:** * **NotConnectedException**: If the client is not connected to the screen. #### `async previous()` Sends the 'previous' command to the screen. **Raises:** * **NotConnectedException**: If the client is not connected to the screen. #### `async refresh_auth()` Refreshes the lounge token using the stored refresh token. **Raises:** * **NotPairedException**: If the screen is not yet known; pair first. #### `async seek_to(time: float)` Seeks to the specified time in seconds. **Raises:** * **NotConnectedException**: If the client is not connected to the screen. #### `async send_dpad_command(button_input: DpadCommand)` Sends a DPAD command, simulating remote control input. **Raises:** * **NotConnectedException**: If the client is not connected to the screen. #### `async set_auto_play_mode(enabled: bool)` Enables or disables auto-play mode. **Raises:** * **NotConnectedException**: If the client is not connected to the screen. #### `async set_closed_captions(language_code: str | None, video_id: str)` Sets the closed captions for a given video ID to the specified language code. Set `language_code` to `None` to turn off captions. **Raises:** * **NotConnectedException**: If the client is not connected to the screen. #### `async set_playback_speed(speed: float)` Sets the playback speed to the given value (0.25-2). **Raises:** * **NotConnectedException**: If the client is not connected to the screen. #### `async set_volume(volume: int)` Sets the volume to the given value (0-100). **Raises:** * **NotConnectedException**: If the client is not connected to the screen. #### `async skip_ad()` Skips the current advertisement if possible. **Raises:** * **NotConnectedException**: If the client is not connected to the screen. #### `store_auth_state()` Returns the current authentication parameters as a serializable dictionary. #### `async subscribe()` Starts listening for events from the screen. Events will be sent to the `event_listener` provided during object creation. **Raises:** * **NotSupportedException**: If the screen does not allow a lounge client. * **NotConnectedException**: If the client is not yet connected to the screen. ``` -------------------------------- ### State Enum and Parse Method Source: https://pyytlounge.readthedocs.io/en/latest/pyytlounge.html Documentation for the State enumeration and its static parse method, used to represent and interpret playback states. ```APIDOC ## State Enum ### Description The `State` enumeration represents the possible playback states of the YouTube player. ### Members - `Advertisement` (1081): Indicates that an advertisement is currently playing. - `Buffering` (0): Indicates that the player is currently buffering content. - `Paused` (2): Indicates that the playback is paused. - `Playing` (1): Indicates that the content is currently playing. - `Starting` (3): Indicates that the playback is starting. - `Stopped` (-1): Indicates that the playback has stopped. ### Static Method - `_parse(_state: str) -> State` Parses a string representation of a state into its corresponding `State` enum value. ``` -------------------------------- ### Empty Listener Implementation Source: https://pyytlounge.readthedocs.io/en/latest/_modules/pyytlounge/event_listener.html An empty listener implementation that can be used for convenience when no specific event handling is required. ```python class _EmptyListener(EventListener): """Empty listener which does nothing for convenience""" ``` -------------------------------- ### Process Event: Playback Speed Changed Source: https://pyytlounge.readthedocs.io/en/latest/_modules/pyytlounge/wrapper.html Handles the 'onPlaybackSpeedChanged' event, triggering a playback speed changed listener and then fetching the now playing information. ```python elif event_type == "onPlaybackSpeedChanged": await self.event_listener.playback_speed_changed(PlaybackSpeedEvent(args[0])) await self.get_now_playing() ``` -------------------------------- ### Set Autoplay Mode Source: https://pyytlounge.readthedocs.io/en/latest/_modules/pyytlounge/wrapper.html Enables or disables the autoplay mode. Pass 'enabled' as a boolean. Requires connection to the screen. ```python await self._command("setAutoplayMode", {"autoplayMode": "ENABLED" if enabled else "DISABLED"}) ``` -------------------------------- ### connect Source: https://pyytlounge.readthedocs.io/en/latest/_modules/pyytlounge/wrapper.html Attempts to connect to the YouTube lounge service using previously set tokens. This method handles the binding process and event parsing. ```APIDOC ## connect ### Description Attempt to connect using the previously set tokens. ### Method POST ### Endpoint /bc/bind ### Parameters #### Request Body - **app** (string) - Required - Application identifier, typically 'web'. - **mdx-version** (string) - Required - MDX protocol version, typically '3'. - **name** (string) - Required - The name of the device. - **id** (string) - Required - The screen ID for authentication. - **device** (string) - Required - Device type, typically 'REMOTE_CONTROL'. - **capabilities** (string) - Required - Comma-separated list of supported capabilities. - **magnaKey** (string) - Required - Authentication key, typically 'cloudPairedDevice'. - **ui** (string) - Required - UI indicator, typically 'false'. - **deviceContext** (string) - Required - Contextual information about the device. - **theme** (string) - Required - UI theme, typically 'cl'. - **loungeIdToken** (string) - Required - The lounge ID token for authentication. ### Request Example ```json { "app": "web", "mdx-version": "3", "name": "YourDeviceName", "id": "your_screen_id", "device": "REMOTE_CONTROL", "capabilities": "que,dsdtr,atp,vsp", "magnaKey": "cloudPairedDevice", "ui": "false", "deviceContext": "user_agent=dunno&window_width_points=&window_height_points=&os_name=android&ms=", "theme": "cl", "loungeIdToken": "your_lounge_token" } ``` ### Response #### Success Response (200) - **bool** - Returns true if the connection was successful and the client is now connected. ### Raises - **NotSupportedException**: If the screen does not allow the lounge client. - **NotLinkedException**: If the client is not yet linked. ``` -------------------------------- ### pyytlounge.models.AuthState Source: https://pyytlounge.readthedocs.io/en/latest/pyytlounge.html Represents and manages authentication state for YouTube, allowing serialization and deserialization for persistence. ```APIDOC ## AuthState Stores information used to authenticate with YouTube. Can be serialized and deserialized for reuse. ### Methods - `deserialize(_data: dict)`: Deserializes state from a dictionary into this object. - `serialize() -> AuthStateData`: Serializes the current state into a dictionary. ### Attributes - `expiry` (int) - `lounge_id_token` (str) - `refresh_token` (str) - `screen_id` (str) - `version` (int) ``` -------------------------------- ### YtLoungeApi Methods Source: https://pyytlounge.readthedocs.io/en/latest/pyytlounge.html This section outlines the core methods of the YtLoungeApi class, which are used to establish connections, manage playback, and retrieve information from the YouTube Lounge service. ```APIDOC ## YtLoungeApi Methods ### Description The `YtLoungeApi` class provides the primary interface for interacting with the YouTube Lounge service. It allows for connection management, playback control, and status checks. ### Methods - `__init__(_device_name: str, _event_listener: EventListener | None = None, _logger: Logger | None = None)` Initializes the YtLoungeApi with a device name and optional event listener and logger. - `connected() -> bool` Checks if the current session with the screen is established and active. - `linked() -> bool` Verifies if the client is paired with a device and possesses a known lounge ID token. - `load_auth_state(_data: dict_)` Loads authentication parameters from a deserialized dictionary. - `_close() -> None` Asynchronously closes the current session with the screen. - `_connect() -> bool` Attempts to establish a connection to the screen using previously set authentication tokens. Raises: * `NotSupportedException`: If the screen does not support the lounge client. * `NotLinkedException`: If the client has not been linked yet (requires pairing or authorization refresh). - `_disconnect() -> bool` Asynchronously disconnects from the current session. Raises: * `NotConnectedException`: If the client is already disconnected. - `_get_now_playing() -> bool` Asynchronously requests an update on the currently playing media from the screen. Raises: * `NotConnectedException`: If the client is not connected to the screen. - `_is_available() -> bool` Asynchronously checks if the screen is available via the YouTube API. Requires prior linking. Raises: * `NotLinkedException`: If the client is not yet linked. - `_next() -> bool` Asynchronously sends a command to the screen to play the next item in the queue. Raises: * `NotConnectedException`: If the client is not connected to the screen. - `_pair(_pairing_code: str) -> bool` Asynchronously pairs with a device using a provided manual pairing code. ``` -------------------------------- ### Store Authentication State Source: https://pyytlounge.readthedocs.io/en/latest/_modules/pyytlounge/wrapper.html Returns the current authentication parameters as a dictionary, which can be serialized for later use. ```python def store_auth_state(self) -> dict: """Return auth parameters as dict which can be serialized for later use""" return { "screenId": self.auth.screen_id, "lounge_id_token": self.auth.lounge_id_token, "refresh_token": self.auth.refresh_token, } ```