""" Cache layer: avoids re-downloading the same URL twice. Key = SHA256(url). Cache index kept as a flat JSON file so it survives restarts. """ import hashlib import json import os import threading CACHE_DIR = "cache" INDEX_PATH = os.path.join(CACHE_DIR, "index.json") _lock = threading.Lock() def _url_hash(url: str) -> str: return hashlib.sha256(url.encode("utf-8")).hexdigest() def _load_index() -> dict: if not os.path.exists(INDEX_PATH): return {} with open(INDEX_PATH, "r") as f: return json.load(f) def _save_index(index: dict): os.makedirs(CACHE_DIR, exist_ok=True) tmp = INDEX_PATH + ".tmp" with open(tmp, "w") as f: json.dump(index, f, indent=2) os.replace(tmp, INDEX_PATH) # atomic write, avoids torn reads def get_cached_path(url: str) -> str | None: with _lock: index = _load_index() entry = index.get(_url_hash(url)) if entry and os.path.exists(entry["path"]): return entry["path"] return None def register_cache(url: str, local_path: str): with _lock: index = _load_index() index[_url_hash(url)] = {"url": url, "path": local_path} _save_index(index) def cache_path_for(url: str, ext: str) -> str: os.makedirs(os.path.join(CACHE_DIR, "files"), exist_ok=True) return os.path.join(CACHE_DIR, "files", f"{_url_hash(url)}{ext}")