← RETOUR À L'INDEX
/tutoriels/ comfyui / codex-et-comfyui-desktop-pour-illustrer-le-portfolio.md

Codex et ComfyUI Desktop pour illustrer le portfolio

Piloter en sécurité ComfyUI Desktop sous Windows depuis WSL2, puis générer avec Flux Schnell des couvertures WebP cohérentes pour le portfolio.

CAT · COMFYUI LECTURE · 19 min PUBLIÉ · 2026-04-12 MAJ · 2026-07-27

[TUTO-11] Codex et ComfyUI Desktop pour illustrer le portfolio

Temps estimé : 45 min

Résultat final : Codex, depuis le projet portfolio ouvert sous WSL2, envoie un workflow Flux Schnell à ComfyUI Desktop sous Windows. La couverture 16:9 est convertie en WebP, contrôlée visuellement et enregistrée sous public/tutos/covers/<slug>.webp. Claude Code peut exécuter la même procédure comme variante compatible.

Environnement de référence actuel :

  • hôte : Windows 11 ;
  • environnement de développement : WSL2, réseau mirrored ;
  • GPU : AMD Radeon RX 7900 XT, 20 Go de VRAM ;
  • génération : ComfyUI Desktop sous Windows, avec PyTorch/ROCm natif Windows ;
  • workflow principal opérationnel : Flux Schnell ;
  • variante : Flux Dev BF16, limitée par les 20 Go de VRAM et susceptible de décharger une partie du modèle en RAM ;
  • pilote des projets personnels : Codex ; Claude Code reste compatible, mais n’est plus le pilote unique.

La version exacte de ComfyUI Desktop, du pilote AMD, de PyTorch/ROCm et les mesures de VRAM sont à vérifier sur la machine. Ce tutoriel ne donne donc aucun temps de génération ni benchmark.

Objectif

Mettre en place ce flux :

Codex ou Claude Code (WSL2)
  → API locale ComfyUI (Windows)
  → workflow API Flux Schnell
  → PNG temporaire 16:9
  → conversion WebP sans métadonnées
  → public/tutos/covers/<slug>.webp

La connexion recommandée reste sur la boucle locale grâce au mode réseau WSL mirrored. Le pont netsh portproxy n’est documenté qu’en solution de repli pour une installation WSL en mode NAT.

Prérequis

  • ComfyUI Desktop installé sous Windows et lancé au moins une fois ;
  • workflow Flux Schnell fonctionnel manuellement dans ComfyUI ;
  • Codex ouvert sur le projet personnel portfolio sous WSL2 ;
  • Python 3 et curl disponibles dans WSL2 ;
  • accès au frontmatter de l’article pour récupérer son slug.

Vérifier WSL et les outils locaux :

wsl.exe --version
python3 --version
curl --version

La distribution utilisée et la version exacte de WSL sont à vérifier sur la machine. La configuration de référence actuelle contient :

# C:\Users\<utilisateur>\.wslconfig
[wsl2]
networkingMode=mirrored

Après une modification de .wslconfig, appliquer la configuration depuis PowerShell :

wsl.exe --shutdown

Cette commande arrête toutes les distributions WSL et leurs processus en cours. Enregistrer le travail avant de l’exécuter.

Étape 1 : Vérifier le workflow et les modèles Flux

Le dépôt confirme les noms suivants :

UsageNom présent dans le dépôtÉtat
Workflow principalflux1-schnell.safetensorsFlux Schnell opérationnel ; chemin et hash à vérifier sur la machine
VAEae.safetensorsutilisé par les workflows Flux
Encodeur CLIPclip_l.safetensorsutilisé par les workflows Flux
Encodeur T5 historiquet5xxl_fp16.safetensorsréférencé dans .claude/flux_dev.json
Modèle secondaireflux1-dev.safetensorsFlux Dev BF16, limité par la VRAM

