From c4cfa5372f326918d8cd8b0d50df43d6b67299ea Mon Sep 17 00:00:00 2001 From: DevilXD <4180725+DevilXD@users.noreply.github.com> Date: Mon, 3 Feb 2025 21:31:06 +0100 Subject: [PATCH] Login flow now uses the returned verification_uri; to prefill the user_code on the activation page --- gui.py | 4 ++-- twitch.py | 7 ++++--- utils.py | 13 +++++++------ 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/gui.py b/gui.py index 75848aa..4cbe49e 100644 --- a/gui.py +++ b/gui.py @@ -581,7 +581,7 @@ class LoginForm: continue return login_data - async def ask_enter_code(self, user_code: str) -> None: + async def ask_enter_code(self, page_url: URL, user_code: str) -> None: self.update(_("gui", "login", "required"), None) # ensure the window isn't hidden into tray when this runs self._manager.grab_attention(sound=False) @@ -589,7 +589,7 @@ class LoginForm: await self.wait_for_login_press() self._manager.print(f"Enter this code on the Twitch's device activation page: {user_code}") await asyncio.sleep(4) - webopen("https://www.twitch.tv/activate") + webopen(page_url) def update(self, status: str, user_id: int | None): if user_id is not None: diff --git a/twitch.py b/twitch.py index de43e75..6233490 100644 --- a/twitch.py +++ b/twitch.py @@ -128,6 +128,7 @@ class _AuthState: } while True: try: + now = datetime.now(timezone.utc) async with self._twitch.request( "POST", "https://id.twitch.tv/oauth2/device", headers=headers, data=payload ) as response: @@ -136,17 +137,17 @@ class _AuthState: # "expires_in": 1800, # "interval": 5, # "user_code": "8 chars [A-Z]", - # "verification_uri": "https://www.twitch.tv/activate" + # "verification_uri": "https://www.twitch.tv/activate?device-code=ABCDEFGH" # } - now = datetime.now(timezone.utc) response_json: JsonType = await response.json() device_code: str = response_json["device_code"] user_code: str = response_json["user_code"] interval: int = response_json["interval"] + verification_uri: URL = URL(response_json["verification_uri"]) expires_at = now + timedelta(seconds=response_json["expires_in"]) # Print the code to the user, open them the activate page so they can type it in - await login_form.ask_enter_code(user_code) + await login_form.ask_enter_code(verification_uri, user_code) payload = { "client_id": self._twitch._client_type.CLIENT_ID, diff --git a/utils.py b/utils.py index edc5df6..02c3f2b 100644 --- a/utils.py +++ b/utils.py @@ -21,7 +21,7 @@ from datetime import datetime, timezone from collections import abc, OrderedDict from typing import Any, Literal, Callable, Generic, Mapping, TypeVar, ParamSpec, cast -import yarl +from yarl import URL from PIL.ImageTk import PhotoImage from PIL import Image as Image_module @@ -176,7 +176,7 @@ def _serialize(obj: Any) -> Any: # NOTE: IntEnum cannot be used, as it will get serialized as a plain integer, # then loaded back as an integer as well. d = obj.value - elif isinstance(obj, yarl.URL): + elif isinstance(obj, URL): d = str(obj) else: raise TypeError(obj) @@ -190,7 +190,7 @@ def _serialize(obj: Any) -> Any: _MISSING = object() SERIALIZE_ENV: dict[str, Callable[[Any], object]] = { "set": set, - "URL": yarl.URL, + "URL": URL, "PriorityMode": PriorityMode, "datetime": lambda d: datetime.fromtimestamp(d, timezone.utc), } @@ -254,7 +254,8 @@ def json_save(path: Path, contents: Mapping[Any, Any], *, sort: bool = False) -> json.dump(contents, file, default=_serialize, sort_keys=sort, indent=4) -def webopen(url: str): +def webopen(url: URL | str): + url_str = str(url) if IS_PACKAGED and sys.platform == "linux": # https://pyinstaller.org/en/stable/ # runtime-information.html#ld-library-path-libpath-considerations @@ -268,7 +269,7 @@ def webopen(url: str): # pop current os.environ.pop(ld_env) - webbrowser.open_new_tab(url) + webbrowser.open_new_tab(url_str) if ld_path_curr is not None: os.environ[ld_env] = ld_path_curr @@ -276,7 +277,7 @@ def webopen(url: str): # pop original os.environ.pop(ld_env) else: - webbrowser.open_new_tab(url) + webbrowser.open_new_tab(url_str) class ExponentialBackoff: