{"id":27753248,"url":"https://github.com/pupba/Comfy_ForEach","last_synced_at":"2025-04-29T05:02:18.387Z","repository":{"id":289774172,"uuid":"970422680","full_name":"pupba/Comfy_ForEach","owner":"pupba","description":"A collection of ComfyUI custom nodes designed for image batch processing, per-index image operations, and AWS integration using EventBridge.","archived":false,"fork":false,"pushed_at":"2025-04-25T00:36:07.000Z","size":6,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-25T01:31:50.036Z","etag":null,"topics":["aws","comfyui","comfyui-custom-node","comfyui-nodes"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pupba.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-04-22T02:03:12.000Z","updated_at":"2025-04-25T00:36:11.000Z","dependencies_parsed_at":"2025-04-25T01:46:52.381Z","dependency_job_id":null,"html_url":"https://github.com/pupba/Comfy_ForEach","commit_stats":null,"previous_names":["pupba/comfy_foreach"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pupba%2FComfy_ForEach","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pupba%2FComfy_ForEach/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pupba%2FComfy_ForEach/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pupba%2FComfy_ForEach/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pupba","download_url":"https://codeload.github.com/pupba/Comfy_ForEach/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251438912,"owners_count":21589540,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aws","comfyui","comfyui-custom-node","comfyui-nodes"],"created_at":"2025-04-29T05:02:16.902Z","updated_at":"2025-04-29T05:02:18.378Z","avatar_url":"https://github.com/pupba.png","language":"Python","funding_links":[],"categories":["Workflows (3395) sorted by GitHub Stars","All Workflows Sorted by GitHub Stars"],"sub_categories":[],"readme":"# 📦 ComfyForEach: Custom ComfyUI Nodes for Batch Image Processing and Used in AWS\n\n![version](https://img.shields.io/badge/version-1.0.0-blue.svg)\n![license](https://img.shields.io/badge/license-MIT-green.svg)\n\nA collection of ComfyUI custom nodes designed for image batch processing, per-index image operations, and AWS integration using EventBridge.\n\n[👉 Comfy Registry Link](https://registry.comfy.org/nodes/foreach)\n\n## 📁 Directory Structure\n\n```\ncustom_nodes/\n└── Comfy_ForEach/\n    ├── __init__.py\n    ├── context.py\n    ├── task_manager.py\n    ├── loader_nodes.py\n    ├── index_selector_nodes.py\n    ├── logic_nodes.py\n    ├── save_nodes.py\n    ├── aws_event_node.py\n    └── requirements.txt\n```\n\n## ✅ What needs to be modified in the ComfyUI\n\n**`execution.py`**\n\n```python\n######### Error Logging #########\ntry:\n    results.append(getattr(obj, func)(**inputs))\n\nexcept Exception as e:\n    import logging\n    from datetime import datetime\n    import traceback\n    import boto3\n    import custom_nodes.Comfy_ForEach.context as context\n    import json\n\n    logger = logging.getLogger(\"comfy_node_error\")\n    node_name = obj.__class__.__name__\n    error_msg = f\"[ERROR {datetime.now().isoformat()} | TaskID:{context.get_task_id()} | Node: {node_name} | Index: {index} | {type(e).__name__}: {e}\"\n\n    logger.error(error_msg)\n    logger.error(traceback.format_exc())\n\n    event = boto3.client(\"events\",region_name=\"us-east-1\")\n\n    # Changed Your Message\n    resp = event.put_events(\n        Entries=[\n            {\n                \"Source\":\"comfyui.ec2\",\n                \"DetailType\":\"ComfyUI Task State\",\n                \"Detail\":json.dumps({\n                    \"task_id\":task_id,\n                    \"status\":\"FAILED\",\n                    \"timestamp\":datetime.utcnow().isoformat(),\n                    \"error_msg\":error_msg\n                }),\n                \"EventBusName\":\"default\"\n            }\n        ]\n    )\n    if resp.get(\"FailedEntryCount\", 0) \u003e 0:\n        raise RuntimeError(\"❌ EventBridge Send Failed\")\n    else:\n        print(\"✅ EventBridge Send Success\")\n\n    raise\n#################################\n```\n\n**`main.py`**\n\n```python\nsetup_logger(log_level=args.verbose, use_stdout=args.log_stdout,log_path=\"./logs\")\n```\n\n**`app/logger`**\n\n```python\n...\nfrom logging.handlers import TimedRotatingFileHandler\nimport os\n...\n\ndef setup_logger(log_level: str = 'INFO', capacity: int = 300, use_stdout: bool = False,log_path:str | None=None):\n    global logs\n    if logs:\n        return\n\n    # Override output streams and log to buffer\n    logs = deque(maxlen=capacity)\n\n    global stdout_interceptor\n    global stderr_interceptor\n    stdout_interceptor = sys.stdout = LogInterceptor(sys.stdout)\n    stderr_interceptor = sys.stderr = LogInterceptor(sys.stderr)\n\n    # Setup default global logger\n    logger = logging.getLogger()\n    logger.setLevel(log_level)\n\n    stream_handler = logging.StreamHandler()\n    # stream_handler.setFormatter(logging.Formatter(\"%(message)s\"))\n    stream_handler.setFormatter(logging.Formatter(\"[%(asctime)s] [%(levelname)s] %(message)s\"))\n\n    if use_stdout:\n        # Only errors and critical to stderr\n        stream_handler.addFilter(lambda record: not record.levelno \u003c logging.ERROR)\n\n        # Lesser to stdout\n        stdout_handler = logging.StreamHandler(sys.stdout)\n        stdout_handler.setFormatter(logging.Formatter(\"%(message)s\"))\n        stdout_handler.addFilter(lambda record: record.levelno \u003c logging.ERROR)\n        logger.addHandler(stdout_handler)\n\n    logger.addHandler(stream_handler)\n\n    # Error Log\n    if log_path:\n        os.makedirs(log_path, exist_ok=True)\n        error_log_file = os.path.join(log_path, \"errors.log\")\n\n        error_file_handler = TimedRotatingFileHandler(\n            error_log_file,\n            when=\"midnight\",\n            interval=1,\n            backupCount=7,\n            encoding=\"utf-8\"\n        )\n        error_file_handler.suffix = \"%Y-%m-%d\"\n        error_file_handler.setLevel(logging.ERROR)\n        error_file_handler.setFormatter(logging.Formatter(\"[%(asctime)s] [%(levelname)s] %(message)s\"))\n        logger.addHandler(error_file_handler)\n\n```\n\n## 🧩 Node Overview\n\n### 🔹 TaskIDStorageNode\n\n- Stores a task ID in global memory (context.py) for use across the workflow.\n\n- **Category** : Workflow Utils\n\n- **Output** : task_id (STRING)\n\n### 🔹 FolderImageLoaderNode\n\n- Loads all .png images from a specified folder.\n\n- Outputs a list of image tensors and their filenames.\n\n- **Category** : ComfyForEach/Load\n\n- **Outputs** : image_list (IMAGE), image_name_list (STRING)\n\n### 🔹 IndexedImageSelectorNode\n\n- Selects a specific image from a list using an index.\n\n- **Category** : ComfyForEach/Select\n\n- **Output** : image (IMAGE)\n\n### 🔹 IndexedNameSelectorNode\n\n- Selects a specific image from a list using an index.\n\n- **Category** : ComfyForEach/Select\n\n- **Outputs** : file_name (STRING)\n\n### 🔹 IsLastIndexNode\n\n- Checks whether the current index is the last in a sequence.\n\n- Useful for triggering events only once at the end of a loop.\n\n- **Category** : ComfyForEach/Logic\n\n- **Output** : is_last (BOOLEAN)\n\n### 🔹 SaveExactNameImageNode\n\n- Saves an image tensor with an exact filename and folder path.\n\n- **Category** : ComfyForEach/Save\n\n- **Output** : None (Terminal node)\n\n### 🔹 EventBridgeTriggerNode\n\n- Simulates AWS EventBridge notification.\n\n- Writes a SUCCESS or FAILED event as a .json log based on is_last. (🔥 Please change it to the region of your **Event Bridge** that you will definitely request. And please also grant it from **IAM**.)\n\n- **Category** : ComfyForEach/AWS\n\n- **Output** : None (Terminal node)\n\n## 🧪 Requirements\n\nInstall dependencies:\n\n```bash\npip install -r requirements.txt\n```\n\nContents of `requirements.txt`\n\n```txt\npillow\nboto3\nopencv-python-headless\nnumpy\n# torch (usually installed with ComfyUI)\n```\n\n## 🔧 How to Use\n\n1. Clone or copy this into your ComfyUI/custom_nodes/Comfy_ForEach/ directory.\n\n2. Launch ComfyUI. The nodes will appear under categories like:\n\n   - ComfyForEach/Load\n\n   - ComfyForEach/Select\n\n   - ComfyForEach/Save\n\n   - ComfyForEach/AWS\n\n   - ComfyForEach/TaskID\n\n```bash\npython3 main.py --use-split-cross-attention --fast --input-directory ./test --output-directory ./test --verbose ERROR\n```\n\n3. Build workflows that iterate over image folders and process each image index-by-index.\n\n## 📌 Example Use Case\n\nA loop-style batch processor that:\n\n- Loads all images from a task-specific folder.\n\n- Selects images by index.\n\n- Saves outputs with structured filenames.\n\n- Triggers an event only when the last image is processed.\n\nUsed in distributed processing systems where each image may be handled independently but result aggregation is done based on a `task_id` .\n\n## 🛠 Maintainer Notes\n\n- Uses `context.py` to globally store and retrieve task_id across node executions.\n\n- EventBridge simulation can be replaced with real AWS API (boto3.client(\"events\")) as needed.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpupba%2FComfy_ForEach","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpupba%2FComfy_ForEach","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpupba%2FComfy_ForEach/lists"}