新增通用文件上传节点
This commit is contained in:
parent
029d41a327
commit
64cb10c369
|
|
@ -26,7 +26,7 @@ image = (
|
||||||
.run_commands("comfy node install https://github.com/Kosinkadink/ComfyUI-VideoHelperSuite.git")
|
.run_commands("comfy node install https://github.com/Kosinkadink/ComfyUI-VideoHelperSuite.git")
|
||||||
.run_commands("comfy node install https://github.com/WASasquatch/was-node-suite-comfyui.git")
|
.run_commands("comfy node install https://github.com/WASasquatch/was-node-suite-comfyui.git")
|
||||||
.run_commands("comfy node install https://github.com/cubiq/ComfyUI_essentials.git")
|
.run_commands("comfy node install https://github.com/cubiq/ComfyUI_essentials.git")
|
||||||
.add_local_dir(local_path=r'/Users/charon/Desktop/ComfyUI-CustomNode',
|
.add_local_dir(local_path=r'D:\code\ComfyUI-CustomNode',
|
||||||
remote_path='/root/comfy/ComfyUI/custom_nodes',
|
remote_path='/root/comfy/ComfyUI/custom_nodes',
|
||||||
copy=True
|
copy=True
|
||||||
)
|
)
|
||||||
|
|
@ -37,7 +37,7 @@ image = (
|
||||||
.run_commands("rm -rf /root/comfy/ComfyUI/input&&ln -s /models/input /root/comfy/ComfyUI/input")
|
.run_commands("rm -rf /root/comfy/ComfyUI/input&&ln -s /models/input /root/comfy/ComfyUI/input")
|
||||||
.run_commands("rm -rf /root/comfy/ComfyUI/output&&ln -s /models/output /root/comfy/ComfyUI/output")
|
.run_commands("rm -rf /root/comfy/ComfyUI/output&&ln -s /models/output /root/comfy/ComfyUI/output")
|
||||||
)
|
)
|
||||||
app = modal.App(image=image, name='comfyui-video-agent')
|
app = modal.App(image=image, name='cf-video-api')
|
||||||
custom_secret = modal.Secret.from_name("comfyui-custom-secret", environment_name="dev")
|
custom_secret = modal.Secret.from_name("comfyui-custom-secret", environment_name="dev")
|
||||||
vol = modal.Volume.from_name("comfy_model", environment_name="dev", create_if_missing=True)
|
vol = modal.Volume.from_name("comfy_model", environment_name="dev", create_if_missing=True)
|
||||||
|
|
||||||
|
|
@ -55,9 +55,9 @@ vol = modal.Volume.from_name("comfy_model", environment_name="dev", create_if_mi
|
||||||
@modal.concurrent(
|
@modal.concurrent(
|
||||||
max_inputs=10
|
max_inputs=10
|
||||||
)
|
)
|
||||||
@modal.web_server(8000, startup_timeout=600, label='image-video-agent-1')
|
@modal.web_server(8000, startup_timeout=120, label='image-video-agent-1')
|
||||||
def ui_1():
|
def ui_1():
|
||||||
process = subprocess.Popen("comfy launch -- --cpu --listen 0.0.0.0 --port 8000", shell=True)
|
subprocess.Popen("comfy launch -- --cpu --listen 0.0.0.0 --port 8000", shell=True)
|
||||||
|
|
||||||
|
|
||||||
@app.function(
|
@app.function(
|
||||||
|
|
@ -73,6 +73,6 @@ def ui_1():
|
||||||
@modal.concurrent(
|
@modal.concurrent(
|
||||||
max_inputs=10
|
max_inputs=10
|
||||||
)
|
)
|
||||||
@modal.web_server(8000, startup_timeout=600, label='image-video-agent-2')
|
@modal.web_server(8000, startup_timeout=120, label='image-video-agent-2')
|
||||||
def ui_2():
|
def ui_2():
|
||||||
subprocess.Popen("comfy launch -- --cpu --listen 0.0.0.0 --port 8000", shell=True)
|
subprocess.Popen("comfy launch -- --cpu --listen 0.0.0.0 --port 8000", shell=True)
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
import mimetypes
|
import mimetypes
|
||||||
import uuid
|
|
||||||
import boto3
|
|
||||||
import os
|
import os
|
||||||
from botocore.config import Config
|
import uuid
|
||||||
import asyncio
|
|
||||||
import torch
|
import boto3
|
||||||
import numpy as np
|
import folder_paths
|
||||||
from PIL import Image
|
import numpy as np
|
||||||
import folder_paths
|
import requests
|
||||||
|
import torch
|
||||||
|
from PIL import Image
|
||||||
|
from botocore.config import Config
|
||||||
|
|
||||||
# 尝试导入 scipy,如果失败则给出提示
|
|
||||||
try:
|
try:
|
||||||
import scipy.io.wavfile as wavfile
|
import scipy.io.wavfile as wavfile
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
|
@ -17,7 +17,6 @@ except ImportError:
|
||||||
print("[FileUploadNode] 提示: Scipy 库未安装, 如果需要处理音频输入, 请运行: pip install scipy")
|
print("[FileUploadNode] 提示: Scipy 库未安装, 如果需要处理音频输入, 请运行: pip install scipy")
|
||||||
print("------------------------------------------------------------------------------------")
|
print("------------------------------------------------------------------------------------")
|
||||||
|
|
||||||
# --- AWS S3 配置 ---
|
|
||||||
aws_settings = {
|
aws_settings = {
|
||||||
'access_key_id': 'AKIAYRH5NGRSWHN2L4M6',
|
'access_key_id': 'AKIAYRH5NGRSWHN2L4M6',
|
||||||
'secret_access_key': 'kfAqoOmIiyiywi25xaAkJUQbZ/EKDnzvI6NRCW1l',
|
'secret_access_key': 'kfAqoOmIiyiywi25xaAkJUQbZ/EKDnzvI6NRCW1l',
|
||||||
|
|
@ -27,8 +26,7 @@ aws_settings = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# --- 核心上传逻辑 ---
|
def upload_file_s3_v2(file_path: str, remove: bool = False, perpetual: bool = False):
|
||||||
async def upload_file_s3_v2(file_path: str, remove: bool = False, perpetual: bool = False):
|
|
||||||
"""
|
"""
|
||||||
使用 boto3 客户端异步上传文件到 S3
|
使用 boto3 客户端异步上传文件到 S3
|
||||||
"""
|
"""
|
||||||
|
|
@ -80,6 +78,26 @@ async def upload_file_s3_v2(file_path: str, remove: bool = False, perpetual: boo
|
||||||
return resp_data
|
return resp_data
|
||||||
|
|
||||||
|
|
||||||
|
def upload_file_gs(file_path: str):
|
||||||
|
headers = {
|
||||||
|
'accept': 'application/json',
|
||||||
|
}
|
||||||
|
file_name = os.path.basename(file_path)
|
||||||
|
with open(file_path, 'rb') as f:
|
||||||
|
file_stream = f.read()
|
||||||
|
mime_type, _ = mimetypes.guess_type(file_path)
|
||||||
|
files = {
|
||||||
|
'file': (file_name, file_stream, mime_type),
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.post(
|
||||||
|
'https://https://modal-prod.bowong.cc/api/file/upload/s3',
|
||||||
|
headers=headers,
|
||||||
|
files=files,
|
||||||
|
)
|
||||||
|
return response.json()
|
||||||
|
|
||||||
|
|
||||||
class FileUploadNode:
|
class FileUploadNode:
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
@ -87,7 +105,8 @@ class FileUploadNode:
|
||||||
return {
|
return {
|
||||||
"required": {
|
"required": {
|
||||||
"perpetual": ("BOOLEAN", {"default": False}),
|
"perpetual": ("BOOLEAN", {"default": False}),
|
||||||
"remove_source_file": ("BOOLEAN", {"default": False}),
|
# "remove_source_file": ("BOOLEAN", {"default": False}),
|
||||||
|
"cdn_type": (['s3', 'gs'], {"default": "s3"}),
|
||||||
},
|
},
|
||||||
"optional": {
|
"optional": {
|
||||||
"video": ("*",),
|
"video": ("*",),
|
||||||
|
|
@ -124,11 +143,11 @@ class FileUploadNode:
|
||||||
waveform_int16 = np.int16(waveform_np * 32767)
|
waveform_int16 = np.int16(waveform_np * 32767)
|
||||||
output_dir = folder_paths.get_temp_directory()
|
output_dir = folder_paths.get_temp_directory()
|
||||||
(full_output_folder, filename, _, _, _) = folder_paths.get_save_image_path("uploader_temp_audio", output_dir)
|
(full_output_folder, filename, _, _, _) = folder_paths.get_save_image_path("uploader_temp_audio", output_dir)
|
||||||
filepath = os.path.join(full_output_folder, f"{filename}.wav")
|
filepath = os.path.join(full_output_folder, f"{filename}.mp3")
|
||||||
wavfile.write(filepath, sample_rate, waveform_int16)
|
wavfile.write(filepath, sample_rate, waveform_int16)
|
||||||
return filepath
|
return filepath
|
||||||
|
|
||||||
def upload_file(self, perpetual, remove_source_file, image=None, audio=None, video=None):
|
def upload_file(self, perpetual, cdn_type: str = 's3', image=None, audio=None, video=None):
|
||||||
resolved_path = None
|
resolved_path = None
|
||||||
if video is not None:
|
if video is not None:
|
||||||
print('[FileUploadNode INFO] 检测到视频输入...')
|
print('[FileUploadNode INFO] 检测到视频输入...')
|
||||||
|
|
@ -171,17 +190,14 @@ class FileUploadNode:
|
||||||
return (f"ERROR: 解析后的文件路径无效或文件不存在: {resolved_path}",)
|
return (f"ERROR: 解析后的文件路径无效或文件不存在: {resolved_path}",)
|
||||||
|
|
||||||
print(f"[FileUploadNode INFO] 最终待上传文件: {resolved_path}")
|
print(f"[FileUploadNode INFO] 最终待上传文件: {resolved_path}")
|
||||||
loop = asyncio.new_event_loop()
|
if cdn_type == 's3':
|
||||||
asyncio.set_event_loop(loop)
|
result = upload_file_s3_v2(
|
||||||
result = loop.run_until_complete(
|
|
||||||
upload_file_s3_v2(
|
|
||||||
file_path=resolved_path,
|
file_path=resolved_path,
|
||||||
remove=remove_source_file,
|
remove=False,
|
||||||
perpetual=perpetual
|
perpetual=perpetual
|
||||||
)
|
)
|
||||||
)
|
else:
|
||||||
loop.close()
|
result = upload_file_gs(resolved_path)
|
||||||
|
|
||||||
if result['status']:
|
if result['status']:
|
||||||
return (result['data'],)
|
return (result['data'],)
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -117,6 +117,8 @@ class ImgSubmitNode:
|
||||||
},
|
},
|
||||||
"optional": {
|
"optional": {
|
||||||
"image": ("IMAGE",),
|
"image": ("IMAGE",),
|
||||||
|
"img_urls": ("STRING", {"multiline": False, "default": "", "description": '输入图片的链接'}),
|
||||||
|
"output_count": ("STRING", {"default": 1, "multiline": False}),
|
||||||
"image_filename": ("STRING", {"multiline": False, "default": ""}),
|
"image_filename": ("STRING", {"multiline": False, "default": ""}),
|
||||||
"image_urls": (
|
"image_urls": (
|
||||||
"STRING", {"multiline": True, "default": "", "placeholder": "单个或多个图片URL,用英文逗号隔开..."}),
|
"STRING", {"multiline": True, "default": "", "placeholder": "单个或多个图片URL,用英文逗号隔开..."}),
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ class LLMUionNode:
|
||||||
RETURN_TYPES = ("STRING",)
|
RETURN_TYPES = ("STRING",)
|
||||||
RETURN_NAMES = ("text",)
|
RETURN_NAMES = ("text",)
|
||||||
FUNCTION = "execute"
|
FUNCTION = "execute"
|
||||||
CATEGORY = "不忘科技-自定义节点🚩/LLM"
|
|
||||||
|
|
||||||
def tensor_to_pil(self, tensor):
|
def tensor_to_pil(self, tensor):
|
||||||
if tensor is None:
|
if tensor is None:
|
||||||
|
|
|
||||||
|
|
@ -72,10 +72,12 @@ def fetch_and_process_models():
|
||||||
try:
|
try:
|
||||||
video_response = None
|
video_response = None
|
||||||
for u in video_urls.values():
|
for u in video_urls.values():
|
||||||
|
print(f'start request config from:{u}')
|
||||||
try:
|
try:
|
||||||
video_response = requests.get(u, timeout=10, headers={
|
video_response = requests.get(u, timeout=10, headers={
|
||||||
'accept': 'application/json'})
|
'accept': 'application/json'})
|
||||||
video_response.raise_for_status()
|
video_response.raise_for_status()
|
||||||
|
print(f'config response:{video_response.text}')
|
||||||
break
|
break
|
||||||
except:
|
except:
|
||||||
continue
|
continue
|
||||||
|
|
|
||||||
|
|
@ -2,5 +2,5 @@
|
||||||
"""
|
"""
|
||||||
File __init__.py
|
File __init__.py
|
||||||
Author silence
|
Author silence
|
||||||
Date 2025/9/8 10:08
|
Date 2025/9/11 19:21
|
||||||
"""
|
"""
|
||||||
Loading…
Reference in New Issue