--- name: comfyui_fetch_civitai_image kind: function lang: py domain: ml version: "1.0.0" purity: impure signature: "def comfyui_fetch_civitai_image(image_url: str, *, dest_dir: str, nsfw: bool = False, nsfw_subdir: str = \"nsfw\", token: str | None = None, prefer_original: bool = True, timeout_s: float = 120.0) -> dict" description: "Descarga el PNG de una imagen de Civitai a disco, SEGREGANDO el NSFW a una subcarpeta marcada (//). Reescribe la URL redimensionada (/width=N/) a la original (/original=true/) para conservar el workflow ComfyUI embebido. Aplica la misma validacion no-HTML que comfyui_download_model (rechaza paginas de error/login de Cloudflare disfrazadas de imagen) y nombra el archivo por el UUID de la imagen para evitar colisiones. Impura: HTTP GET + escritura en disco." tags: [comfyui, civitai, ml, download, images, nsfw, http] uses_functions: [] uses_types: [] returns: [] returns_optional: false error_type: "error_go_core" imports: ["os", "re", "urllib.error", "urllib.parse", "urllib.request"] params: - name: image_url desc: "URL de la imagen (campo url de comfyui_search_civitai_images)." - name: dest_dir desc: "Carpeta destino (se expande ~). Se crea si no existe. keyword-only, obligatorio." - name: nsfw desc: "Si True, la imagen se guarda en // en vez de directamente en dest_dir (segregacion obligatoria de NSFW). keyword-only." - name: nsfw_subdir desc: "Nombre de la subcarpeta para NSFW. Default 'nsfw'. keyword-only." - name: token desc: "Token Civitai (header Authorization Bearer). Algunas imagenes lo exigen para el original. None lo omite. No hardcodear. keyword-only." - name: prefer_original desc: "Si True (default) reescribe /width=N/ a /original=true/ para conservar el workflow embebido. keyword-only." - name: timeout_s desc: "Timeout HTTP en segundos. keyword-only." output: "dict {ok, path, size_bytes, nsfw, error}. ok=False si la respuesta era HTML de error, demasiado pequena, o fallo la red/escritura (sin dejar basura en disco). nsfw refleja la carpeta usada." tested: false tests: [] test_file_path: "" file_path: "python/functions/ml/comfyui_fetch_civitai_image.py" --- ## Ejemplo ```python import sys, os sys.path.insert(0, os.path.join(os.environ["HOME"], "fn_registry", "python", "functions")) from ml.comfyui_search_civitai_images import comfyui_search_civitai_images from ml.comfyui_fetch_civitai_image import comfyui_fetch_civitai_image sr = comfyui_search_civitai_images(nsfw="None", sort="Most Reactions", limit=5) item = sr["items"][0] # Imagen SFW -> directamente en dest_dir. res = comfyui_fetch_civitai_image(item["url"], dest_dir=os.path.expanduser("~/ComfyUI/civitai_harvest"), nsfw=item["nsfw"]) print(res["ok"], res["path"]) # ~/ComfyUI/civitai_harvest/.png # Imagen NSFW -> segregada a la subcarpeta nsfw/. # comfyui_fetch_civitai_image(url, dest_dir="...", nsfw=True) -> .../nsfw/.png ``` ## Cuando usarla Tras `comfyui_search_civitai_images`, para bajar el PNG original de una imagen (con su workflow ComfyUI embebido) y luego destilarlo con `comfyui_extract_recipe_from_png`. Pasa `nsfw=True` (del item del search) para que el contenido adulto quede SIEMPRE en su carpeta separada. ## Gotchas - **Segregacion NSFW**: la politica del sistema permite NSFW pero SIEMPRE separado. Pasa `nsfw=True` y la imagen va a `/nsfw/`; nunca mezcles adulto y SFW en la misma carpeta. - **El PNG es un secreto potencial**: el workflow embebido puede traer prompts NSFW y, ocasionalmente, rutas locales del autor. Trata el archivo como dato (no lo commitees; vive fuera del repo, en `~/ComfyUI/`). - **No todas las imagenes traen workflow ComfyUI**: Civitai recomprime muchas a JPEG (sin chunks tEXt) o son de A1111. La descarga funciona igual; la extraccion posterior degradara. Por eso el pipeline itera varios items. - `prefer_original=True` reescribe `/width=N/` a `/original=true/` para maximizar conservar la metadata; si la URL ya es original, la deja igual. - Valida que la respuesta no sea HTML (Cloudflare/login) ni < 1 KB: en esos casos devuelve `ok=False` y NO deja basura en disco. - Impura: HTTP GET + escritura en disco. Requiere internet.