diff --git a/README.md b/README.md index 80e7cfc..6b4a445 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,52 @@ -# NeoChooser -A stylish Image Chooser node for ComfyUI with neon UI and audio notifications. +# ComfyUI Neo Image Chooser 👁️ + +**Neo Image Chooser** — это узел для выбора изображений в ComfyUI, выполненный в приятном UI. Он позволяет останавливать рабочий процесс для выбора лучших кадров. + +![Preview](https://raw.githubusercontent.com/ВАШ_НИК/ComfyUI-NeoChooser/main/preview.png) *(Совет: добавь сюда скриншот ноды, когда зальешь на Гитхаб)* + +## 🚀 Основные возможности + +- **Audio Notifications**: Нода позовет вас звуковым сигналом, когда генерация дойдет до этапа выбора. Больше не нужно гипнотизировать монитор в ожидании. +- **Smart Resizing**: + - **Compact Mode**: Сжимается в тонкую аккуратную панель, чтобы не занимать место. + - **Preview Mode**: Расширяется для удобного просмотра и выбора нескольких изображений сразу. +- **⚡ Quick Run**: После того как выбор сделан, кнопки управления заменяются на кнопку **RUN GENERATION**, позволяющую запустить новый цикл прямо из ноды, не возвращаясь к основному меню. +- **Batch Control**: Кнопка **SKIP** для пропуска текущей пачки или **CANCEL** для полной остановки очереди. + +## 🛠 Установка + +### Через ComfyUI-Manager (Рекомендуется) +1. Откройте **ComfyUI Manager**. +2. Нажмите кнопку **"Install via Git URL"**. +3. Вставьте ссылку на этот репозиторий: `https://github.com/ВАШ_НИК/ComfyUI-NeoChooser` +4. Нажмите **OK** и перезагрузите ComfyUI. + +### Вручную +1. Перейдите в папку `ComfyUI/custom_nodes/`. +2. Клонируйте репозиторий: + ```bash + git clone [https://github.com/ВАШ_НИК/ComfyUI-NeoChooser.git](https://github.com/ВАШ_НИК/ComfyUI-NeoChooser.git) +3. Перезагрузите ComfyUI. + +## 🎧 Настройка звуков +Чтобы добавить любые звуки уведомлений, перейдите в папку: custom_nodes/ComfyUI-NeoChooser/web/sounds/ +Положите туда свои файлы в формате .mp3. Обновите страницу в браузере. + +Бесплатные звуки можно скачать здесь: + +[Mixkit Notification Sounds](https://mixkit.co/free-sound-effects/notification/) (Очень чистые и современные звуки). + +[Freesound.org](https://freesound.org/) (Огромная база, ищите по тегу "ping" или "notification"). + +## 📖 Как пользоваться +1. Добавьте ноду через меню: NeoNodes -> NeoChooser. + +2. Подключите выход IMAGE от вашего KSampler или VAE Decode к входу ноды. + +3. Запустите генерацию. + +4. Когда процесс дойдет до NeoChooser, Генерация встанет на паузу. + +5. Кликните на изображения, которые хотите оставить. (Если изображение одно, оно выделится автоматом) + +6. Нажмите CONTINUE, чтобы отправить выбранные фото дальше по workflow, или SKIP, если хотите просто продолжить без выбора. \ No newline at end of file diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..0b29186 --- /dev/null +++ b/__init__.py @@ -0,0 +1,73 @@ +import server +from aiohttp import web +import time +import folder_paths +import os +import numpy as np +from PIL import Image +import comfy.model_management + +PAUSE_STATE = {} + +@server.PromptServer.instance.routes.post("/neo_chooser/reply") +async def api_reply(request): + data = await request.json() + PAUSE_STATE[str(data["node_id"])] = data + return web.json_response({"status": "ok"}) + +@server.PromptServer.instance.routes.get("/neo_chooser/sounds") +async def get_sounds(request): + sound_dir = os.path.join(os.path.dirname(__file__), "web", "sounds") + if not os.path.exists(sound_dir): + os.makedirs(sound_dir) + return web.json_response([]) + files = [f for f in os.listdir(sound_dir) if f.lower().endswith('.mp3')] + files.sort() + return web.json_response(files) + +class NeoChooser: + @classmethod + def INPUT_TYPES(s): + return { + "required": {"images": ("IMAGE",)}, + "hidden": {"unique_id": "UNIQUE_ID"} + } + + RETURN_TYPES = ("IMAGE",) + CATEGORY = "NeoNodes" + FUNCTION = "process" + + def process(self, images, unique_id): + out_urls = [] + temp_dir = folder_paths.get_temp_directory() + for i, t in enumerate(images): + img_data = 255. * t.cpu().numpy() + img = Image.fromarray(np.clip(img_data, 0, 255).astype(np.uint8)) + filename = f"neo_temp_{unique_id}_{int(time.time())}_{i}.png" + img.save(os.path.join(temp_dir, filename)) + out_urls.append({"filename": filename, "type": "temp", "subfolder": ""}) + + server.PromptServer.instance.send_sync("neo_chooser_show", {"node_id": unique_id, "images": out_urls}) + + PAUSE_STATE[str(unique_id)] = None + while PAUSE_STATE[str(unique_id)] is None: + time.sleep(0.05) + comfy.model_management.throw_exception_if_processing_interrupted() + + reply = PAUSE_STATE.pop(str(unique_id)) + + if reply["action"] == "cancel": + # Тихая, родная отмена ComfyUI без красного окна ошибки + comfy.model_management.interrupt_current_processing() + return (images,) + + selected = reply.get("selected", []) + + if not selected or len(selected) == 0: + return (images,) + + return (images[selected],) + +NODE_CLASS_MAPPINGS = {"NeoChooser": NeoChooser} +NODE_DISPLAY_NAME_MAPPINGS = {"NeoChooser": "Neo Image Chooser 👁️"} +WEB_DIRECTORY = "./web" \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..a9a1a25 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +numpy +Pillow \ No newline at end of file