From 166e238f417687a1601c386a292edc2e55d0dc21 Mon Sep 17 00:00:00 2001 From: dmunozv04 <39565245+dmunozv04@users.noreply.github.com> Date: Sun, 25 May 2025 14:02:59 +0200 Subject: [PATCH 1/3] Mimick YouTube iOS app --- src/iSponsorBlockTV/ytlounge.py | 68 +++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/src/iSponsorBlockTV/ytlounge.py b/src/iSponsorBlockTV/ytlounge.py index c852855..7c318cc 100644 --- a/src/iSponsorBlockTV/ytlounge.py +++ b/src/iSponsorBlockTV/ytlounge.py @@ -5,6 +5,9 @@ from typing import Any, List import pyytlounge from aiohttp import ClientSession +from pyytlounge.wrapper import NotLinkedException, api_base, as_aiter, Dict +from uuid import uuid4 + from .constants import youtube_client_blacklist create_task = asyncio.create_task @@ -236,3 +239,68 @@ class YtLoungeApi(pyytlounge.YtLoungeApi): if self.conn is not None: await self.conn.close() self.session = web_session + + 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": "ytios-phone-20.15.1", + "VER": "8", + "v": "2", + } + + async def connect(self) -> bool: + """Attempt to connect using the previously set tokens""" + if not self.linked(): + raise NotLinkedException("Not linked") + + connect_body = { + "id": str(uuid4()), + "mdx-version": "3", + "TYPE": "xmlhttp", + "theme": "cl", + "sessionSource": "MDX_SESSION_SOURCE_UNKNOWN", + "connectParams": "{\"setStatesParams\": \"{\"playbackSpeed\":0}\"}", + "sessionNonce": str(uuid4()), + "RID": "1", + "CVER": "1", + "capabilities": "que,dsdtr,atp,vsp", + "ui": "false", + "app": "ytios-phone-20.15.1", + "pairing_type": "manual", + "VER": "8", + "loungeIdToken": self.auth.lounge_id_token, + "device": "REMOTE_CONTROL", + "name": self.device_name, + } + connect_url = ( + f"{api_base}/bc/bind" + ) + async with self.session.post(url=connect_url, data=connect_body) as resp: + try: + text = await resp.text() + if resp.status == 401: + self._lounge_token_expired() + return False + + if resp.status != 200: + self._logger.warning( + "Unknown reply to connect %i %s", resp.status, resp.reason + ) + return False + lines = text.splitlines() + async for events in self._parse_event_chunks(as_aiter(lines)): + self._process_events(events) + self._command_offset = 1 + return self.connected() + except: + self._logger.exception( + "Handle connect failed, status %s reason %s", + resp.status, + resp.reason, + ) + raise From fd6f0d7283a42b5d3152fdbccc35a8ec27f294a2 Mon Sep 17 00:00:00 2001 From: dmunozv04 <39565245+dmunozv04@users.noreply.github.com> Date: Wed, 28 May 2025 00:18:32 +0200 Subject: [PATCH 2/3] Attempt to fix the issue --- src/iSponsorBlockTV/ytlounge.py | 50 ++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/src/iSponsorBlockTV/ytlounge.py b/src/iSponsorBlockTV/ytlounge.py index 7c318cc..7c7f8d5 100644 --- a/src/iSponsorBlockTV/ytlounge.py +++ b/src/iSponsorBlockTV/ytlounge.py @@ -1,5 +1,6 @@ import asyncio import json +import sys from typing import Any, List import pyytlounge @@ -284,7 +285,12 @@ class YtLoungeApi(pyytlounge.YtLoungeApi): try: text = await resp.text() if resp.status == 401: - self._lounge_token_expired() + if "Connection denied" in text: + self._logger.warning( + "Connection denied, attempting to circumvent the issue" + ) + await self.connect_as_screen() + #self._lounge_token_expired() return False if resp.status != 200: @@ -304,3 +310,45 @@ class YtLoungeApi(pyytlounge.YtLoungeApi): resp.reason, ) raise + + async def connect_as_screen(self) -> bool: + """Attempt to connect using the previously set tokens""" + if not self.linked(): + raise NotLinkedException("Not linked") + + connect_body = { + "id": str(uuid4()), + "mdx-version": "3", + "TYPE": "xmlhttp", + "theme": "cl", + "sessionSource": "MDX_SESSION_SOURCE_UNKNOWN", + "connectParams": "{\"setStatesParams\": \"{\"playbackSpeed\":0}\"}", + "sessionNonce": str(uuid4()), + "RID": "1", + "CVER": "1", + "capabilities": "que,dsdtr,atp,vsp", + "ui": "false", + "app": "ytios-phone-20.15.1", + "pairing_type": "manual", + "VER": "8", + "loungeIdToken": self.auth.lounge_id_token, + "device": "LOUNGE_SCREEN", + "name": self.device_name, + } + connect_url = ( + f"{api_base}/bc/bind" + ) + async with self.session.post(url=connect_url, data=connect_body) as resp: + try: + await resp.text() + self.logger.error("Connected as screen: please force close the app on the device for iSponsorBlockTV to work properly") + self.logger.warn("Exiting in 5 seconds") + await asyncio.sleep(5) + sys.exit(0) + except: + self._logger.exception( + "Handle connect failed, status %s reason %s", + resp.status, + resp.reason, + ) + raise From 98c1211b0982b2697089b6f23d2a24d9000e3502 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 28 May 2025 21:48:24 +0000 Subject: [PATCH 3/3] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/iSponsorBlockTV/ytlounge.py | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/src/iSponsorBlockTV/ytlounge.py b/src/iSponsorBlockTV/ytlounge.py index 7c7f8d5..f071cf5 100644 --- a/src/iSponsorBlockTV/ytlounge.py +++ b/src/iSponsorBlockTV/ytlounge.py @@ -240,7 +240,7 @@ class YtLoungeApi(pyytlounge.YtLoungeApi): if self.conn is not None: await self.conn.close() self.session = web_session - + def _common_connection_parameters(self) -> Dict[str, Any]: return { "name": self.device_name, @@ -265,7 +265,7 @@ class YtLoungeApi(pyytlounge.YtLoungeApi): "TYPE": "xmlhttp", "theme": "cl", "sessionSource": "MDX_SESSION_SOURCE_UNKNOWN", - "connectParams": "{\"setStatesParams\": \"{\"playbackSpeed\":0}\"}", + "connectParams": '{"setStatesParams": "{"playbackSpeed":0}"}', "sessionNonce": str(uuid4()), "RID": "1", "CVER": "1", @@ -278,9 +278,7 @@ class YtLoungeApi(pyytlounge.YtLoungeApi): "device": "REMOTE_CONTROL", "name": self.device_name, } - connect_url = ( - f"{api_base}/bc/bind" - ) + connect_url = f"{api_base}/bc/bind" async with self.session.post(url=connect_url, data=connect_body) as resp: try: text = await resp.text() @@ -290,13 +288,11 @@ class YtLoungeApi(pyytlounge.YtLoungeApi): "Connection denied, attempting to circumvent the issue" ) await self.connect_as_screen() - #self._lounge_token_expired() + # self._lounge_token_expired() return False if resp.status != 200: - self._logger.warning( - "Unknown reply to connect %i %s", resp.status, resp.reason - ) + self._logger.warning("Unknown reply to connect %i %s", resp.status, resp.reason) return False lines = text.splitlines() async for events in self._parse_event_chunks(as_aiter(lines)): @@ -322,7 +318,7 @@ class YtLoungeApi(pyytlounge.YtLoungeApi): "TYPE": "xmlhttp", "theme": "cl", "sessionSource": "MDX_SESSION_SOURCE_UNKNOWN", - "connectParams": "{\"setStatesParams\": \"{\"playbackSpeed\":0}\"}", + "connectParams": '{"setStatesParams": "{"playbackSpeed":0}"}', "sessionNonce": str(uuid4()), "RID": "1", "CVER": "1", @@ -335,13 +331,13 @@ class YtLoungeApi(pyytlounge.YtLoungeApi): "device": "LOUNGE_SCREEN", "name": self.device_name, } - connect_url = ( - f"{api_base}/bc/bind" - ) + connect_url = f"{api_base}/bc/bind" async with self.session.post(url=connect_url, data=connect_body) as resp: try: await resp.text() - self.logger.error("Connected as screen: please force close the app on the device for iSponsorBlockTV to work properly") + self.logger.error( + "Connected as screen: please force close the app on the device for iSponsorBlockTV to work properly" + ) self.logger.warn("Exiting in 5 seconds") await asyncio.sleep(5) sys.exit(0)