La documentation ComfyUI actuelle propose t5xxl_fp8_e4m3fn.safetensors pour réduire l’empreinte mémoire de Flux Schnell. Le nom réellement sélectionné dans le workflow opérationnel n’est pas versionné dans ce dépôt : il est à vérifier sur la machine dans le nœud DualCLIPLoader.

Le dépôt mentionne encore models/unet/flux1-schnell.safetensors, alors que la documentation ComfyUI actuelle utilise models/diffusion_models/flux1-schnell.safetensors. Ne déplace pas un modèle qui fonctionne : le chemin réel de ComfyUI Desktop et les dossiers qu’il indexe sont à vérifier sur la machine.

Vérifier les fichiers depuis le dossier de modèles réellement configuré dans ComfyUI Desktop :

sha256sum \
  models/diffusion_models/flux1-schnell.safetensors \
  models/vae/ae.safetensors \
  models/text_encoders/clip_l.safetensors

Adapte uniquement les chemins confirmés par l’installation. Les valeurs SHA256 attendues ne sont pas renseignées dans le dépôt : compare-les aux fiches des fichiers téléchargés depuis les sources officielles avant toute nouvelle utilisation.

⚠️ Sécurité : un fichier de modèle non vérifié est une dépendance non fiable. Télécharge uniquement depuis la source officielle, préfère .safetensors et n’exécute jamais un script fourni avec un modèle sans l’avoir audité.

Dans ComfyUI, charger le workflow Flux Schnell et produire une image manuellement avant de poursuivre. Contrôler dans les nœuds de chargement :

  • le nom exact du modèle de diffusion ;
  • le VAE ;
  • les deux encodeurs de texte ;
  • le nombre d’étapes enregistré dans le workflow ;
  • l’absence de custom node ou d’appel cloud non nécessaire.

Flux Dev BF16 reste une variante : ne l’utilise pas comme workflow par défaut sur cette RX 7900 XT si son exécution provoque un manque de VRAM ou un offload important. Le comportement exact est à vérifier sur la machine.

Étape 2 : Connecter WSL2 à ComfyUI sans exposer l’API

Avec networkingMode=mirrored, WSL2 peut joindre un service Windows limité à la boucle locale. Laisse ComfyUI Desktop sur son adresse locale, puis teste d’abord le port de référence :

export COMFYUI_URL="http://127.0.0.1:8000"
curl --fail --silent "$COMFYUI_URL/system_stats" \
  | python3 -m json.tool \
  | head -20

⚠️ Sécurité : /system_stats expose des informations sur le système, les périphériques et la VRAM. Le tutoriel ne configure aucune authentification pour l’API locale ComfyUI ; n’exécute ce test que via 127.0.0.1 ou une liaison explicitement filtrée.

Si le test échoue, vérifier dans ComfyUI Desktop le port réellement affiché dans les journaux. Le port 8000 est celui documenté dans ce dépôt, mais il est à vérifier sur la machine.

Ne configure pas cette option en mode réseau mirrored :

--listen 0.0.0.0

⚠️ Sécurité : --listen 0.0.0.0 fait écouter ComfyUI sur toutes les interfaces IPv4. Selon le pare-feu et le profil réseau Windows, l’interface, l’API, la file d’attente, les informations système et les fichiers servis par /view peuvent alors devenir accessibles depuis le réseau local sans authentification.

Si l’option est déjà présente dans Settings → Server Config → Extra Arguments, la retirer, redémarrer ComfyUI Desktop, puis refaire le test sur 127.0.0.1.

Étape 3 : Utiliser netsh portproxy uniquement en repli NAT

Cette étape est inutile avec le mode mirrored. Elle ne s’applique que si WSL2 fonctionne en NAT et ne peut pas atteindre le service Windows via 127.0.0.1.

Depuis WSL2, relever l’adresse de Windows vue par WSL et l’adresse de la distribution :

