feat(ml): cosecha Civitai → skills candidatas (search/fetch/extract + harvest oneshot)
Cierra la 3ª pieza del sistema comfyui-skill: cosechar de Civitai imágenes con su workflow+receta embebidos para clonar su calidad y alimentar la librería de skills. - comfyui_search_civitai_images: GET /api/v1/images; resuelve query->versión de modelo (el endpoint no admite query textual, da HTTP 500); token de pass; reintenta 503. - comfyui_fetch_civitai_image: descarga el PNG original (conserva workflow embebido), SEGREGA NSFW a <dest>/nsfw/, validación no-HTML, nombre único por UUID. - comfyui_extract_recipe_from_png: import_workflow_png + read_png_metadata + fallback flux (CLIPTextEncode/UNETLoader) -> receta candidata (source='civitai', score_n=0). - comfyui_harvest_civitai_skill_oneshot (pipeline): search->fetch->extract->save_skill; itera items, 2º pase al feed global, NO baja modelos a ciegas (missing_models). Hallazgo: la API de Civitai ya no expone meta (null); la receta sale del workflow ComfyUI embebido en el PNG. Política: NSFW permitido pero SIEMPRE segregado. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
---
|
||||
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 (<dest_dir>/<nsfw_subdir>/). 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 <dest_dir>/<nsfw_subdir>/ 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/<uuid>.png
|
||||
|
||||
# Imagen NSFW -> segregada a la subcarpeta nsfw/.
|
||||
# comfyui_fetch_civitai_image(url, dest_dir="...", nsfw=True) -> .../nsfw/<uuid>.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 `<dest_dir>/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.
|
||||
Reference in New Issue
Block a user