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 jinja2
import requests
from playwright.sync_api import Playwright, sync_playwright # type: ignore
base_dir = os.path.dirname(__file__)
@ -41,28 +40,43 @@ dest = config["location"]["dest"]
ics_file = config["location"]["ics_file"]
def run(playwright: Playwright) -> None:
"""Login to the Wheelie Fresh Bin website."""
browser = playwright.chromium.launch(headless=True)
context = browser.new_context()
def perform_login() -> None:
"""Login to the Wheelie Fresh Bin website using OAuth2 token endpoint."""
session = requests.Session()
page = context.new_page()
# OAuth2 password grant format
token_data = {
"grant_type": "password",
"username": username,
"password": password,
}
page.goto(login_url)
page.locator('input[name="UserName"]').fill(username)
page.locator('input[name="Password"]').fill(password)
page.locator('input[name="RememberMe"]').check()
# POST to the token endpoint
token_url = "https://portal.wheeliefreshbins.com/Token"
response = session.post(token_url, data=token_data)
with page.expect_navigation(url=summary_url):
page.locator('input:has-text("Log in")').click()
if response.status_code != 200:
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)
context.close()
browser.close()
with open(auth_json_path, "w") as f:
json.dump(auth_data, f, indent=2)
def get_cookie_value() -> str:
@ -92,8 +106,7 @@ def read_html_from_json(r: requests.models.Response) -> str:
def login() -> None:
"""Login to Wheelie Fresh Bins."""
with sync_playwright() as playwright:
run(playwright)
perform_login()
def get_schedule_html() -> str | typing.NoReturn: