pythonlib: Add enable_cache, fixed font spacing, etc 0.3.9

- Enable bf cache with `enable_cache=True` #74
- Font spacing is now fixed per session #63
- Re-download (or raise error) when target install path is empty. Caused when the user cancels the download.
- Bumped minimum version to beta.15
This commit is contained in:
daijro 2024-11-11 22:11:43 -06:00
parent 476e119c4f
commit 3a33283cd2
5 changed files with 73 additions and 19 deletions

View file

@ -8,7 +8,7 @@ class CONSTRAINTS:
The minimum and maximum supported versions of the Camoufox browser. The minimum and maximum supported versions of the Camoufox browser.
""" """
MIN_VERSION = 'beta.12' MIN_VERSION = 'beta.15'
MAX_VERSION = '1' MAX_VERSION = '1'
@staticmethod @staticmethod

View file

@ -178,3 +178,11 @@ class VirtualDisplayNotSupported(VirtualDisplayError):
""" """
... ...
class CamoufoxNotInstalled(FileNotFoundError):
"""
Raised when camoufox is not installed.
"""
...

View file

@ -21,7 +21,12 @@ from typing_extensions import TypeAlias
from yaml import CLoader, load from yaml import CLoader, load
from .__version__ import CONSTRAINTS from .__version__ import CONSTRAINTS
from .exceptions import UnsupportedArchitecture, UnsupportedOS, UnsupportedVersion from .exceptions import (
CamoufoxNotInstalled,
UnsupportedArchitecture,
UnsupportedOS,
UnsupportedVersion,
)
DownloadBuffer: TypeAlias = Union[BytesIO, tempfile._TemporaryFileWrapper, BufferedWriter] DownloadBuffer: TypeAlias = Union[BytesIO, tempfile._TemporaryFileWrapper, BufferedWriter]
@ -55,6 +60,13 @@ OS_ARCH_MATRIX: Dict[str, List[str]] = {
'lin': ['x86_64', 'arm64', 'i686'], 'lin': ['x86_64', 'arm64', 'i686'],
} }
# The relative path to the camoufox executable
LAUNCH_FILE = {
'win': 'camoufox.exe',
'lin': 'camoufox-bin',
'mac': '../MacOS/camoufox',
}
def rprint(*a, **k): def rprint(*a, **k):
click.secho(*a, **k, bold=True) click.secho(*a, **k, bold=True)
@ -102,7 +114,7 @@ class Version:
if not os.path.exists(version_path): if not os.path.exists(version_path):
raise FileNotFoundError( raise FileNotFoundError(
f"Version information not found at {version_path}. " f"Version information not found at {version_path}. "
"You are likely using an unsupported version of Camoufox." "Please run `camoufox fetch` to install."
) )
with open(version_path, 'rb') as f: with open(version_path, 'rb') as f:
version_data = orjson.loads(f.read()) version_data = orjson.loads(f.read())
@ -358,14 +370,16 @@ def camoufox_path(download_if_missing: bool = True) -> Path:
""" """
Full path to the camoufox folder. Full path to the camoufox folder.
""" """
if os.path.exists(INSTALL_DIR) and Version.from_path().is_supported():
return INSTALL_DIR
# Ensure the directory exists # Ensure the directory exists and is not empty
if not os.path.exists(INSTALL_DIR): if not os.path.exists(INSTALL_DIR) or not os.listdir(INSTALL_DIR):
if not download_if_missing: if not download_if_missing:
raise FileNotFoundError(f"Camoufox executable not found at {INSTALL_DIR}") raise FileNotFoundError(f"Camoufox executable not found at {INSTALL_DIR}")
# Camoufox exists and the the version is supported
elif os.path.exists(INSTALL_DIR) and Version.from_path().is_supported():
return INSTALL_DIR
# Ensure the version is supported # Ensure the version is supported
else: else:
if not download_if_missing: if not download_if_missing:
@ -385,6 +399,19 @@ def get_path(file: str) -> str:
return str(camoufox_path() / file) return str(camoufox_path() / file)
def launch_path() -> str:
"""
Get the path to the camoufox executable.
"""
launch_path = get_path(LAUNCH_FILE[OS_NAME])
if not os.path.exists(launch_path):
# Not installed error
raise CamoufoxNotInstalled(
f"Camoufox is not installed at {camoufox_path()}. Please run `camoufox fetch` to install."
)
return launch_path
def webdl( def webdl(
url: str, url: str,
desc: Optional[str] = None, desc: Optional[str] = None,

View file

@ -4,7 +4,7 @@ from os import environ
from os.path import abspath from os.path import abspath
from pathlib import Path from pathlib import Path
from pprint import pprint from pprint import pprint
from random import randrange from random import randint, randrange
from typing import Any, Dict, List, Literal, Optional, Tuple, Union, cast from typing import Any, Dict, List, Literal, Optional, Tuple, Union, cast
import numpy as np import numpy as np
@ -29,19 +29,22 @@ from .exceptions import (
from .fingerprints import from_browserforge, generate_fingerprint from .fingerprints import from_browserforge, generate_fingerprint
from .ip import Proxy, public_ip, valid_ipv4, valid_ipv6 from .ip import Proxy, public_ip, valid_ipv4, valid_ipv6
from .locale import geoip_allowed, get_geolocation, handle_locales from .locale import geoip_allowed, get_geolocation, handle_locales
from .pkgman import OS_NAME, get_path, installed_verstr from .pkgman import OS_NAME, get_path, installed_verstr, launch_path
from .virtdisplay import VirtualDisplay from .virtdisplay import VirtualDisplay
from .warnings import LeakWarning from .warnings import LeakWarning
from .xpi_dl import add_default_addons from .xpi_dl import add_default_addons
LAUNCH_FILE = {
'win': 'camoufox.exe',
'lin': 'camoufox-bin',
'mac': '../MacOS/camoufox',
}
ListOrString: TypeAlias = Union[Tuple[str, ...], List[str], str] ListOrString: TypeAlias = Union[Tuple[str, ...], List[str], str]
# Camoufox preferences to cache previous pages and requests
CACHE_PREFS = {
'browser.sessionhistory.max_entries': 10,
'browser.sessionhistory.max_total_viewers': -1,
'browser.cache.memory.enable': True,
'browser.cache.disk_cache_ssl': True,
'browser.cache.disk.smart_size.enabled': True,
}
def get_env_vars( def get_env_vars(
config_map: Dict[str, str], user_agent_os: str config_map: Dict[str, str], user_agent_os: str
@ -354,9 +357,10 @@ def launch_options(
fingerprint: Optional[Fingerprint] = None, fingerprint: Optional[Fingerprint] = None,
ff_version: Optional[int] = None, ff_version: Optional[int] = None,
headless: Optional[bool] = None, headless: Optional[bool] = None,
executable_path: Optional[str] = None, executable_path: Optional[Union[str, Path]] = None,
firefox_user_prefs: Optional[Dict[str, Any]] = None, firefox_user_prefs: Optional[Dict[str, Any]] = None,
proxy: Optional[Dict[str, str]] = None, proxy: Optional[Dict[str, str]] = None,
enable_cache: Optional[bool] = None,
args: Optional[List[str]] = None, args: Optional[List[str]] = None,
env: Optional[Dict[str, Union[str, float, bool]]] = None, env: Optional[Dict[str, Union[str, float, bool]]] = None,
i_know_what_im_doing: Optional[bool] = None, i_know_what_im_doing: Optional[bool] = None,
@ -413,13 +417,15 @@ def launch_options(
Whether to run the browser in headless mode. Defaults to False. Whether to run the browser in headless mode. Defaults to False.
Note: If you are running linux, passing headless='virtual' to Camoufox & AsyncCamoufox Note: If you are running linux, passing headless='virtual' to Camoufox & AsyncCamoufox
will use Xvfb. will use Xvfb.
executable_path (Optional[str]): executable_path (Optional[Union[str, Path]]):
Custom Camoufox browser executable path. Custom Camoufox browser executable path.
firefox_user_prefs (Optional[Dict[str, Any]]): firefox_user_prefs (Optional[Dict[str, Any]]):
Firefox user preferences to set. Firefox user preferences to set.
proxy (Optional[Dict[str, str]]): proxy (Optional[Dict[str, str]]):
Proxy to use for the browser. Proxy to use for the browser.
Note: If geoip is True, a request will be sent through this proxy to find the target IP. Note: If geoip is True, a request will be sent through this proxy to find the target IP.
enable_cache (Optional[bool]):
Cache previous pages, requests, etc (uses more memory).
args (Optional[List[str]]): args (Optional[List[str]]):
Arguments to pass to the browser. Arguments to pass to the browser.
env (Optional[Dict[str, Union[str, float, bool]]]): env (Optional[Dict[str, Union[str, float, bool]]]):
@ -449,6 +455,7 @@ def launch_options(
if env is None: if env is None:
env = cast(Dict[str, Union[str, float, bool]], environ) env = cast(Dict[str, Union[str, float, bool]], environ)
if isinstance(executable_path, str): if isinstance(executable_path, str):
# Convert executable path to a Path object
executable_path = Path(abspath(executable_path)) executable_path = Path(abspath(executable_path))
# Handle virtual display # Handle virtual display
@ -504,6 +511,8 @@ def launch_options(
if fonts: if fonts:
config['fonts'] = fonts config['fonts'] = fonts
update_fonts(config, target_os) update_fonts(config, target_os)
# Set a fixed font spacing seed
set_into(config, 'fonts:spacing_seed', randint(0, 2147483647)) # nosec
# Set geolocation # Set geolocation
if geoip: if geoip:
@ -563,6 +572,10 @@ def launch_options(
LeakWarning.warn('allow_webgl', i_know_what_im_doing) LeakWarning.warn('allow_webgl', i_know_what_im_doing)
firefox_user_prefs['webgl.disabled'] = False firefox_user_prefs['webgl.disabled'] = False
# Cache previous pages, requests, etc (uses more memory)
if enable_cache:
firefox_user_prefs.update(CACHE_PREFS)
# Load the addons # Load the addons
threaded_try_load_addons(get_debug_port(args), addons) threaded_try_load_addons(get_debug_port(args), addons)
@ -571,8 +584,14 @@ def launch_options(
**get_env_vars(config, target_os), **get_env_vars(config, target_os),
**env, **env,
} }
# Prepare the executable path
if executable_path:
executable_path = str(executable_path)
else:
executable_path = launch_path()
return { return {
"executable_path": executable_path or get_path(LAUNCH_FILE[OS_NAME]), "executable_path": executable_path,
"args": args, "args": args,
"env": env_vars, "env": env_vars,
"firefox_user_prefs": firefox_user_prefs, "firefox_user_prefs": firefox_user_prefs,

View file

@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
[tool.poetry] [tool.poetry]
name = "camoufox" name = "camoufox"
version = "0.3.8" version = "0.3.9"
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"