WINDOWS_HOST_IP=$(ip route show default | awk '{print $3}')
WSL_IP=$(hostname -I | awk '{print $1}')
printf 'Windows=%s\nWSL=%s\n' "$WINDOWS_HOST_IP" "$WSL_IP"

Ouvrir ensuite PowerShell en tant qu’administrateur et recopier les deux valeurs :

$WindowsHostIp = "<adresse Windows affichée dans WSL>"
$WslIp = "<adresse WSL affichée dans WSL>"

netsh interface portproxy add v4tov4 `
  listenport=8188 `
  listenaddress=$WindowsHostIp `
  connectport=8000 `
  connectaddress=127.0.0.1 `
  protocol=tcp

⚠️ Sécurité : une règle portproxy est persistante et peut rester active après la fin du test. Ici, listenaddress doit être l’adresse virtuelle précise vue par WSL, jamais une adresse générique. Vérifie la règle après chaque changement d’adresse WSL.

Créer une règle de pare-feu limitée à cette paire d’adresses :

New-NetFirewallRule `
  -DisplayName "ComfyUI-WSL2-Bridge" `
  -Direction Inbound `
  -Action Allow `
  -Protocol TCP `
  -LocalAddress $WindowsHostIp `
  -LocalPort 8188 `
  -RemoteAddress $WslIp `
  -Profile Any

⚠️ Sécurité : n’autorise pas 8188 pour Any en adresse distante. Une règle limitée seulement au port, ou à tout le sous-réseau local, peut exposer l’API ComfyUI à d’autres machines. Le profil Any n’est acceptable ici que parce que LocalAddress et RemoteAddress sont tous deux restreints.

Cette variante souvent copiée sur Internet est à proscrire :

netsh interface portproxy add v4tov4 `
  listenport=8188 `
  listenaddress=0.0.0.0 `
  connectport=8000 `
  connectaddress=127.0.0.1

⚠️ Sécurité : listenaddress=0.0.0.0 accepte les connexions sur toutes les interfaces IPv4 de Windows. Une règle de pare-feu absente, trop large, désactivée ou associée au mauvais profil suffit alors à rendre l’API accessible sur le LAN, sans authentification.

Auditer le pont et le pare-feu :

netsh interface portproxy show v4tov4
Get-NetTCPConnection -State Listen -LocalPort 8188
Get-NetFirewallRule -DisplayName "ComfyUI-WSL2-Bridge" |
  Get-NetFirewallAddressFilter

Depuis WSL2, utiliser uniquement l’adresse relevée :

export COMFYUI_URL="http://${WINDOWS_HOST_IP}:8188"
curl --fail --silent "$COMFYUI_URL/system_stats" \
  | python3 -m json.tool \
  | head -20

⚠️ Sécurité : ce test confirme aussi que l’endpoint non authentifié est joignable. Si une autre machine du LAN peut obtenir une réponse, supprimer immédiatement le pont et la règle de pare-feu, puis corriger le filtrage avant de continuer.

Supprimer le repli dès qu’il n’est plus nécessaire :

netsh interface portproxy delete v4tov4 `
  listenport=8188 `
  listenaddress=$WindowsHostIp `
  protocol=tcp

Remove-NetFirewallRule -DisplayName "ComfyUI-WSL2-Bridge"

Étape 4 : Exporter le workflow au format API

ComfyUI distingue le workflow destiné à l’interface et le workflow destiné à l’API.

  1. Ouvrir http://127.0.0.1:8000 sous Windows.
  2. Charger le workflow Flux Schnell déjà validé.
  3. Si l’option n’apparaît pas, activer les options de développement dans les paramètres.
  4. Utiliser File → Export (API).
  5. Enregistrer le fichier dans le projet portfolio sous workflows/flux-schnell-api.json.

⚠️ Sécurité : relis le JSON avant de le versionner. Des custom nodes peuvent y enregistrer un prompt privé, un chemin local, une URL distante, un identifiant ou une clé. Le format API n’est pas une garantie d’absence de secrets.

