108 lines
2.9 KiB
Text
Executable file
108 lines
2.9 KiB
Text
Executable file
#!/usr/bin/env -S uv run --script
|
|
# /// script
|
|
# dependencies = ["playwright"]
|
|
# ///
|
|
"""
|
|
Headless Firefox with your cookies via cookiefire.
|
|
|
|
Usage:
|
|
playfox https://twitter.com
|
|
playfox -P jtm https://private.site
|
|
playfox --interactive https://site.com
|
|
"""
|
|
import subprocess
|
|
import sys
|
|
from playwright.sync_api import sync_playwright
|
|
|
|
|
|
def parse_netscape_cookies(cookie_text):
|
|
"""Convert cookiefire output to Playwright format."""
|
|
cookies = []
|
|
for line in cookie_text.strip().split("\n"):
|
|
if line.startswith("#") or not line.strip():
|
|
continue
|
|
parts = line.split("\t")
|
|
if len(parts) >= 7:
|
|
domain, _, path, secure, expires, name, value = parts[:7]
|
|
exp = float(expires)
|
|
if exp <= 0:
|
|
exp = -1
|
|
elif exp > 9999999999:
|
|
exp = exp / 1000
|
|
cookies.append(
|
|
{
|
|
"name": name,
|
|
"value": value,
|
|
"domain": domain,
|
|
"path": path,
|
|
"expires": exp,
|
|
"httpOnly": False,
|
|
"secure": secure.upper() == "TRUE",
|
|
"sameSite": "Lax",
|
|
}
|
|
)
|
|
return cookies
|
|
|
|
|
|
def main():
|
|
args = sys.argv[1:]
|
|
profile = "jtm"
|
|
url = None
|
|
dump = False
|
|
interactive = False
|
|
|
|
i = 0
|
|
while i < len(args):
|
|
if args[i] == "-P" and i + 1 < len(args):
|
|
profile = args[i + 1]
|
|
i += 2
|
|
elif args[i] == "--dump":
|
|
dump = True
|
|
i += 1
|
|
elif args[i] == "--interactive" or args[i] == "-i":
|
|
interactive = True
|
|
i += 1
|
|
elif args[i].startswith("http"):
|
|
url = args[i]
|
|
i += 1
|
|
else:
|
|
i += 1
|
|
|
|
if not url:
|
|
print("Usage: playfox [-P profile] [--dump] [--interactive] URL", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
# get cookies
|
|
cookie_text = subprocess.check_output(["cookiefire", profile], text=True)
|
|
cookies = parse_netscape_cookies(cookie_text)
|
|
|
|
with sync_playwright() as p:
|
|
browser = p.firefox.launch(headless=True)
|
|
context = browser.new_context()
|
|
context.add_cookies(cookies)
|
|
|
|
page = context.new_page()
|
|
|
|
# logging
|
|
page.on("console", lambda msg: print(f"[console] {msg.text}"))
|
|
page.on("pageerror", lambda err: print(f"[error] {err}"))
|
|
page.on("request", lambda req: print(f"[req] {req.method} {req.url}"))
|
|
|
|
page.goto(url)
|
|
|
|
if dump:
|
|
print(page.content())
|
|
|
|
if interactive:
|
|
print("\n--- interactive mode ---")
|
|
print("page object available as 'page'")
|
|
print("ctrl-d to exit\n")
|
|
import code
|
|
|
|
code.interact(local={"page": page, "context": context, "browser": browser})
|
|
|
|
browser.close()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|