diff --git a/constants.py b/constants.py index a40ca717..0c158ee0 100644 --- a/constants.py +++ b/constants.py @@ -80,7 +80,7 @@ def _resource_path(relative_path: Path | str) -> Path: MAX_CHANNELS = MAX_TOPICS // TOPICS_PER_CHANNEL # Misc DEFAULT_LANG = "English" -BASE_URL = URL("https://twitch.tv") +BASE_URL = URL("https://www.twitch.tv") # Intervals and Delays PING_INTERVAL = timedelta(minutes=3) PING_TIMEOUT = timedelta(seconds=10) @@ -99,7 +99,8 @@ def _resource_path(relative_path: Path | str) -> Path: class ClientInfo: - def __init__(self, client_id: str, user_agents: str | list[str]) -> None: + def __init__(self, client_url: URL, client_id: str, user_agents: str | list[str]) -> None: + self.CLIENT_URL: URL = client_url self.CLIENT_ID: str = client_id self.USER_AGENT: str if isinstance(user_agents, list): @@ -108,11 +109,12 @@ def __init__(self, client_id: str, user_agents: str | list[str]) -> None: self.USER_AGENT = user_agents def __iter__(self): - return iter((self.CLIENT_ID, self.USER_AGENT)) + return iter((self.CLIENT_URL, self.CLIENT_ID, self.USER_AGENT)) class ClientType: WEB = ClientInfo( + BASE_URL, "kimne78kx3ncx6brgo4mv6wki5h1ko", ( "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " @@ -120,6 +122,7 @@ class ClientType: ), ) MOBILE_WEB = ClientInfo( + URL("https://m.twitch.tv"), "r8s4dac0uhzifbpu9sjdiwzctle17ff", [ ( @@ -153,6 +156,7 @@ class ClientType: ] ) ANDROID_APP = ClientInfo( + BASE_URL, "kd1unb4b3q4t58fwlpcbzcbnm76a8fp", ( "Dalvik/2.1.0 (Linux; U; Android 7.1.2; SM-G977N Build/LMY48Z) " @@ -160,6 +164,7 @@ class ClientType: ), ) SMARTBOX = ClientInfo( + URL("https://android.tv.twitch.tv"), "ue6666qo983tsx6so1t0vnawi233wa", ( "Mozilla/5.0 (Linux; Android 7.1; Smart Box C1) AppleWebKit/537.36 " diff --git a/twitch.py b/twitch.py index cadebba1..588dec0b 100644 --- a/twitch.py +++ b/twitch.py @@ -91,7 +91,7 @@ def decode(self, s: str, *args): return obj -CLIENT_ID, USER_AGENT = ClientType.MOBILE_WEB +CLIENT_URL, CLIENT_ID, USER_AGENT = ClientType.MOBILE_WEB SAFE_LOADS = lambda s: json.loads(s, cls=SkipExtraJsonDecoder) @@ -281,9 +281,9 @@ async def _oauth_login(self) -> str: "Cache-Control": "no-cache", "Client-Id": CLIENT_ID, "Host": "id.twitch.tv", - "Origin": "https://android.tv.twitch.tv", + "Origin": str(CLIENT_URL), "Pragma": "no-cache", - "Referer": "https://android.tv.twitch.tv/", + "Referer": str(CLIENT_URL), "User-Agent": USER_AGENT, "X-Device-Id": self.device_id, } @@ -504,8 +504,8 @@ def headers( if hasattr(self, "device_id"): headers["X-Device-Id"] = self.device_id if gql: - headers["Origin"] = "https://www.twitch.tv" - headers["Referer"] = "https://www.twitch.tv/" + headers["Origin"] = str(BASE_URL) + headers["Referer"] = str(BASE_URL) headers["Authorization"] = f"OAuth {self.access_token}" if integrity: headers["Client-Integrity"] = self.integrity_token @@ -518,12 +518,12 @@ async def validate(self): async def _validate(self): if not hasattr(self, "session_id"): self.session_id = create_nonce(CHARS_HEX_LOWER, 16) - if not self._hasattrs("client_version", "device_id", "access_token", "user_id"): + if not self._hasattrs("device_id", "access_token", "user_id"): session = await self._twitch.get_session() jar = cast(aiohttp.CookieJar, session.cookie_jar) - if not self._hasattrs("client_version", "device_id"): + if not self._hasattrs("device_id"): async with self._twitch.request( - "GET", BASE_URL, headers=self.headers() + "GET", CLIENT_URL, headers=self.headers() ) as response: page_html = await response.text("utf8") assert page_html is not None @@ -532,7 +532,7 @@ async def _validate(self): # raise MinerException("Unable to extract client_version") # self.client_version = match.group(1) # doing the request ends up setting the "unique_id" value in the cookie - cookie = jar.filter_cookies(BASE_URL) + cookie = jar.filter_cookies(CLIENT_URL) self.device_id = cookie["unique_id"].value if not self._hasattrs("access_token", "user_id"): # looks like we're missing something @@ -540,7 +540,7 @@ async def _validate(self): logger.info("Checking login") login_form.update(_("gui", "login", "logging_in"), None) for attempt in range(2): - cookie = jar.filter_cookies(BASE_URL) + cookie = jar.filter_cookies(CLIENT_URL) if "auth-token" not in cookie: self.access_token = await self._oauth_login() cookie["auth-token"] = self.access_token @@ -557,8 +557,8 @@ async def _validate(self): if status == 401: # the access token we have is invalid - clear the cookie and reauth logger.info("Restored session is invalid") - assert BASE_URL.host is not None - jar.clear_domain(BASE_URL.host) + assert CLIENT_URL.host is not None + jar.clear_domain(CLIENT_URL.host) continue elif status == 200: validate_response = await response.json() @@ -572,7 +572,7 @@ async def _validate(self): logger.info(f"Login successful, user ID: {self.user_id}") login_form.update(_("gui", "login", "logged_in"), self.user_id) # update our cookie and save it - jar.update_cookies(cookie, BASE_URL) + jar.update_cookies(cookie, CLIENT_URL) jar.save(COOKIES_PATH) # if not self._hasattrs("integrity_token") or self.integrity_expired: # async with self._twitch.request(