Valider le JSON et afficher les noms de modèles réellement exportés :

python3 -m json.tool workflows/flux-schnell-api.json > /dev/null
rg -n '"(unet_name|vae_name|clip_name1|clip_name2|ckpt_name)"' \
  workflows/flux-schnell-api.json

Afficher les IDs, types et entrées des nœuds :

python3 - workflows/flux-schnell-api.json <<'PY'
import json
import sys

workflow = json.load(open(sys.argv[1], encoding="utf-8"))
for node_id, node in workflow.items():
    inputs = ", ".join(node.get("inputs", {}).keys())
    print(f"{node_id:>8}  {node.get('class_type', '?'):<28}  {inputs}")
PY

Les IDs de .claude/flux_dev.json (56:47, 56:48, etc.) appartiennent au workflow Flux Dev historique. Ne les recopie pas dans Flux Schnell. Le JSON exporté sur la machine est la source de vérité.

Étape 5 : Ajouter un lanceur de couverture au portfolio

Le script existant scripts/generate-covers.mjs produit les couvertures SVG de repli. Ne l’écrase pas : le lanceur ComfyUI porte un autre nom et produit le PNG temporaire destiné au WebP prioritaire.

Créer scripts/comfyui-cover.py dans le projet portfolio avec ce contenu :

#!/usr/bin/env python3
import argparse
import json
import os
from pathlib import Path
import random
import re
import time
from urllib import error, parse, request


def api_json(base_url, path, method="GET", payload=None):
    data = None if payload is None else json.dumps(payload).encode("utf-8")
    headers = {"Content-Type": "application/json"} if data else {}
    req = request.Request(
        f"{base_url.rstrip('/')}{path}",
        data=data,
        headers=headers,
        method=method,
    )
    try:
        with request.urlopen(req, timeout=30) as response:
            return json.load(response)
    except error.HTTPError as exc:
        body = exc.read().decode("utf-8", errors="replace")
        raise SystemExit(f"ComfyUI HTTP {exc.code}: {body}") from exc


def choose_node(workflow, explicit_id, label, predicate):
    if explicit_id:
        if explicit_id not in workflow:
            raise SystemExit(f"{label}: nœud {explicit_id!r} absent du workflow")
        return explicit_id, workflow[explicit_id]

    matches = [
        (node_id, node)
        for node_id, node in workflow.items()
        if predicate(node)
    ]
    if len(matches) != 1:
        ids = ", ".join(node_id for node_id, _ in matches) or "aucun"
        raise SystemExit(
            f"{label}: détection ambiguë ({ids}). "
            f"Indique l'ID avec l'option correspondante."
        )
    return matches[0]


parser = argparse.ArgumentParser()
parser.add_argument("--workflow", required=True)
parser.add_argument("--slug", required=True)
parser.add_argument("--prompt", required=True)
parser.add_argument("--output", required=True)
parser.add_argument(
    "--base-url",
    default=os.environ.get("COMFYUI_URL", "http://127.0.0.1:8000"),
)
parser.add_argument("--width", type=int, default=1280)
parser.add_argument("--height", type=int, default=720)
parser.add_argument("--seed", type=int)
parser.add_argument("--prompt-node")
parser.add_argument("--size-node")
parser.add_argument("--seed-node")
parser.add_argument("--save-node")
parser.add_argument("--timeout", type=int, default=300)
args = parser.parse_args()

if not re.fullmatch(r"[a-z0-9]+(?:-[a-z0-9]+)*", args.slug):
    raise SystemExit("Slug invalide : utiliser uniquement a-z, 0-9 et des tirets")
if args.width * 9 != args.height * 16:
    raise SystemExit("La couverture doit respecter exactement le ratio 16:9")

workflow = json.loads(Path(args.workflow).read_text(encoding="utf-8"))

