From 30710385cd7a0a5d64d370406933352de1ca5443 Mon Sep 17 00:00:00 2001 From: Luis Fernando Martinelli Date: Sat, 3 Jan 2026 18:34:23 -0300 Subject: [PATCH] Delete ComfyUI-WaveSpeed directory --- ComfyUI-WaveSpeed/LICENSE | 21 -- ComfyUI-WaveSpeed/README.md | 20 -- ComfyUI-WaveSpeed/__init__.py | 18 -- ComfyUI-WaveSpeed/nodes/node_apikey_loader.py | 26 --- .../nodes/node_image_uploader.py | 68 ------- ComfyUI-WaveSpeed/nodes/node_wavespeed_api.py | 179 ------------------ ComfyUI-WaveSpeed/nodes/utils.py | 32 ---- ComfyUI-WaveSpeed/requirements.txt | 0 8 files changed, 364 deletions(-) delete mode 100644 ComfyUI-WaveSpeed/LICENSE delete mode 100644 ComfyUI-WaveSpeed/README.md delete mode 100644 ComfyUI-WaveSpeed/__init__.py delete mode 100644 ComfyUI-WaveSpeed/nodes/node_apikey_loader.py delete mode 100644 ComfyUI-WaveSpeed/nodes/node_image_uploader.py delete mode 100644 ComfyUI-WaveSpeed/nodes/node_wavespeed_api.py delete mode 100644 ComfyUI-WaveSpeed/nodes/utils.py delete mode 100644 ComfyUI-WaveSpeed/requirements.txt diff --git a/ComfyUI-WaveSpeed/LICENSE b/ComfyUI-WaveSpeed/LICENSE deleted file mode 100644 index 6da3b20..0000000 --- a/ComfyUI-WaveSpeed/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2026 fmartinellidev - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/ComfyUI-WaveSpeed/README.md b/ComfyUI-WaveSpeed/README.md deleted file mode 100644 index 40023fe..0000000 --- a/ComfyUI-WaveSpeed/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# 🌊 ComfyUI-WaveSpeed - -[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) -[![ComfyUI](https://img.shields.io/badge/ComfyUI-Node-blueviolet)](https://github.com/comfyanonymous/ComfyUI) - -NĂł **genĂ©rico e flexĂ­vel** para integrar **qualquer modelo do [WaveSpeed](https://wavespeed.ai)** no ComfyUI — incluindo Nano Banana Pro, Wan 2.1, Flux, Kling, Veo e mais. - -✅ Upload direto no WaveSpeed CDN -✅ Barra de progresso no terminal (igual ao KSampler) -✅ Suporte a imagem e vĂ­deo -✅ `endpoint_url` customizĂĄvel — basta colar a URL do playground! - ---- - -## 📩 Instalação - -No seu terminal (dentro da pasta `ComfyUI/custom_nodes`): - -```bash -git clone https://github.com/fmartinellidev/ComfyUI-WaveSpeed.git \ No newline at end of file diff --git a/ComfyUI-WaveSpeed/__init__.py b/ComfyUI-WaveSpeed/__init__.py deleted file mode 100644 index 1120bb8..0000000 --- a/ComfyUI-WaveSpeed/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# __init__.py — wavespeed -from .node_wavespeed_api import WaveSpeedAPINode -from .node_apikey_loader import WaveSpeedAPIKeyLoader -from .node_image_uploader import WaveSpeedImageUploader - -NODE_CLASS_MAPPINGS = { - "WaveSpeedAPI": WaveSpeedAPINode, # ← ID genĂ©rico - "WaveSpeedAPIKeyLoader": WaveSpeedAPIKeyLoader, - "WaveSpeedImageUploader": WaveSpeedImageUploader, -} - -NODE_DISPLAY_NAME_MAPPINGS = { - "WaveSpeedAPI": "WaveSpeed API (Generic)", # ← nome claro e reutilizĂĄvel - "WaveSpeedAPIKeyLoader": "WaveSpeed API Key Loader", - "WaveSpeedImageUploader": "WaveSpeed Image Uploader (Official)", -} - -__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS"] \ No newline at end of file diff --git a/ComfyUI-WaveSpeed/nodes/node_apikey_loader.py b/ComfyUI-WaveSpeed/nodes/node_apikey_loader.py deleted file mode 100644 index c52bb6c..0000000 --- a/ComfyUI-WaveSpeed/nodes/node_apikey_loader.py +++ /dev/null @@ -1,26 +0,0 @@ -# node_apikey_loader.py - -class WaveSpeedAPIKeyLoader: - @classmethod - def INPUT_TYPES(cls): - return { - "required": { - "api_key": ("STRING", {"multiline": False, "default": "", "placeholder": "ex: ws_sk_xxxxxxxx"}), - } - } - - RETURN_TYPES = ("STRING",) - RETURN_NAMES = ("api_key",) - FUNCTION = "load" - CATEGORY = "api node/utility/WaveSpeed" - DISPLAY_NAME = "WaveSpeed API Key Loader" - NODE_ID = "WaveSpeedAPIKeyLoader" - - def load(self, api_key): - key = api_key.strip() - if not key: - raise ValueError("API key nĂŁo pode estar vazia.") - #if not key.startswith("ws_sk_"): - # Apenas aviso — nĂŁo impede (algumas chaves podem nĂŁo seguir padrĂŁo) - #print("⚠ Aviso: sua chave nĂŁo começa com 'ws_sk_' — verifique se estĂĄ correta.") - return (key,) \ No newline at end of file diff --git a/ComfyUI-WaveSpeed/nodes/node_image_uploader.py b/ComfyUI-WaveSpeed/nodes/node_image_uploader.py deleted file mode 100644 index 9c7b8a6..0000000 --- a/ComfyUI-WaveSpeed/nodes/node_image_uploader.py +++ /dev/null @@ -1,68 +0,0 @@ -# node_image_uploader.py -import requests -import torch -import numpy as np -from PIL import Image -from io import BytesIO -import os - -class WaveSpeedImageUploader: - @classmethod - def INPUT_TYPES(cls): - return { - "required": { - "image": ("IMAGE",), - "api_key": ("STRING", { - "multiline": False, - "default": "", - "placeholder": "ou defina WAVESPEED_API_KEY" - }), - "output_format": (["png", "jpeg"], {"default": "png"}) - } - } - - RETURN_TYPES = ("STRING",) - RETURN_NAMES = ("image_url",) - FUNCTION = "upload" - CATEGORY = "api node/image/WaveSpeed" - DISPLAY_NAME = "WaveSpeed Image Uploader (Official)" - NODE_ID = "WaveSpeedImageUploader" - - def upload(self, image: torch.Tensor, api_key: str, output_format: str): - final_api_key = api_key.strip() or os.getenv("WAVESPEED_API_KEY", "").strip() - if not final_api_key: - raise ValueError("API key nĂŁo fornecida.") - - if image.ndim == 4: - image = image[0] - - img_np = (image.cpu().numpy() * 255).astype("uint8") - pil_img = Image.fromarray(img_np, mode="RGB") - - buffer = BytesIO() - if output_format == "png": - pil_img.save(buffer, format="PNG") - else: - pil_img.save(buffer, format="JPEG", quality=95) - buffer.seek(0) - - upload_url = "https://api.wavespeed.ai/api/v3/media/upload/binary" - headers = {"Authorization": f"Bearer {final_api_key}"} - files = {"file": (f"image.{output_format}", buffer, f"image/{output_format}")} - - try: - print("đŸ“€ Upload oficial no WaveSpeed...") - response = requests.post(upload_url, headers=headers, files=files, timeout=30) - response.raise_for_status() - data = response.json() - - # ✅ Extrai download_url de data e remove espaços - image_url = data.get("data", {}).get("download_url", "").strip() - if not image_url.startswith("http"): - raise ValueError(f"download_url nĂŁo encontrada. Resposta: {data}") - - print(f"✅ URL oficial do WaveSpeed: {image_url}") - return (image_url,) - - except Exception as e: - raise RuntimeError(f"Falha no upload: {e}") \ No newline at end of file diff --git a/ComfyUI-WaveSpeed/nodes/node_wavespeed_api.py b/ComfyUI-WaveSpeed/nodes/node_wavespeed_api.py deleted file mode 100644 index fd6ee34..0000000 --- a/ComfyUI-WaveSpeed/nodes/node_wavespeed_api.py +++ /dev/null @@ -1,179 +0,0 @@ -# node_wavespeed_api.py -import requests -import time -import os -import sys -import torch -import numpy as np -from PIL import Image -from io import BytesIO - -def _print_progress(percent, status="processing", width=40): - filled = int(width * percent / 100) - bar = "█" * filled + "░" * (width - filled) - sys.stdout.write(f"\r⏳ [{bar}] {int(percent)}% | {status}") - sys.stdout.flush() - -def _clear_progress(): - sys.stdout.write("\r" + " " * 80 + "\r") - sys.stdout.flush() - -class WaveSpeedAPINode: - @classmethod - def INPUT_TYPES(cls): - return { - "required": { - "prompt": ("STRING", {"multiline": True, "default": "make her smile, studio lighting"}), - "endpoint_url": ("STRING", { - "multiline": False, - "default": "https://api.wavespeed.ai/api/v3/google/nano-banana-pro/edit" - }), - "poll_interval": ("INT", {"default": 3, "min": 1, "max": 10}), - }, - "optional": { - "image_url": ("STRING", {"multiline": False, "default": ""}), - "image": ("IMAGE",), - "api_key": ("STRING", {"multiline": False, "default": ""}), - "extra_payload": ("STRING", { - "multiline": True, - "default": "{}", - "placeholder": "JSON adicional (ex: {\"resolution\": \"2k\"})" - }), - } - } - - RETURN_TYPES = ("IMAGE", "STRING", "STRING") - RETURN_NAMES = ("image", "request_id", "raw_response") - FUNCTION = "generate" - NODE_ID = "WaveSpeedAPI" - DISPLAY_NAME = "WaveSpeed API (Generic)" - CATEGORY = "api node/image/WaveSpeed" - - def generate(self, prompt, endpoint_url, poll_interval, image_url="", image=None, api_key="", extra_payload="{}"): - # 1. Resolve API key - final_api_key = api_key.strip() or os.getenv("WAVESPEED_API_KEY", "").strip() - if not final_api_key: - raise ValueError("API key nĂŁo fornecida.") - - # 2. Resolve imagem - if image is not None: - print("đŸ“€ Upload da imagem no WaveSpeed CDN...") - image_url = self._upload_to_wavespeed(image, final_api_key) - print(f"✅ URL oficial: {image_url}") - elif not image_url.strip(): - # ❗ Alguns endpoints nĂŁo exigem imagem (ex: text-to-video) - print("⚠ Nenhuma imagem fornecida — verifique se o endpoint a suporta.") - - # 3. Monta payload base - payload = {"prompt": prompt} - if image_url: - payload["images"] = [image_url] - - # 4. Adiciona extra_payload (JSON seguro) - import json - try: - extra = json.loads(extra_payload) if extra_payload.strip() else {} - payload.update(extra) - except Exception as e: - raise ValueError(f"extra_payload invĂĄlido (deve ser JSON): {e}") - - # 5. Headers - headers = { - "Authorization": f"Bearer {final_api_key}", - "Content-Type": "application/json" - } - - # 6. POST - print(f"đŸ“€ Enviando para: {endpoint_url}") - response = requests.post(endpoint_url, json=payload, headers=headers, timeout=30) - response.raise_for_status() - resp_json = response.json() - raw_response_str = json.dumps(resp_json, indent=2) - - data = resp_json.get("data", {}) - request_id = data.get("id") - get_url = data.get("urls", {}).get("get", "").strip() - - if not request_id or not get_url: - raise RuntimeError(f"Resposta invĂĄlida. Campos ausentes: id={bool(request_id)}, get_url={bool(get_url)}") - - print(f"\n🚀 Job iniciado: {request_id}") - - # 7. Polling com progresso - current_percent = 0 - ESTIMATED_TOTAL_SECONDS = 20 # adaptĂĄvel - steps = max(1, int(ESTIMATED_TOTAL_SECONDS / poll_interval)) - increment = 99.0 / steps - - try: - while True: - time.sleep(poll_interval) - result = requests.get(get_url, headers=headers, timeout=30) - result.raise_for_status() - poll_data = result.json() - poll_result = poll_data.get("data", {}) - status = poll_result.get("status") - - if status == "completed": - _print_progress(100.0, "done") - print() - outputs = poll_result.get("outputs", []) - if not outputs: - _clear_progress() - raise RuntimeError("Nenhum output retornado.") - # Suporta image ou video - output_url = outputs[0] - break - - elif status == "failed": - _clear_progress() - error = poll_result.get("error", "Erro desconhecido") - raise RuntimeError(f"Falha: {error}") - - elif status in ("pending", "processing", "created"): - current_percent = min(99, current_percent + increment) - _print_progress(current_percent, status) - - else: - _clear_progress() - raise RuntimeError(f"Status inesperado: {status}") - - except Exception as e: - _clear_progress() - raise - - # 8. Download (suporte a imagem apenas — para vĂ­deo, retornar URL) - if output_url.lower().endswith(('.png', '.jpg', '.jpeg')): - print("đŸ“„ Baixando imagem...") - img_response = requests.get(output_url, timeout=30) - img_response.raise_for_status() - pil_img = Image.open(BytesIO(img_response.content)).convert("RGB") - image_np = np.array(pil_img).astype(np.float32) / 255.0 - image_tensor = torch.from_numpy(image_np)[None,] - return (image_tensor, request_id, raw_response_str) - else: - # Para vĂ­deos, retornar placeholder preto + URL no raw_response - print(f"🎬 SaĂ­da Ă© um vĂ­deo: {output_url}") - placeholder = torch.zeros((1, 512, 512, 3), dtype=torch.float32) - return (placeholder, request_id, raw_response_str + f"\n\nVIDEO_URL: {output_url}") - - def _upload_to_wavespeed(self, image_tensor: torch.Tensor, api_key: str, output_format="png"): - if image_tensor.ndim == 4: - image_tensor = image_tensor[0] - img_np = (image_tensor.cpu().numpy() * 255).astype("uint8") - pil_img = Image.fromarray(img_np, mode="RGB") - buffer = BytesIO() - pil_img.save(buffer, format=output_format.upper()) - buffer.seek(0) - - upload_url = "https://api.wavespeed.ai/api/v3/media/upload/binary" - headers = {"Authorization": f"Bearer {api_key}"} - files = {"file": (f"img.{output_format}", buffer, f"image/{output_format}")} - - response = requests.post(upload_url, headers=headers, files=files, timeout=30) - response.raise_for_status() - data = response.json() - image_url = data.get("data", {}).get("download_url", "").strip() - if not image_url.startswith("http"): - raise ValueError(f"download_url invĂĄlida: {data}") - return image_url \ No newline at end of file diff --git a/ComfyUI-WaveSpeed/nodes/utils.py b/ComfyUI-WaveSpeed/nodes/utils.py deleted file mode 100644 index f8fe5b3..0000000 --- a/ComfyUI-WaveSpeed/nodes/utils.py +++ /dev/null @@ -1,32 +0,0 @@ -# utils.py — versĂŁo final e funcional (comprovada por vocĂȘ) -import requests -from PIL import Image -from io import BytesIO - -def image_to_temp_url(image_tensor, output_format="jpeg"): - if image_tensor.ndim == 4: - image_tensor = image_tensor[0] - - img_np = (image_tensor.cpu().numpy() * 255).astype("uint8") - pil_img = Image.fromarray(img_np, mode="RGB") - - buffer = BytesIO() - pil_img.save(buffer, format=output_format.upper()) - buffer.seek(0) - - try: - # ✅ Funcionando: temp.sh + User-Agent vĂĄlido - response = requests.post( - "https://temp.sh/upload", - files={"file": (f"img.{output_format}", buffer, f"image/{output_format}")}, - headers={ - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" - } - ) - response.raise_for_status() - url = response.text.strip() - if not url.startswith("http"): - raise ValueError(f"URL invĂĄlida: {url}") - return url - except Exception as e: - raise RuntimeError(f"Falha ao fazer upload da imagem: {e}") \ No newline at end of file diff --git a/ComfyUI-WaveSpeed/requirements.txt b/ComfyUI-WaveSpeed/requirements.txt deleted file mode 100644 index e69de29..0000000