mirror of
https://github.com/daijro/camoufox.git
synced 2026-09-21 16:00:58 +00:00
maybe_download_addons() treated an addon as already downloaded whenever its
directory existed. A download that fails partway leaves an empty directory
behind, which is then trusted on every later launch, so confirm_paths()
raises InvalidAddonPath: manifest.json is missing and never recovers. Gate
the check on manifest.json presence and rmtree the partial directory on
failure. Closes #308.
(cherry picked from commit 0a8211969b)
98 lines
3.2 KiB
Python
98 lines
3.2 KiB
Python
import os
|
|
import shutil
|
|
from enum import Enum
|
|
from multiprocessing import Lock
|
|
from typing import List, Optional
|
|
|
|
from .exceptions import InvalidAddonPath
|
|
from .pkgman import INSTALL_DIR, unzip, webdl
|
|
|
|
# Addons are stored in a shared folder, not per-browser version
|
|
ADDONS_DIR = INSTALL_DIR / "addons"
|
|
|
|
|
|
class DefaultAddons(Enum):
|
|
"""
|
|
Default addons to be downloaded
|
|
"""
|
|
|
|
UBO = "https://addons.mozilla.org/firefox/downloads/latest/ublock-origin/latest.xpi"
|
|
|
|
|
|
def confirm_paths(paths: List[str]) -> None:
|
|
"""
|
|
Confirms that the addon paths are valid
|
|
"""
|
|
for path in paths:
|
|
if not os.path.isdir(path):
|
|
raise InvalidAddonPath(path)
|
|
if not os.path.exists(os.path.join(path, 'manifest.json')):
|
|
raise InvalidAddonPath(
|
|
'manifest.json is missing. Addon path must be a path to an extracted addon.'
|
|
)
|
|
|
|
|
|
def add_default_addons(
|
|
addons_list: List[str], exclude_list: Optional[List[DefaultAddons]] = None
|
|
) -> None:
|
|
"""
|
|
Adds default addons, minus any specified in exclude_list, to addons_list
|
|
"""
|
|
# Build a dictionary from DefaultAddons, excluding keys found in exclude_list
|
|
if exclude_list is None:
|
|
exclude_list = []
|
|
|
|
addons = [addon for addon in DefaultAddons if addon not in exclude_list]
|
|
|
|
with Lock():
|
|
maybe_download_addons(addons, addons_list)
|
|
|
|
|
|
def download_and_extract(url: str, extract_path: str, name: str) -> None:
|
|
"""
|
|
Downloads and extracts an addon from a given URL to a specified path
|
|
"""
|
|
# Create a temporary file to store the downloaded zip
|
|
buffer = webdl(url, desc=f"Downloading addon ({name})", bar=False)
|
|
unzip(buffer, extract_path, f"Extracting addon ({name})", bar=False)
|
|
|
|
|
|
def get_addon_path(addon_name: str) -> str:
|
|
"""
|
|
Returns a path to the addon in the shared addons folder.
|
|
"""
|
|
return str(ADDONS_DIR / addon_name)
|
|
|
|
|
|
def maybe_download_addons(
|
|
addons: List[DefaultAddons], addons_list: Optional[List[str]] = None
|
|
) -> None:
|
|
"""
|
|
Downloads and extracts addons from a given dictionary to a specified list
|
|
Skips downloading if the addon is already downloaded
|
|
"""
|
|
for addon in addons:
|
|
# Get the addon path
|
|
addon_path = get_addon_path(addon.name)
|
|
|
|
# Check if the addon is already extracted. A bare directory is not
|
|
# enough: a failed download leaves an empty dir behind, so require the
|
|
# manifest that confirm_paths() looks for.
|
|
if os.path.exists(os.path.join(addon_path, 'manifest.json')):
|
|
# Add the existing addon path to addons_list
|
|
if addons_list is not None:
|
|
addons_list.append(addon_path)
|
|
continue
|
|
|
|
# Addon isn't extracted, create directory and download
|
|
try:
|
|
os.makedirs(addon_path, exist_ok=True)
|
|
download_and_extract(addon.value, addon_path, addon.name)
|
|
# Add the new addon directory path to addons_list
|
|
if addons_list is not None:
|
|
addons_list.append(addon_path)
|
|
except Exception as e:
|
|
# Drop the partial directory so the next run re-downloads.
|
|
shutil.rmtree(addon_path, ignore_errors=True)
|
|
print(f"Failed to download and extract {addon.name}: {e}")
|