prompt_id, prompt_node = choose_node(
    workflow,
    args.prompt_node,
    "Prompt",
    lambda node: (
        node.get("class_type") == "CLIPTextEncode"
        and "text" in node.get("inputs", {})
    ),
)
size_id, size_node = choose_node(
    workflow,
    args.size_node,
    "Dimensions",
    lambda node: (
        node.get("class_type") in {"EmptySD3LatentImage", "EmptyLatentImage"}
        and {"width", "height"} <= node.get("inputs", {}).keys()
    ),
)
seed_id, seed_node = choose_node(
    workflow,
    args.seed_node,
    "Seed",
    lambda node: (
        "seed" in node.get("inputs", {})
        or "noise_seed" in node.get("inputs", {})
    ),
)
save_id, save_node = choose_node(
    workflow,
    args.save_node,
    "Sortie",
    lambda node: node.get("class_type") == "SaveImage",
)

seed = args.seed if args.seed is not None else random.SystemRandom().randrange(2**63)
prompt_node["inputs"]["text"] = args.prompt
size_node["inputs"]["width"] = args.width
size_node["inputs"]["height"] = args.height

for node in workflow.values():
    if node.get("class_type") == "ModelSamplingFlux":
        inputs = node.get("inputs", {})
        if "width" in inputs and "height" in inputs:
            inputs["width"] = args.width
            inputs["height"] = args.height

seed_input = "seed" if "seed" in seed_node["inputs"] else "noise_seed"
seed_node["inputs"][seed_input] = seed
save_node["inputs"]["filename_prefix"] = f"portfolio/{args.slug}"

queued = api_json(
    args.base_url,
    "/prompt",
    method="POST",
    payload={"prompt": workflow, "client_id": f"portfolio-{args.slug}"},
)
job_id = queued.get("prompt_id")
if not job_id:
    raise SystemExit(f"Workflow refusé : {json.dumps(queued, ensure_ascii=False)}")

deadline = time.monotonic() + args.timeout
image_info = None
while time.monotonic() < deadline:
    history = api_json(args.base_url, f"/history/{job_id}")
    entry = history.get(job_id)
    if entry:
        status = entry.get("status", {})
        if status.get("status_str") == "error":
            raise SystemExit(f"Échec ComfyUI : {json.dumps(status, ensure_ascii=False)}")
        images = entry.get("outputs", {}).get(save_id, {}).get("images", [])
        if images:
            image_info = images[0]
            break
    time.sleep(2)

if image_info is None:
    raise SystemExit(f"Timeout après {args.timeout}s pour le prompt {job_id}")

query = parse.urlencode(
    {
        "filename": image_info["filename"],
        "subfolder": image_info.get("subfolder", ""),
        "type": image_info.get("type", "output"),
    }
)
output = Path(args.output)
output.parent.mkdir(parents=True, exist_ok=True)
with request.urlopen(
    f"{args.base_url.rstrip('/')}/view?{query}",
    timeout=30,
) as response:
    output.write_bytes(response.read())

print(f"Image : {output}")
print(f"Seed : {seed}")
print(
    f"Nœuds : prompt={prompt_id}, dimensions={size_id}, "
    f"seed={seed_id}, sortie={save_id}"
)

Le script détecte les nœuds simples par leur class_type. Si plusieurs nœuds correspondent, il s’arrête au lieu de modifier le mauvais. Relance-le alors avec --prompt-node, --size-node, --seed-node ou --save-node, en utilisant les IDs affichés à l’étape précédente.

L’automatisation ne reconstruit pas le workflow et ne duplique pas les noms des modèles : elle modifie uniquement le prompt, les dimensions, la seed et le préfixe de sortie du workflow API exporté.

Étape 6 : Appliquer la convention de prompt Macchiato bleu

Pour garder une série cohérente, conserve le même workflow Flux Schnell et commence chaque prompt par le même contrat visuel :

Editorial technical illustration for a software engineering portfolio,
dark Catppuccin Macchiato color palette,
deep backgrounds #24273a, #1e2030 and #181926,
dominant blue #8aadf4 with sapphire #7dc4e4 and sky #91d7e3 accents,
clean geometric composition, subtle blue glow, restrained fine grain,
high contrast, one clear focal subject, generous negative space,
no readable text, no letters, no numbers, no logo, no watermark,
16:9 landscape composition.

Ajoute ensuite seulement :

  1. le sujet concret de l’article ;
  2. deux ou trois éléments visuels maximum ;
  3. la relation entre ces éléments ;
  4. la zone à laisser calme pour le recadrage responsive.

Exemple pour ce tutoriel :

Editorial technical illustration for a software engineering portfolio,
dark Catppuccin Macchiato color palette,
deep backgrounds #24273a, #1e2030 and #181926,
dominant blue #8aadf4 with sapphire #7dc4e4 and sky #91d7e3 accents,
clean geometric composition, subtle blue glow, restrained fine grain,
high contrast, one clear focal subject, generous negative space,
no readable text, no letters, no numbers, no logo, no watermark,
16:9 landscape composition.
An abstract AMD GPU on the Windows side connected through a narrow local bridge
to a workflow graph inside a Linux terminal, flowing into a single image frame,
calm space on the left, precise editorial vector-like rendering.

Flux Schnell n’utilise pas nécessairement un negative prompt dans le workflow officiel. Les exclusions importantes sont donc formulées directement dans le prompt principal. Leur respect reste à contrôler visuellement.

Étape 7 : Générer et nommer une couverture 16:9 depuis Codex

Depuis la racine du projet portfolio, récupérer le slug du frontmatter. Adapter ARTICLE au fichier concerné :

ARTICLE="src/content/tutorials/TUTO-11-comfyui-wsl2-generation-images.md"
SLUG=$(sed -n 's/^slug::space:*//p' "$ARTICLE" | head -1 | tr -d "\"'")

if ! [[ "$SLUG" =~ ^[a-z0-9]+(-[a-z0-9]+)*$ ]]; then
  printf 'Slug invalide : %s\n' "$SLUG" >&2
  exit 1
fi

printf 'Couverture cible : public/tutos/covers/%s.webp\n' "$SLUG"

Dans Codex, demander :

Dans le projet portfolio, lis le frontmatter de l'article ciblé et
workflows/flux-schnell-api.json. Prépare un prompt conforme au contrat visuel
Macchiato bleu du TUTO-11. Exécute scripts/comfyui-cover.py en 1280x720 avec
le slug exact de l'article. Ne modifie ni le workflow source ni un autre fichier.
Arrête-toi et montre les IDs candidats si la détection d'un nœud est ambiguë.

Codex peut alors exécuter la commande suivante avec le prompt validé :

export COMFYUI_URL="http://127.0.0.1:8000"

PROMPT=$(cat <<'PROMPT_EOF'
Editorial technical illustration for a software engineering portfolio,
dark Catppuccin Macchiato color palette, deep background #24273a,
dominant blue #8aadf4 with sapphire #7dc4e4 and sky #91d7e3 accents,
clean geometric composition, subtle blue glow, restrained fine grain,
no readable text, no logo, no watermark, 16:9 landscape composition.
An abstract AMD GPU connected through a narrow local bridge to a workflow graph,
flowing into a single image frame, calm space on the left.
PROMPT_EOF
)

python3 scripts/comfyui-cover.py \
  --workflow workflows/flux-schnell-api.json \
  --slug "$SLUG" \
  --prompt "$PROMPT" \
  --width 1280 \
  --height 720 \
  --output "public/tutos/covers/${SLUG}.png"

