pythonlib: Cleanup & bump to 0.4.6

- `camoufox test` will no longer highlight the cursor by default
- Fixed launch_options blocking async
- WebGL database cleanup & added ability to query all possible vendor/renderer pairs
This commit is contained in:
daijro 2024-12-04 19:35:54 -06:00
parent 31963aa83b
commit 4c52518dd2
7 changed files with 54 additions and 23 deletions

View file

@ -114,7 +114,7 @@ def test(url: Optional[str] = None) -> None:
""" """
from .sync_api import Camoufox from .sync_api import Camoufox
with Camoufox(headless=False, env=environ) as browser: with Camoufox(headless=False, env=environ, config={'showcursor': False}) as browser:
page = browser.new_page() page = browser.new_page()
if url: if url:
page.goto(url) page.goto(url)

View file

@ -1,3 +1,5 @@
import asyncio
from functools import partial
from typing import Any, Dict, Optional, Union, overload from typing import Any, Dict, Optional, Union, overload
from playwright.async_api import ( from playwright.async_api import (
@ -82,13 +84,17 @@ async def AsyncNewBrowser(
else: else:
virtual_display = None virtual_display = None
opt = from_options or launch_options(headless=headless, debug=debug, **kwargs) if not from_options:
from_options = await asyncio.get_event_loop().run_in_executor(
None,
partial(launch_options, headless=headless, debug=debug, **kwargs),
)
# Persistent context # Persistent context
if persistent_context: if persistent_context:
context = await playwright.firefox.launch_persistent_context(**opt) context = await playwright.firefox.launch_persistent_context(**from_options)
return await async_attach_vd(context, virtual_display) return await async_attach_vd(context, virtual_display)
# Browser # Browser
browser = await playwright.firefox.launch(**opt) browser = await playwright.firefox.launch(**from_options)
return await async_attach_vd(browser, virtual_display) return await async_attach_vd(browser, virtual_display)

View file

@ -55,16 +55,16 @@ LOCAL_DATA: Path = Path(os.path.abspath(__file__)).parent
# The supported architectures for each OS # The supported architectures for each OS
OS_ARCH_MATRIX: Dict[str, List[str]] = { OS_ARCH_MATRIX: Dict[str, List[str]] = {
'mac': ['x86_64', 'arm64'],
'win': ['x86_64', 'i686'], 'win': ['x86_64', 'i686'],
'mac': ['x86_64', 'arm64'],
'lin': ['x86_64', 'arm64', 'i686'], 'lin': ['x86_64', 'arm64', 'i686'],
} }
# The relative path to the camoufox executable # The relative path to the camoufox executable
LAUNCH_FILE = { LAUNCH_FILE = {
'win': 'camoufox.exe', 'win': 'camoufox.exe',
'lin': 'camoufox-bin',
'mac': '../MacOS/camoufox', 'mac': '../MacOS/camoufox',
'lin': 'camoufox-bin',
} }

View file

@ -82,13 +82,14 @@ def NewBrowser(
else: else:
virtual_display = None virtual_display = None
opt = from_options or launch_options(headless=headless, debug=debug, **kwargs) if not from_options:
from_options = launch_options(headless=headless, debug=debug, **kwargs)
# Persistent context # Persistent context
if persistent_context: if persistent_context:
context = playwright.firefox.launch_persistent_context(**opt) context = playwright.firefox.launch_persistent_context(**from_options)
return sync_attach_vd(context, virtual_display) return sync_attach_vd(context, virtual_display)
# Browser # Browser
browser = playwright.firefox.launch(**opt) browser = playwright.firefox.launch(**from_options)
return sync_attach_vd(browser, virtual_display) return sync_attach_vd(browser, virtual_display)

View file

@ -1,10 +1,15 @@
import sqlite3 import sqlite3
from pathlib import Path from pathlib import Path
from typing import Dict, Optional from typing import Dict, List, Optional, Tuple
import numpy as np import numpy as np
import orjson import orjson
from camoufox.pkgman import OS_ARCH_MATRIX
# Get database path relative to this file
DB_PATH = Path(__file__).parent / 'webgl_data.db'
def sample_webgl( def sample_webgl(
os: str, vendor: Optional[str] = None, renderer: Optional[str] = None os: str, vendor: Optional[str] = None, renderer: Optional[str] = None
@ -24,23 +29,19 @@ def sample_webgl(
Raises: Raises:
ValueError: If invalid OS provided or no data found for OS/vendor/renderer ValueError: If invalid OS provided or no data found for OS/vendor/renderer
""" """
# Map OS to probability column # Check that the OS is valid (avoid SQL injection)
os_map = {'win': 'windows', 'mac': 'macos', 'lin': 'linux'} if os not in OS_ARCH_MATRIX:
if os not in os_map: raise ValueError(f'Invalid OS: {os}. Must be one of: win, mac, lin')
raise ValueError(f'Invalid OS: {os}. Must be one of: {", ".join(os_map)}')
os = os_map[os]
# Get database path relative to this file
db_path = Path(__file__).parent / 'webgl_data.db'
# Connect to database # Connect to database
conn = sqlite3.connect(db_path) conn = sqlite3.connect(DB_PATH)
cursor = conn.cursor() cursor = conn.cursor()
if vendor and renderer: if vendor and renderer:
# Get specific vendor/renderer pair and verify it exists for this OS # Get specific vendor/renderer pair and verify it exists for this OS
cursor.execute( cursor.execute(
f'SELECT vendor, renderer, data, {os} FROM webgl_fingerprints WHERE vendor = ? AND renderer = ?', f'SELECT vendor, renderer, data, {os} FROM webgl_fingerprints ' # nosec
'WHERE vendor = ? AND renderer = ?',
(vendor, renderer), (vendor, renderer),
) )
result = cursor.fetchone() result = cursor.fetchone()
@ -51,7 +52,7 @@ def sample_webgl(
if result[3] <= 0: # Check OS-specific probability if result[3] <= 0: # Check OS-specific probability
# Get a list of possible (vendor, renderer) pairs for this OS # Get a list of possible (vendor, renderer) pairs for this OS
cursor.execute( cursor.execute(
f'SELECT DISTINCT vendor, renderer FROM webgl_fingerprints WHERE {os} > 0' f'SELECT DISTINCT vendor, renderer FROM webgl_fingerprints WHERE {os} > 0' # nosec
) )
possible_pairs = cursor.fetchall() possible_pairs = cursor.fetchall()
raise ValueError( raise ValueError(
@ -63,7 +64,9 @@ def sample_webgl(
return orjson.loads(result[2]) return orjson.loads(result[2])
# Get all vendor/renderer pairs and their probabilities for this OS # Get all vendor/renderer pairs and their probabilities for this OS
cursor.execute(f'SELECT vendor, renderer, data, {os} FROM webgl_fingerprints WHERE {os} > 0') cursor.execute(
f'SELECT vendor, renderer, data, {os} FROM webgl_fingerprints WHERE {os} > 0' # nosec
)
results = cursor.fetchall() results = cursor.fetchall()
conn.close() conn.close()
@ -82,3 +85,24 @@ def sample_webgl(
# Parse the JSON data string # Parse the JSON data string
return orjson.loads(data_strs[idx]) return orjson.loads(data_strs[idx])
def get_possible_pairs() -> Dict[str, List[Tuple[str, str]]]:
"""
Get all possible (vendor, renderer) pairs for all OS, where the probability is greater than 0.
"""
# Connect to database
conn = sqlite3.connect(DB_PATH)
cursor = conn.cursor()
# Get all vendor/renderer pairs for each OS where probability > 0
result: Dict[str, List[Tuple[str, str]]] = {}
for os_type in OS_ARCH_MATRIX:
cursor.execute(
'SELECT DISTINCT vendor, renderer FROM webgl_fingerprints '
f'WHERE {os_type} > 0 ORDER BY {os_type} DESC', # nosec
)
result[os_type] = cursor.fetchall()
conn.close()
return result

View file

@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
[tool.poetry] [tool.poetry]
name = "camoufox" name = "camoufox"
version = "0.4.6-beta" version = "0.4.6"
description = "Wrapper around Playwright to help launch Camoufox" description = "Wrapper around Playwright to help launch Camoufox"
authors = ["daijro <daijro.dev@gmail.com>"] authors = ["daijro <daijro.dev@gmail.com>"]
license = "MIT" license = "MIT"