omegafox/pythonlib/camoufox/server.py
daijro 79c436e506 pythonlib: Add remote server launching #7
Uses a hacky work around to run Javascript in Playwright's internal library to gain access to the launchServer method.
2024-10-02 02:45:26 -05:00

70 lines
1.9 KiB
Python

import subprocess
from pathlib import Path
from typing import Any, Dict, NoReturn, Tuple, Union
import orjson
from playwright._impl._driver import compute_driver_executable
from camoufox.pkgman import LOCAL_DATA
from camoufox.utils import get_launch_options
LAUNCH_SCRIPT: Path = LOCAL_DATA / "launchServer.js"
def camel_case(snake_str: str) -> str:
"""
Convert a string to camelCase
"""
if len(snake_str) < 2:
return snake_str
camel_case_str = ''.join(x.capitalize() for x in snake_str.lower().split('_'))
return camel_case_str[0].lower() + camel_case_str[1:]
def to_camel_case_dict(data: Dict[str, Any]) -> Dict[str, Any]:
"""
Convert a dictionary to camelCase
"""
return {camel_case(key): value for key, value in data.items()}
def get_nodejs() -> str:
"""
Get the bundled Node.js executable
"""
# Note: Older versions of Playwright return a string rather than a tuple.
_nodejs: Union[str, Tuple[str, ...]] = compute_driver_executable()[0]
if isinstance(_nodejs, tuple):
return _nodejs[0]
return _nodejs
def launch_server(**kwargs) -> NoReturn:
"""
Launch a Playwright server. Takes the same arguments as `Camoufox()`.
Prints the websocket endpoint to the console.
"""
config = get_launch_options(**kwargs)
nodejs = get_nodejs()
data = orjson.dumps(to_camel_case_dict(config)).decode()
process = subprocess.Popen( # nosec
[
nodejs,
str(LAUNCH_SCRIPT),
],
cwd=Path(nodejs).parent / "package",
stdin=subprocess.PIPE,
text=True,
)
# Write data to stdin and close the stream
if process.stdin:
process.stdin.write(data)
process.stdin.close()
# Wait forever
process.wait()
# Add an explicit return statement to satisfy the NoReturn type hint
raise RuntimeError("Server process terminated unexpectedly")