⚠️ Sécurité : cette commande appelle /prompt, /history/<prompt_id> et /view, sans authentification dans cette configuration locale. N’utilise jamais une URL ComfyUI provenant du LAN, d’Internet ou d’un tiers sans transport sécurisé, contrôle d’accès et authentification explicitement vérifiés.

La seed affichée par le script doit être conservée dans les notes de travail si l’image doit pouvoir être régénérée. En cas de manque de VRAM avec Flux Schnell, tester 1024x576, toujours en 16:9. La nécessité de cette réduction est à vérifier sur la machine.

Ne bascule pas automatiquement vers Flux Dev BF16 : il est documenté comme variante limitée par la VRAM, pas comme solution de repli plus légère.

Étape 8 : Utiliser la même automatisation depuis Claude Code

Claude Code reste compatible : il doit exécuter le même script et le même workflow API, sans maintenir un second payload ComfyUI.

L’ancien skill ~/.claude/skills/comfyui-image-gen peut être conservé comme variante historique, à condition de :

  • remplacer l’IP fixe 172.24.16.1 par COMFYUI_URL ;
  • supprimer le payload Flux Dev et ses IDs codés en dur ;
  • appeler scripts/comfyui-cover.py avec le slug et le prompt ;
  • conserver Codex comme procédure principale pour les projets personnels.

Prompt compatible Claude Code :

Utilise scripts/comfyui-cover.py et workflows/flux-schnell-api.json.
Génère une couverture 16:9 pour cet article avec son slug exact et la convention
Macchiato bleu du TUTO-11. N'expose pas ComfyUI et n'invente aucun ID de nœud.

Étape 9 : Convertir en WebP et supprimer les métadonnées

Installer l’encodeur WebP dans WSL2 si nécessaire :

sudo apt update
sudo apt install -y webp libimage-exiftool-perl

Convertir le PNG temporaire. cwebp ne copie aucune métadonnée par défaut ; -metadata none rend cette intention explicite :

PNG="public/tutos/covers/${SLUG}.png"
WEBP="public/tutos/covers/${SLUG}.webp"

cwebp \
  -preset picture \
  -q 82 \
  -m 6 \
  -mt \
  -metadata none \
  "$PNG" \
  -o "$WEBP"

⚠️ Sécurité : les PNG produits par ComfyUI peuvent contenir le prompt, le workflow, des chemins ou des informations sur l’outil. Ne publie pas le PNG intermédiaire. Publie uniquement le WebP réencodé avec -metadata none, après contrôle.

La valeur 82 est un réglage éditorial de départ, pas un benchmark. Comparer visuellement le WebP au PNG et ajuster si des aplats, lignes ou dégradés deviennent visibles.

Vérifier le nom, le type et les métadonnées potentiellement sensibles :

test -s "$WEBP"
file "$WEBP"

if exiftool -G1 -a -s "$WEBP" \
  | grep -Eai 'EXIF|XMP|GPS|Comment|Author|Software|Prompt|Workflow|Parameters'; then
  echo "Métadonnée potentiellement sensible détectée" >&2
  exit 1
else
  echo "Aucune métadonnée sensible détectée"
fi

file et ExifTool affichent toujours des informations techniques comme le nom, la taille ou les dimensions du fichier. Le contrôle porte sur les champs publiables susceptibles de révéler le prompt, le workflow, un chemin, un logiciel, une identité ou une géolocalisation.

Étape 10 : Vérifier visuellement et valider la destination

Ouvrir le WebP depuis WSL2 :

explorer.exe "$(wslpath -w "$WEBP")"

Contrôler manuellement :

  • ratio 16:9 et cadrage lisible sur grand et petit écran ;
  • sujet principal immédiatement compréhensible ;
  • palette dominée par #8aadf4, #7dc4e4 et #91d7e3 sur fonds Macchiato sombres ;
  • absence de texte illisible, pseudo-logo, watermark, visage ou donnée non souhaitée ;
  • absence d’artefacts gênants ;
  • cohérence avec les autres couvertures du portfolio.

