Replace Playwright with requests for login

Playwright is heavyweight and currently broken. The login now uses the OAuth2
token endpoint directly with requests library instead of browser automation.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Edward Betts 2026-02-03 10:10:55 +00:00
parent 2ade1eee5e
commit cdf7c3e2bc

View file

@ -12,7 +12,6 @@ from datetime import date, datetime, timezone
import ics # type: ignore import ics # type: ignore
import jinja2 import jinja2
import requests import requests
from playwright.sync_api import Playwright, sync_playwright # type: ignore
base_dir = os.path.dirname(__file__) base_dir = os.path.dirname(__file__)
@ -41,28 +40,43 @@ dest = config["location"]["dest"]
ics_file = config["location"]["ics_file"] ics_file = config["location"]["ics_file"]
def run(playwright: Playwright) -> None: def perform_login() -> None:
"""Login to the Wheelie Fresh Bin website.""" """Login to the Wheelie Fresh Bin website using OAuth2 token endpoint."""
browser = playwright.chromium.launch(headless=True) session = requests.Session()
context = browser.new_context()
page = context.new_page() # OAuth2 password grant format
token_data = {
"grant_type": "password",
"username": username,
"password": password,
}
page.goto(login_url) # POST to the token endpoint
page.locator('input[name="UserName"]').fill(username) token_url = "https://portal.wheeliefreshbins.com/Token"
page.locator('input[name="Password"]').fill(password) response = session.post(token_url, data=token_data)
page.locator('input[name="RememberMe"]').check()
with page.expect_navigation(url=summary_url): if response.status_code != 200:
page.locator('input:has-text("Log in")').click() raise Exception(f"Login failed with status {response.status_code}: {response.text}")
page.locator('a:has-text("Schedule")').click() # The token endpoint should set the authentication cookie
cookie_value = session.cookies.get(".AspNet.Cookies")
if not cookie_value:
raise Exception("Authentication cookie not found after login")
page.close() # Save cookie in the same format as Playwright did
auth_data = {
"cookies": [
{
"name": ".AspNet.Cookies",
"value": cookie_value,
"domain": "portal.wheeliefreshbins.com",
"path": "/",
}
]
}
context.storage_state(path=auth_json_path) with open(auth_json_path, "w") as f:
context.close() json.dump(auth_data, f, indent=2)
browser.close()
def get_cookie_value() -> str: def get_cookie_value() -> str:
@ -92,8 +106,7 @@ def read_html_from_json(r: requests.models.Response) -> str:
def login() -> None: def login() -> None:
"""Login to Wheelie Fresh Bins.""" """Login to Wheelie Fresh Bins."""
with sync_playwright() as playwright: perform_login()
run(playwright)
def get_schedule_html() -> str | typing.NoReturn: def get_schedule_html() -> str | typing.NoReturn: