From 005643400bcf6a5a3dde0c498e68fbfff8efcd82 Mon Sep 17 00:00:00 2001 From: fudosanit Date: Wed, 10 Dec 2025 18:10:25 +0900 Subject: [PATCH] first commit --- README.md | 20 +++++++++++++ __init__.py | 3 ++ zip_output_node.py | 75 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 98 insertions(+) create mode 100644 README.md create mode 100644 __init__.py create mode 100644 zip_output_node.py diff --git a/README.md b/README.md new file mode 100644 index 0000000..fa5b0e7 --- /dev/null +++ b/README.md @@ -0,0 +1,20 @@ +# ComfyUI Zip Output Node + +[ComfyUI](https://github.com/comfyanonymous/ComfyUI) の `output` フォルダ内にある生成物を、ボタン一つでZIPファイルに圧縮するためのカスタムノードです。 + +RunPod や Google Colab などのクラウド環境(永続化ディスクを使用しない環境)で、生成された大量の画像を一度にまとめてダウンロードしたい場合に最適化されています。 + +## 主な機能 + +- **outputフォルダの一括Zip化:** 生成された画像やファイルを1つのZipにまとめます。 +- **Zipの入れ子防止:** 生成されるZipファイルの中に、過去のZipファイルが含まれないように除外処理を行います(Zip of Zip 問題の解決)。 +- **ディスク容量の節約:** オプションで、新しいZipを作成する直前に古いZipファイルを自動削除できます(RunPodなどの容量制限がある環境で有用)。 +- **ダウンロードURL支援:** ブラウザから直接ダウンロードしやすいよう、ファイル名単体を出力します。 + +## インストール方法 + +`ComfyUI/custom_nodes/` ディレクトリに移動し、このリポジトリをクローンしてください。 + +```bash +cd ComfyUI/custom_nodes/ +git clone https://github.com/fudosanit/ComfyUI-ZipOutput.git \ No newline at end of file diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..13df1b9 --- /dev/null +++ b/__init__.py @@ -0,0 +1,3 @@ +from .zip_output_node import NODE_CLASS_MAPPINGS + +__all__ = ["NODE_CLASS_MAPPINGS"] \ No newline at end of file diff --git a/zip_output_node.py b/zip_output_node.py new file mode 100644 index 0000000..7e3536d --- /dev/null +++ b/zip_output_node.py @@ -0,0 +1,75 @@ +import zipfile +import os +from pathlib import Path +from datetime import datetime + +# 標準モジュールがない場合の安全策 +try: + import folder_paths +except ImportError: + folder_paths = None + +class ZipOutputFolder: + @classmethod + def INPUT_TYPES(cls): + return { + "required": { + "seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}), + "clear_previous_zips": ("BOOLEAN", {"default": True}), + } + } + + # 出力を2つに増やしました + RETURN_TYPES = ("STRING", "STRING") + RETURN_NAMES = ("full_path", "filename_only") + FUNCTION = "zip_folder" + CATEGORY = "Custom" + + def zip_folder(self, seed, clear_previous_zips): + if folder_paths: + output_dir = Path(folder_paths.get_output_directory()) + else: + output_dir = Path("output") + + if not output_dir.exists(): + output_dir = Path("output") + + # --- オプション: 古いzip削除 --- + if clear_previous_zips: + for existing_zip in output_dir.glob("*.zip"): + try: + existing_zip.unlink() + print(f"Deleted old zip: {existing_zip}") + except Exception as e: + print(f"Failed to delete {existing_zip}: {e}") + + # --- Zipファイル名作成 --- + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + zip_filename = f"output_{timestamp}.zip" + zip_path = output_dir / zip_filename + + # --- Zip作成 --- + with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zipf: + for file_path in output_dir.rglob("*"): + if not file_path.is_file(): + continue + if file_path.absolute() == zip_path.absolute(): + continue + if file_path.suffix.lower() == ".zip": + continue + + arcname = file_path.relative_to(output_dir) + zipf.write(file_path, arcname) + + print(f"Zip created: {zip_path}") + + # フルパスと、ファイル名単体を返す + return (str(zip_path), zip_filename) + +NODE_CLASS_MAPPINGS = { + "Zip Output Folder": ZipOutputFolder +} + +NODE_DISPLAY_NAME_MAPPINGS = { + "Zip Output Folder": "Zip Output Folder (Batch)" +} \ No newline at end of file