diff --git a/.env.example b/.env.example index 816abb4..1b92287 100644 --- a/.env.example +++ b/.env.example @@ -12,9 +12,10 @@ ABOGEN_OUTPUT_DIR=./storage/output # Temporary working directory. When running in Docker, keep this inside the # mounted data volume (or another writable host path) so non-root users can -# write to it. For local (non-Docker) usage, change this to a path that makes -# sense on your machine or comment it out to fall back to the OS cache -# directory. Mounted to /data/cache in Docker. +# write to it. Hugging Face caches and other temp files are redirected here via +# XDG_CACHE_HOME/HF_HOME when running under Docker. For local (non-Docker) +# usage, change this to a path that makes sense on your machine or comment it +# out to fall back to the OS cache directory. Mounted to /data/cache in Docker. ABOGEN_TEMP_DIR=./storage/tmp # UID/GID used when running the Docker container. 1000:1000 matches most Linux hosts. diff --git a/README.md b/README.md index 3fdd8f2..b0488f0 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,10 @@ Browse to http://localhost:8808. Uploaded source files are stored in `/data/uplo | `ABOGEN_OUTPUT_DIR` | `/data/outputs` | Container path for rendered audio/subtitles | | `ABOGEN_SETTINGS_DIR` | `/config` | Container path for JSON settings/configuration | | `ABOGEN_TEMP_DIR` | `/data/cache` (Docker) or platform cache dir | Container path for temporary files | +| `XDG_CACHE_HOME` | `/data/cache` | Base directory for cache data (used by many Python libraries) | +| `HF_HOME` | `/data/cache/huggingface` | Hugging Face model cache directory | +| `HUGGINGFACE_HUB_CACHE` | `/data/cache/huggingface` | Alias for Hugging Face hub cache | +| `TRANSFORMERS_CACHE` | `/data/cache/huggingface` | Transformers model cache directory | | `ABOGEN_UID` | `1000` | UID that the container should run as (matches host user) | | `ABOGEN_GID` | `1000` | GID that the container should run as (matches host group) | @@ -71,7 +75,10 @@ When running via Docker Compose, set `ABOGEN_SETTINGS_DIR`, `ABOGEN_OUTPUT_DIR`, and `ABOGEN_TEMP_DIR` in your `.env` file to the host directories you want mounted into the container. Compose maps them to `/config`, `/data/outputs`, and `/data/cache` respectively while exporting -those in-container paths to the application. Ensure each host directory exists +those in-container paths to the application. The cache mount is also shared +with common environment variables (`XDG_CACHE_HOME`, `HF_HOME`, +`HUGGINGFACE_HUB_CACHE`, `TRANSFORMERS_CACHE`) so libraries like Hugging Face +store downloads in the same writable location. Ensure each host directory exists and is writable by the UID/GID you configure before starting the stack. ### Docker Compose (GPU by default) diff --git a/abogen/utils.py b/abogen/utils.py index 1d004dd..c6835cf 100644 --- a/abogen/utils.py +++ b/abogen/utils.py @@ -158,32 +158,51 @@ def get_user_cache_root(): if last_error is not None: raise last_error + def _configure_cache_env(cache_path): + os.environ.setdefault("XDG_CACHE_HOME", cache_path) + + hf_cache = os.path.join(cache_path, "huggingface") + for env_var in ("HF_HOME", "HUGGINGFACE_HUB_CACHE", "TRANSFORMERS_CACHE"): + os.environ.setdefault(env_var, hf_cache) + + # Ensure Hugging Face cache directory exists so downloads succeed. + ensure_directory(hf_cache) + + if not os.environ.get("HOME"): + os.environ["HOME"] = os.path.dirname(cache_path) or "/" + + cache_root = None + override = os.environ.get("ABOGEN_TEMP_DIR") if override: try: - return ensure_directory(override) + cache_root = ensure_directory(override) except OSError as exc: logger.warning("ABOGEN_TEMP_DIR=%s is not writable: %s", override, exc) - from platformdirs import user_cache_dir + if cache_root is None: + from platformdirs import user_cache_dir - default_cache = user_cache_dir("abogen", appauthor=False, opinion=True) + default_cache = user_cache_dir("abogen", appauthor=False, opinion=True) - data_root = os.environ.get("ABOGEN_DATA") or os.environ.get("ABOGEN_DATA_DIR") - fallback_paths = [ - default_cache, - os.path.join(data_root, "cache") if data_root else None, - "/data/cache", - "/tmp/abogen-cache", - ] + data_root = os.environ.get("ABOGEN_DATA") or os.environ.get("ABOGEN_DATA_DIR") + fallback_paths = [ + default_cache, + os.path.join(data_root, "cache") if data_root else None, + "/data/cache", + "/tmp/abogen-cache", + ] - try: - return _try_paths(*fallback_paths) - except OSError: - # Final safety net – attempt a tmp directory unique to this process. - tmp_candidate = os.path.join("/tmp", f"abogen-cache-{os.getpid()}") - logger.warning("Falling back to temp cache directory %s", tmp_candidate) - return ensure_directory(tmp_candidate) + try: + cache_root = _try_paths(*fallback_paths) + except OSError: + # Final safety net – attempt a tmp directory unique to this process. + tmp_candidate = os.path.join("/tmp", f"abogen-cache-{os.getpid()}") + logger.warning("Falling back to temp cache directory %s", tmp_candidate) + cache_root = ensure_directory(tmp_candidate) + + _configure_cache_env(cache_root) + return cache_root def get_user_cache_path(folder=None): diff --git a/docker-compose.yaml b/docker-compose.yaml index 828029e..4332c6a 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -23,6 +23,11 @@ services: ABOGEN_OUTPUT_DIR: "/data/outputs" ABOGEN_OUTPUT_ROOT: "/data/outputs" ABOGEN_TEMP_DIR: "/data/cache" + HOME: "/data" + XDG_CACHE_HOME: "/data/cache" + HF_HOME: "/data/cache/huggingface" + HUGGINGFACE_HUB_CACHE: "/data/cache/huggingface" + TRANSFORMERS_CACHE: "/data/cache/huggingface" # --- GPU support ----------------------------------------------------- # These settings assume the NVIDIA Container Toolkit is installed. # Leave them in place for GPU acceleration; comment out the entire block