feat: support http get
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
@@ -0,0 +1,36 @@
|
||||
# ComfyUI HTTP - REST API Nodes
|
||||
|
||||
Powerful REST API nodes for ComfyUI that enable seamless HTTP/REST integration into your workflows.
|
||||
|
||||
## Installation
|
||||
|
||||
1. Clone the repository into your ComfyUI `custom_nodes` directory:
|
||||
```bash
|
||||
cd ComfyUI/custom_nodes
|
||||
git clone https://github.com/wawahuy/ComfyUI-HTTP.git
|
||||
```
|
||||
|
||||
2. Install dependencies:
|
||||
```bash
|
||||
cd ComfyUI-HTTP
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
3. Restart ComfyUI
|
||||
|
||||
## Contributing
|
||||
|
||||
We welcome contributions! Please:
|
||||
|
||||
1. Fork the repository
|
||||
2. Create a feature branch
|
||||
3. Commit your changes
|
||||
4. Submit a pull request
|
||||
|
||||
## License
|
||||
|
||||
MIT License - see LICENSE file for details.
|
||||
|
||||
## Support
|
||||
|
||||
If you encounter issues or have questions, please create an issue on the GitHub repository.
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
from .http_get import HTTPGetNode
|
||||
from .http_get_json_field import HTTPGetJSONFieldNode
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"HTTPGetNode": HTTPGetNode,
|
||||
"HTTPGetJSONFieldNode": HTTPGetJSONFieldNode,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"HTTPGetNode": "HTTP Get",
|
||||
"HTTPGetJSONFieldNode": "HTTP Get JSON Field",
|
||||
}
|
||||
|
||||
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
import requests
|
||||
import json
|
||||
import time
|
||||
from typing import Dict, Any, Tuple
|
||||
|
||||
class HTTPGetNode:
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls) -> Dict[str, Any]:
|
||||
return {
|
||||
"required": {
|
||||
"url": ("STRING", {
|
||||
"multiline": False,
|
||||
"default": "https://httpbin.org/json"
|
||||
}),
|
||||
},
|
||||
"optional": {
|
||||
"headers": ("STRING", {
|
||||
"multiline": True,
|
||||
"default": '{"Content-Type": "application/json", "User-Agent": "ComfyUI-HTTPPull-Starter/1.0"}'
|
||||
}),
|
||||
"params": ("STRING", {
|
||||
"multiline": True,
|
||||
"default": '{"workflow": "started", "source": "comfyui"}'
|
||||
}),
|
||||
"timeout": ("INT", {
|
||||
"default": 30,
|
||||
"min": 5,
|
||||
"max": 300,
|
||||
"step": 5
|
||||
}),
|
||||
"retry_count": ("INT", {
|
||||
"default": 1,
|
||||
"min": 1,
|
||||
"max": 5,
|
||||
"step": 1
|
||||
}),
|
||||
"retry_delay": ("INT", {
|
||||
"default": 5,
|
||||
"min": 1,
|
||||
"max": 30,
|
||||
"step": 1
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING", "STRING", "INT", "BOOLEAN", "STRING")
|
||||
RETURN_NAMES = ("response_text", "response_json", "status_code", "success", "workflow_id")
|
||||
FUNCTION = "http_get"
|
||||
CATEGORY = "HTTP/API"
|
||||
OUTPUT_NODE = False
|
||||
|
||||
def http_get(self, url: str, headers: str = "", params: str = "",
|
||||
timeout: int = 30, retry_count: int = 1, retry_delay: int = 5) -> Tuple[str, str, int, bool, str]:
|
||||
|
||||
workflow_id = f"wf_{int(time.time())}"
|
||||
|
||||
last_error = ""
|
||||
|
||||
for attempt in range(retry_count):
|
||||
try:
|
||||
request_headers = {"Content-Type": "application/json"}
|
||||
if headers:
|
||||
try:
|
||||
parsed_headers = json.loads(headers)
|
||||
request_headers.update(parsed_headers)
|
||||
except json.JSONDecodeError:
|
||||
print(f"Warning: Invalid headers JSON format: {headers}")
|
||||
|
||||
request_params = {}
|
||||
if params:
|
||||
try:
|
||||
request_params = json.loads(params)
|
||||
request_params["workflow_id"] = workflow_id
|
||||
request_params["attempt"] = attempt + 1
|
||||
except json.JSONDecodeError:
|
||||
print(f"Warning: Invalid params JSON format: {params}")
|
||||
|
||||
print(f"HTTP Get - Attempt {attempt + 1}/{retry_count} - URL: {url}")
|
||||
|
||||
response = requests.get(
|
||||
url=url,
|
||||
headers=request_headers,
|
||||
params=request_params,
|
||||
timeout=timeout
|
||||
)
|
||||
|
||||
response_text = response.text
|
||||
|
||||
response_json = "{}"
|
||||
try:
|
||||
json.loads(response_text)
|
||||
response_json = response_text
|
||||
except json.JSONDecodeError:
|
||||
response_json = "{}"
|
||||
|
||||
status_code = response.status_code
|
||||
success = 200 <= status_code < 300
|
||||
|
||||
if success:
|
||||
print(f"HTTP Get - Success: Workflow {workflow_id} started")
|
||||
return (response_text, response_json, status_code, True, workflow_id)
|
||||
else:
|
||||
last_error = f"HTTP error {status_code}: {response_text[:200]}"
|
||||
|
||||
except requests.exceptions.Timeout:
|
||||
last_error = f"Request timeout after {timeout} seconds"
|
||||
print(f"HTTP Get - Attempt {attempt + 1} failed: {last_error}")
|
||||
|
||||
except requests.exceptions.ConnectionError:
|
||||
last_error = f"Connection error to {url}"
|
||||
print(f"HTTP Get - Attempt {attempt + 1} failed: {last_error}")
|
||||
|
||||
except Exception as e:
|
||||
last_error = f"HTTP Get Error: {str(e)}"
|
||||
print(f"HTTP Get - Attempt {attempt + 1} failed: {last_error}")
|
||||
|
||||
if attempt < retry_count - 1:
|
||||
print(f"Waiting {retry_delay} seconds before retry...")
|
||||
time.sleep(retry_delay)
|
||||
|
||||
print(f"HTTP Get - All attempts failed for workflow {workflow_id}")
|
||||
return (last_error, "{}", 0, False, workflow_id)
|
||||
@@ -0,0 +1,77 @@
|
||||
import json
|
||||
from typing import Dict, Any, Tuple, Union
|
||||
|
||||
class HTTPGetJSONFieldNode:
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls) -> Dict[str, Any]:
|
||||
return {
|
||||
"required": {
|
||||
"json_input": ("STRING", {
|
||||
"multiline": True,
|
||||
"default": '{"data": {"message": "Hello World"}, "status": "success"}'
|
||||
}),
|
||||
"field_path": ("STRING", {
|
||||
"multiline": False,
|
||||
"default": "data.message"
|
||||
}),
|
||||
},
|
||||
"optional": {
|
||||
"default_value": ("STRING", {
|
||||
"multiline": False,
|
||||
"default": ""
|
||||
}),
|
||||
"return_as_string": ("BOOLEAN", {
|
||||
"default": True
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING", "STRING", "BOOLEAN")
|
||||
RETURN_NAMES = ("field_value", "error_message", "found")
|
||||
FUNCTION = "get_json_field"
|
||||
CATEGORY = "HTTP/API"
|
||||
|
||||
def get_json_field(self, json_input: str, field_path: str,
|
||||
default_value: str = "", return_as_string: bool = True) -> Tuple[str, str, bool]:
|
||||
try:
|
||||
data = json.loads(json_input)
|
||||
|
||||
path_parts = field_path.split('.')
|
||||
|
||||
current_value = data
|
||||
for part in path_parts:
|
||||
if isinstance(current_value, dict) and part in current_value:
|
||||
current_value = current_value[part]
|
||||
elif isinstance(current_value, list):
|
||||
try:
|
||||
index = int(part)
|
||||
if 0 <= index < len(current_value):
|
||||
current_value = current_value[index]
|
||||
else:
|
||||
return (default_value, f"List index {index} out of range", False)
|
||||
except ValueError:
|
||||
return (default_value, f"Cannot access list with key '{part}'", False)
|
||||
else:
|
||||
return (default_value, f"Field '{part}' not found in path '{field_path}'", False)
|
||||
|
||||
if return_as_string:
|
||||
if isinstance(current_value, (dict, list)):
|
||||
result_value = json.dumps(current_value, ensure_ascii=False)
|
||||
else:
|
||||
result_value = str(current_value)
|
||||
else:
|
||||
result_value = str(current_value)
|
||||
|
||||
print(f"JSON Field Get - Path: {field_path}, Found: {type(current_value).__name__}")
|
||||
return (result_value, "", True)
|
||||
|
||||
except json.JSONDecodeError as e:
|
||||
error_message = f"Invalid JSON format: {str(e)}"
|
||||
print(f"JSON Field Error: {error_message}")
|
||||
return (default_value, error_message, False)
|
||||
|
||||
except Exception as e:
|
||||
error_message = f"Error getting field: {str(e)}"
|
||||
print(f"JSON Field Error: {error_message}")
|
||||
return (default_value, error_message, False)
|
||||
@@ -0,0 +1,3 @@
|
||||
requests>=2.25.0
|
||||
pillow>=8.0.0
|
||||
numpy>=1.19.0
|
||||
Reference in New Issue
Block a user