La destination finale doit être exactement :

public/tutos/covers/<slug>.webp

Le portfolio cherche actuellement les couvertures dans l’ordre webp, png, puis svg. Le WebP portant le slug exact devient donc automatiquement la couverture prioritaire.

Vérifier la présence du fichier :

test -f "public/tutos/covers/${SLUG}.webp"
git status --short -- "public/tutos/covers/${SLUG}.webp"

Ne supprime le PNG intermédiaire qu’après validation visuelle et confirmation que le WebP est la seule version à publier.

Dépannage

curl: (7) Failed to connect en mode mirrored

grep -n "networkingMode" /mnt/c/Users/*/.wslconfig 2>/dev/null
curl -v http://127.0.0.1:8000/system_stats

⚠️ Sécurité : avec curl -v, vérifie que la destination reste 127.0.0.1. Ne remplace pas temporairement l’adresse par l’IP LAN de Windows et n’ajoute pas --listen 0.0.0.0 pour contourner le diagnostic.

Vérifier ensuite que ComfyUI Desktop est démarré, que son port correspond à celui du tuto, puis exécuter wsl.exe --shutdown si .wslconfig vient d’être modifié.

Le repli portproxy ne répond plus

Les adresses WSL peuvent changer après un redémarrage :

netsh interface portproxy show v4tov4
Get-NetFirewallRule -DisplayName "ComfyUI-WSL2-Bridge" |
  Get-NetFirewallAddressFilter
Get-Service iphlpsvc

Supprimer l’ancienne règle et la recréer avec les adresses exactes. Ne l’élargis pas à 0.0.0.0 pour éviter cette maintenance.

node_errors dans la réponse de /prompt

Le workflow, un nom de modèle ou un ID de nœud ne correspond pas à l’installation :

python3 -m json.tool workflows/flux-schnell-api.json > /dev/null
rg -n '"class_type"|"unet_name"|"vae_name"|"clip_name' \
  workflows/flux-schnell-api.json

Réexporter le workflow Flux Schnell fonctionnel depuis la même instance ComfyUI. Ne remplace pas ses IDs par ceux du workflow Flux Dev historique.

La détection du script est ambiguë

Afficher les nœuds comme à l’étape 4, identifier les nœuds effectivement reliés à SaveImage, puis passer leurs IDs :

python3 scripts/comfyui-cover.py \
  --workflow workflows/flux-schnell-api.json \
  --slug "$SLUG" \
  --prompt "$PROMPT" \
  --output "public/tutos/covers/${SLUG}.png" \
  --prompt-node "<ID>" \
  --size-node "<ID>" \
  --seed-node "<ID>" \
  --save-node "<ID>"

⚠️ Sécurité : les IDs ne sont pas un mécanisme de confiance. Relis le class_type et les connexions du nœud avant de l’autoriser ; un custom node peut appeler un service externe ou écrire ailleurs sur le disque.

Manque de VRAM

  • confirmer que le workflow chargé est Flux Schnell ;
  • conserver un seul batch ;
  • essayer 1024x576 ;
  • vérifier le T5 réellement sélectionné dans DualCLIPLoader ;
  • fermer les autres charges GPU ;
  • consulter /system_stats et les journaux ComfyUI ;
  • ne tester Flux Dev BF16 qu’en connaissance de sa limite VRAM.

Les réglages efficaces, la quantité d’offload et la version PyTorch/ROCm sont à vérifier sur la machine.

Métadonnées détectées dans le WebP

Réencoder depuis le PNG original avec cwebp -metadata none, puis relancer ExifTool. Ne publie pas le fichier tant qu’un champ contient un prompt, un workflow, un chemin local, un nom d’utilisateur, une géolocalisation ou une information d’outil inutile.

Références

VR · 2026-04-12 · vraffin.dev FIN DU DOCUMENT