Source code for cycletls.plugins
"""Plugin loading utilities for CycleTLS fingerprints.
This module provides utilities for loading fingerprint profiles from external
sources like JSON files and directories.
Example:
>>> from cycletls.plugins import load_fingerprints_from_dir, load_fingerprints_from_env
>>>
>>> # Load from a specific directory
>>> load_fingerprints_from_dir("/path/to/fingerprints")
>>>
>>> # Or automatically load from CYCLETLS_FINGERPRINTS_DIR env var
>>> load_fingerprints_from_env()
"""
from __future__ import annotations
import logging
import os
from pathlib import Path
from .fingerprints import FingerprintRegistry, TLSFingerprint
logger = logging.getLogger(__name__)
[docs]
def load_fingerprints_from_dir(directory: str | Path) -> int:
"""Load all JSON fingerprint files from a directory.
Scans the given directory for .json files and attempts to load each
as a TLSFingerprint. Invalid files are logged and skipped.
Args:
directory: Path to directory containing fingerprint JSON files
Returns:
Number of fingerprints successfully loaded
Example:
>>> count = load_fingerprints_from_dir("/path/to/fingerprints")
>>> print(f"Loaded {count} fingerprint profiles")
"""
path = Path(directory)
if not path.exists():
logger.warning(f"Fingerprint directory does not exist: {directory}")
return 0
if not path.is_dir():
logger.warning(f"Path is not a directory: {directory}")
return 0
loaded = 0
for json_file in path.glob("*.json"):
try:
profile = TLSFingerprint.from_json(json_file)
FingerprintRegistry.register(profile)
logger.debug(f"Loaded fingerprint profile '{profile.name}' from {json_file}")
loaded += 1
except Exception as e:
logger.warning(f"Failed to load fingerprint from {json_file}: {e}")
logger.info(f"Loaded {loaded} fingerprint profiles from {directory}")
return loaded
[docs]
def load_fingerprints_from_env() -> int:
"""Load fingerprints from directory specified in CYCLETLS_FINGERPRINTS_DIR.
If the environment variable is set, loads all .json files from that directory.
If the variable is not set, does nothing.
Returns:
Number of fingerprints loaded, or 0 if env var not set
Example:
>>> import os
>>> os.environ["CYCLETLS_FINGERPRINTS_DIR"] = "/my/fingerprints"
>>> count = load_fingerprints_from_env()
"""
dir_path = os.environ.get("CYCLETLS_FINGERPRINTS_DIR")
if dir_path:
return load_fingerprints_from_dir(dir_path)
return 0
[docs]
def load_fingerprint_from_file(path: str | Path) -> TLSFingerprint:
"""Load a single fingerprint from a JSON file and register it.
Args:
path: Path to the JSON fingerprint file
Returns:
The loaded TLSFingerprint
Raises:
FileNotFoundError: If file doesn't exist
json.JSONDecodeError: If file is not valid JSON
KeyError: If required fields are missing
"""
profile = TLSFingerprint.from_json(path)
FingerprintRegistry.register(profile)
logger.debug(f"Loaded and registered fingerprint profile '{profile.name}'")
return profile
[docs]
def create_fingerprint_template(path: str | Path, name: str = "custom_browser") -> None:
"""Create a template fingerprint JSON file for users to customize.
Args:
path: Path to save the template file
name: Name for the fingerprint profile
"""
template = TLSFingerprint(
name=name,
ja3=(
"771,4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-"
"47-53,0-23-65281-10-11-35-16-5-13-18-51-45-43-27-17513,29-23-24,0"
),
http2_fingerprint="1:65536,2:0,3:1000,4:6291456,6:262144|15663105|0|m,a,s,p",
user_agent=(
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
),
header_order=[
"host",
"connection",
"user-agent",
"accept",
"accept-encoding",
"accept-language",
"cookie",
],
)
template.to_json(path)
logger.info(f"Created fingerprint template at {path}")
__all__ = [
"load_fingerprints_from_dir",
"load_fingerprints_from_env",
"load_fingerprint_from_file",
"create_fingerprint_template",
]