Completed Setup
This commit is contained in:
Generated
+19
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="MessDetectorOptionsConfiguration">
|
||||
<option name="transferred" value="true" />
|
||||
</component>
|
||||
<component name="PHPCSFixerOptionsConfiguration">
|
||||
<option name="transferred" value="true" />
|
||||
</component>
|
||||
<component name="PHPCodeSnifferOptionsConfiguration">
|
||||
<option name="highlightLevel" value="WARNING" />
|
||||
<option name="transferred" value="true" />
|
||||
</component>
|
||||
<component name="PhpStanOptionsConfiguration">
|
||||
<option name="transferred" value="true" />
|
||||
</component>
|
||||
<component name="PsalmOptionsConfiguration">
|
||||
<option name="transferred" value="true" />
|
||||
</component>
|
||||
</project>
|
||||
Generated
+7
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/ComfyUI_UploadToWebhookHTTP" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
Generated
+24
-17
@@ -4,7 +4,13 @@
|
||||
<option name="autoReloadType" value="SELECTIVE" />
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="5ecd7f77-46de-4759-abcd-75a54d55e666" name="Changes" comment="" />
|
||||
<list default="true" id="5ecd7f77-46de-4759-abcd-75a54d55e666" name="Changes" comment="">
|
||||
<change afterPath="$PROJECT_DIR$/__init__.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/screenshot.png" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/screenshot2.png" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" afterDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||
@@ -17,27 +23,27 @@
|
||||
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
||||
</component>
|
||||
<component name="PhpWorkspaceProjectConfiguration" interpreter_name="PHP" />
|
||||
<component name="ProjectColorInfo"><![CDATA[{
|
||||
"associatedIndex": 3
|
||||
}]]></component>
|
||||
<component name="ProjectColorInfo">{
|
||||
"associatedIndex": 3
|
||||
}</component>
|
||||
<component name="ProjectId" id="2vOePOfpJD8RzPmuSqySDGz0Bj4" />
|
||||
<component name="ProjectViewState">
|
||||
<option name="hideEmptyMiddlePackages" value="true" />
|
||||
<option name="showLibraryContents" value="true" />
|
||||
</component>
|
||||
<component name="PropertiesComponent"><![CDATA[{
|
||||
"keyToString": {
|
||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||
"git-widget-placeholder": "main",
|
||||
"last_opened_file_path": "/Users/jerrychukwudi/projects/ComfyUI_UploadToWebhookHTTP",
|
||||
"node.js.detected.package.eslint": "true",
|
||||
"node.js.detected.package.tslint": "true",
|
||||
"node.js.selected.package.eslint": "(autodetect)",
|
||||
"node.js.selected.package.tslint": "(autodetect)",
|
||||
"nodejs_package_manager_path": "npm",
|
||||
"vue.rearranger.settings.migration": "true"
|
||||
<component name="PropertiesComponent">{
|
||||
"keyToString": {
|
||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||
"git-widget-placeholder": "main",
|
||||
"last_opened_file_path": "/Users/jerrychukwudi/projects/ComfyUI_UploadToWebhookHTTP",
|
||||
"node.js.detected.package.eslint": "true",
|
||||
"node.js.detected.package.tslint": "true",
|
||||
"node.js.selected.package.eslint": "(autodetect)",
|
||||
"node.js.selected.package.tslint": "(autodetect)",
|
||||
"nodejs_package_manager_path": "npm",
|
||||
"vue.rearranger.settings.migration": "true"
|
||||
}
|
||||
}]]></component>
|
||||
}</component>
|
||||
<component name="SharedIndexes">
|
||||
<attachedChunks>
|
||||
<set>
|
||||
@@ -54,7 +60,8 @@
|
||||
<option name="number" value="Default" />
|
||||
<option name="presentableId" value="Default" />
|
||||
<updated>1744020525893</updated>
|
||||
<workItem from="1744020531791" duration="7294000" />
|
||||
<workItem from="1744020531791" duration="7939000" />
|
||||
<workItem from="1744028534478" duration="706000" />
|
||||
</task>
|
||||
<servers />
|
||||
</component>
|
||||
|
||||
@@ -1 +1,105 @@
|
||||
# ComfyUI_UploadToWebhookHTTP
|
||||
# ComfyUI_UploadToWebHook
|
||||
## @jerrywap
|
||||
|
||||

|
||||
|
||||
A custom node for [ComfyUI](https://github.com/comfyanonymous/ComfyUI) that allows you to **send generated image(s) or video clips** directly to an HTTP webhook along with optional metadata such as `prompt_id` and `other_data`.
|
||||
|
||||
---
|
||||
|
||||
## ✨ Features
|
||||
|
||||
- 🔗 Upload a single image or video (from image sequence) to a webhook URL
|
||||
- 📦 Includes `prompt_id` and additional `other_data` as form data
|
||||
- 🧠 Accepts image tensor input from ComfyUI image nodes
|
||||
- 🎛️ Optional save toggle to control file persistence (save or temp)
|
||||
- 📋 Textarea input for passing additional JSON data
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Installation
|
||||
|
||||
1. Clone or download this repo into your `ComfyUI/custom_nodes` directory:
|
||||
|
||||
```
|
||||
cd /workspace/ComfyUI/custom_nodes
|
||||
git clone https://github.com/yourusername/ComfyUI_UploadToWebHook.git
|
||||
```
|
||||
|
||||
2. Install dependencies:
|
||||
|
||||
```
|
||||
cd ComfyUI_UploadToWebHook
|
||||
bash install.sh
|
||||
```
|
||||
|
||||
3. Restart ComfyUI (or use the **Reload Custom Nodes** button if available).
|
||||
|
||||
---
|
||||
|
||||
## 🧩 How to Use
|
||||
|
||||
In ComfyUI:
|
||||
|
||||
1. Drag your image output (e.g., from `VAE Decode`) to the **Send To Http Webhook** node.
|
||||
2. Configure:
|
||||
- `webhook_url`: The URL that will receive your image or video.
|
||||
- `frame_rate`: Only used when sending multiple images as a video.
|
||||
- `save_image`: Toggle to control whether to store file permanently.
|
||||
- `prompt_id`: (optional) Identifier of your prompt or run.
|
||||
- `other_data`: (optional) JSON key-value data sent as extra POST fields.
|
||||
|
||||
---
|
||||
|
||||
## 🖼️ UI Example
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 🧪 Webhook Example
|
||||
|
||||
You can test with a free service like [https://webhook.site](https://webhook.site).
|
||||
|
||||
- The uploaded image/video appears as `file`.
|
||||
- Additional fields (`prompt_id`, `other_data`) are sent as part of the POST form data.
|
||||
|
||||
---
|
||||
|
||||
## 📤 Output Payload
|
||||
|
||||
Sample multipart form sent to webhook:
|
||||
|
||||
```
|
||||
POST /your/webhook HTTP/1.1
|
||||
Content-Type: multipart/form-data
|
||||
|
||||
file: image.png / video.mp4
|
||||
prompt_id: acde070d-8c4c-4f0d-9d8a-162843
|
||||
other_data: {"hello": "world"}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📁 Folder Structure
|
||||
|
||||
```
|
||||
ComfyUI_UploadToWebHook/
|
||||
├── __init__.py
|
||||
├── requirements.txt
|
||||
└── install.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧑💻 Contributing
|
||||
|
||||
Pull requests and feature ideas are welcome!
|
||||
Feel free to fork and create a PR.
|
||||
|
||||
---
|
||||
|
||||
## 📄 License
|
||||
|
||||
This project is licensed under the MIT License.
|
||||
See [LICENSE](./LICENSE) for details.
|
||||
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
import os
|
||||
import json
|
||||
import numpy as np
|
||||
import requests
|
||||
from PIL import Image
|
||||
|
||||
import folder_paths
|
||||
from moviepy.video.io.ImageSequenceClip import ImageSequenceClip
|
||||
|
||||
class UploadToWebHookHTTP:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"images": ("IMAGE",),
|
||||
"webhook_url": ("STRING", {"default": "https://example.com/path_to_webhook"}),
|
||||
"frame_rate": ("INT", {"default": 12, "min": 1, "max": 60, "step": 1}),
|
||||
"save_image": ("BOOLEAN", {"default": True}),
|
||||
"prompt_id": ("STRING", {"default": ""}),
|
||||
"other_data": (
|
||||
"STRING",
|
||||
{
|
||||
"default": "{\n \"key1\": \"value1\",\n \"key2\": \"value2\"\n}",
|
||||
"multiline": True
|
||||
}
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
OUTPUT_NODE = True
|
||||
CATEGORY = "JerrywapWebhookHTTP"
|
||||
FUNCTION = "generate_and_upload_video"
|
||||
|
||||
def generate_and_upload_video(
|
||||
self,
|
||||
images,
|
||||
webhook_url: str,
|
||||
frame_rate: int,
|
||||
save_image=True,
|
||||
prompt_id: str = "",
|
||||
other_data: str = "{}"
|
||||
):
|
||||
output_dir = folder_paths.get_output_directory() if save_image else folder_paths.get_temp_directory()
|
||||
|
||||
(
|
||||
full_output_folder,
|
||||
filename,
|
||||
counter,
|
||||
subfolder,
|
||||
_,
|
||||
) = folder_paths.get_save_image_path("final", output_dir)
|
||||
|
||||
# Prepare payload
|
||||
try:
|
||||
parsed_data = json.loads(other_data)
|
||||
except json.JSONDecodeError:
|
||||
parsed_data = {}
|
||||
|
||||
if prompt_id:
|
||||
parsed_data["prompt_id"] = prompt_id
|
||||
|
||||
if len(images) == 1:
|
||||
single_file_path = os.path.join(full_output_folder, f"{filename}_.png")
|
||||
single_image = 255.0 * images[0].cpu().numpy()
|
||||
single_image_pil = Image.fromarray(single_image.astype(np.uint8))
|
||||
single_image_pil.save(single_file_path)
|
||||
|
||||
with open(single_file_path, "rb") as file:
|
||||
response = requests.post(
|
||||
webhook_url,
|
||||
files={"file": ("image.png", file, "image/png")},
|
||||
data=parsed_data
|
||||
)
|
||||
|
||||
else:
|
||||
frames = [255.0 * image.cpu().numpy() for image in images]
|
||||
clip = ImageSequenceClip(frames, fps=frame_rate)
|
||||
file_path = os.path.join(full_output_folder, f"{filename}_{counter:05}_.mp4")
|
||||
clip.write_videofile(file_path, codec="libx264", fps=frame_rate)
|
||||
|
||||
with open(file_path, "rb") as file_data:
|
||||
response = requests.post(
|
||||
webhook_url,
|
||||
files={"file": ("video.mp4", file_data, "video/mp4")},
|
||||
data=parsed_data
|
||||
)
|
||||
|
||||
# Response output
|
||||
if response.status_code == 200 or response.status_code == 201 or response.status_code == 204:
|
||||
print("✅ Successfully uploaded to Webhook.")
|
||||
print(f"Response Code: {response.status_code}")
|
||||
print(f"Response Text: {response.text}")
|
||||
else:
|
||||
print(f"❌ Failed to upload. Status code: {response.status_code} - {response.text}")
|
||||
|
||||
return ("Uploading Completed",)
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"UploadToWebHookHTTP": UploadToWebHookHTTP,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"UploadToWebHookHTTP": "Send To Http Webhook",
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "🔧 Installing dependencies for ComfyUI_UploadToWebHook..."
|
||||
|
||||
# Exit immediately if a command exits with a non-zero status
|
||||
set -e
|
||||
|
||||
# Activate venv if in ComfyUI RunPod or similar
|
||||
if [ -d "../../venv" ]; then
|
||||
echo "📦 Activating virtual environment..."
|
||||
source ../../venv/bin/activate
|
||||
fi
|
||||
|
||||
# Install requirements
|
||||
pip install -r requirements.txt
|
||||
|
||||
echo "✅ Installation complete!"
|
||||
@@ -0,0 +1,4 @@
|
||||
numpy
|
||||
requests
|
||||
Pillow
|
||||
moviepy
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 87 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 40 KiB |
Reference in New Issue
Block a user