ADD 增加HeyGem APP

ADD 增加包含HeyGem的ComfyUI APP
This commit is contained in:
2025-04-09 18:55:08 +08:00
parent 9976333826
commit 8678b7bb7d
8 changed files with 370 additions and 351 deletions

View File

@@ -1,22 +0,0 @@
services:
comfyui-service:
image: comfyui-image:v1.2
container_name: comfyui
ports:
- 8188:8188
ipc: host
deploy:
resources:
limits:
memory: 64000M
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [ gpu ]
volumes:
#- ./custom_nodes:/app/ComfyUI/custom_nodes
- E://models:/root/comfy/ComfyUI/models
- E://comfy//ComfyUI//input:/root/comfy/ComfyUI/input
# - s3:/app/ComfyUI/input_s3
#- s3:/app/ComfyUI/output_s3

View File

@@ -1,134 +0,0 @@
# 使用 Debian Slim 作为基础镜像
FROM python:3.10-slim
# 创建并配置腾讯云的 apt 源
RUN apt update
RUN apt install -y wget
RUN wget https://linuxmirrors.cn/main.sh
RUN bash main.sh --source mirrors.cloud.tencent.com
RUN apt update
# 替换 pip 源为阿里云
RUN pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/
# 安装 git
RUN apt-get update && apt-get install -y git
# 安装 Python 依赖
RUN pip install fastapi[standard]==0.115.4 \
comfy-cli==1.3.5 \
cos-python-sdk-v5 \
sqlalchemy \
ultralytics \
tencentcloud-sdk-python \
pymysql \
Pillow \
ffmpy \
opencv-python \
av \
imageio \
loguru
# 安装 ComfyUI 及其依赖
RUN comfy --skip-prompt install --nvidia --version 0.3.10
# 克隆并安装自定义节点
RUN git clone https://github.com/M1kep/ComfyLiterals /root/comfy/ComfyUI/custom_nodes/ComfyLiterals && \
if [ -f /root/comfy/ComfyUI/custom_nodes/ComfyLiterals/requirements.txt ]; then pip install -r /root/comfy/ComfyUI/custom_nodes/ComfyLiterals/requirements.txt; fi
RUN git clone https://github.com/evanspearman/ComfyMath /root/comfy/ComfyUI/custom_nodes/ComfyMath && \
if [ -f /root/comfy/ComfyUI/custom_nodes/ComfyMath/requirements.txt ]; then pip install -r /root/comfy/ComfyUI/custom_nodes/ComfyMath/requirements.txt; fi
RUN git clone https://github.com/Kosinkadink/ComfyUI-AnimateDiff-Evolved /root/comfy/ComfyUI/custom_nodes/ComfyUI-AnimateDiff-Evolved && \
if [ -f /root/comfy/ComfyUI/custom_nodes/ComfyUI-AnimateDiff-Evolved/requirements.txt ]; then pip install -r /root/comfy/ComfyUI/custom_nodes/ComfyUI-AnimateDiff-Evolved/requirements.txt; fi
RUN git clone https://e.coding.net/g-ldyi2063/dev/ComfyUI-Bowong.git /root/comfy/ComfyUI/custom_nodes/ComfyUI-Bowong && \
if [ -f /root/comfy/ComfyUI/custom_nodes/ComfyUI-Bowong/requirements.txt ]; then pip install -r /root/comfy/ComfyUI/custom_nodes/ComfyUI-Bowong/requirements.txt; fi
RUN git clone https://github.com/crystian/ComfyUI-Crystools /root/comfy/ComfyUI/custom_nodes/ComfyUI-Crystools && \
if [ -f /root/comfy/ComfyUI/custom_nodes/ComfyUI-Crystools/requirements.txt ]; then pip install -r /root/comfy/ComfyUI/custom_nodes/ComfyUI-Crystools/requirements.txt; fi
RUN git clone https://github.com/pythongosssss/ComfyUI-Custom-Scripts /root/comfy/ComfyUI/custom_nodes/ComfyUI-Custom-Scripts && \
if [ -f /root/comfy/ComfyUI/custom_nodes/ComfyUI-Custom-Scripts/requirements.txt ]; then pip install -r /root/comfy/ComfyUI/custom_nodes/ComfyUI-Custom-Scripts/requirements.txt; fi
RUN git clone https://github.com/BennyKok/comfyui-deploy /root/comfy/ComfyUI/custom_nodes/comfyui-deploy && \
if [ -f /root/comfy/ComfyUI/custom_nodes/comfyui-deploy/requirements.txt ]; then pip install -r /root/comfy/ComfyUI/custom_nodes/comfyui-deploy/requirements.txt; fi
RUN git clone https://github.com/yolain/ComfyUI-Easy-Use /root/comfy/ComfyUI/custom_nodes/ComfyUI-Easy-Use && \
if [ -f /root/comfy/ComfyUI/custom_nodes/ComfyUI-Easy-Use/requirements.txt ]; then pip install -r /root/comfy/ComfyUI/custom_nodes/ComfyUI-Easy-Use/requirements.txt; fi
RUN git clone https://e.coding.net/g-ldyi2063/dev/ComfyUI-VideoHelperSuite.git /root/comfy/ComfyUI/custom_nodes/ComfyUI-VideoHelperSuite && \
if [ -f /root/comfy/ComfyUI/custom_nodes/ComfyUI-VideoHelperSuite/requirements.txt ]; then pip install -r /root/comfy/ComfyUI/custom_nodes/ComfyUI-VideoHelperSuite/requirements.txt; fi
#防止依赖安装问题
RUN pip install conformer==0.3.2 einops>0.6.1 --no-dependencies
RUN pip install -U openai-whisper
RUN apt install --reinstall -y gcc
RUN git clone https://e.coding.net/g-ldyi2063/dev/CosyVoice-ComfyUI.git /root/comfy/ComfyUI/custom_nodes/CosyVoice-ComfyUI && \
if [ -f /root/comfy/ComfyUI/custom_nodes/CosyVoice-ComfyUI/requirements.txt ]; then pip install -r /root/comfy/ComfyUI/custom_nodes/CosyVoice-ComfyUI/requirements.txt; fi
RUN git clone https://github.com/jags111/efficiency-nodes-comfyui /root/comfy/ComfyUI/custom_nodes/efficiency-nodes-comfyui && \
if [ -f /root/comfy/ComfyUI/custom_nodes/efficiency-nodes-comfyui/requirements.txt ]; then pip install -r /root/comfy/ComfyUI/custom_nodes/efficiency-nodes-comfyui/requirements.txt; fi
RUN git clone https://github.com/WASasquatch/was-node-suite-comfyui /root/comfy/ComfyUI/custom_nodes/was-node-suite-comfyui && \
if [ -f /root/comfy/ComfyUI/custom_nodes/was-node-suite-comfyui/requirements.txt ]; then pip install -r /root/comfy/ComfyUI/custom_nodes/was-node-suite-comfyui/requirements.txt; fi
RUN git clone https://github.com/rgthree/rgthree-comfy /root/comfy/ComfyUI/custom_nodes/rgthree-comfy && \
if [ -f /root/comfy/ComfyUI/custom_nodes/rgthree-comfy/requirements.txt ]; then pip install -r /root/comfy/ComfyUI/custom_nodes/rgthree-comfy/requirements.txt; fi
RUN git clone https://github.com/cubiq/ComfyUI_essentials /root/comfy/ComfyUI/custom_nodes/ComfyUI_essentials && \
if [ -f /root/comfy/ComfyUI/custom_nodes/ComfyUI_essentials/requirements.txt ]; then pip install -r /root/comfy/ComfyUI/custom_nodes/ComfyUI_essentials/requirements.txt; fi
RUN git clone https://github.com/melMass/comfy_mtb /root/comfy/ComfyUI/custom_nodes/comfy_mtb && \
if [ -f /root/comfy/ComfyUI/custom_nodes/comfy_mtb/requirements.txt ]; then pip install -r /root/comfy/ComfyUI/custom_nodes/comfy_mtb/requirements.txt; fi
RUN git clone https://e.coding.net/g-ldyi2063/dev/ComfyUI-CustomNode.git /root/comfy/ComfyUI/custom_nodes/ComfyUI-CustomNode && \
if [ -f /root/comfy/ComfyUI/custom_nodes/ComfyUI-CustomNode/requirements.txt ]; then pip install -r /root/comfy/ComfyUI/custom_nodes/ComfyUI-CustomNode/requirements.txt; fi
RUN git clone https://e.coding.net/g-ldyi2063/dev/ComfyUI-LatentSync-Node.git /root/comfy/ComfyUI/custom_nodes/ComfyUI-LatentSync-Node && \
if [ -f /root/comfy/ComfyUI/custom_nodes/ComfyUI-LatentSync-Node/requirements.txt ]; then pip install -r /root/comfy/ComfyUI/custom_nodes/ComfyUI-LatentSync-Node/requirements.txt; fi
#解决PortAudio library not found问题
RUN apt-get install -y libportaudio2
RUN git clone https://e.coding.net/g-ldyi2063/dev/ComfyUI_SparkTTS.git /root/comfy/ComfyUI/custom_nodes/ComfyUI_SparkTTS && \
if [ -f /root/comfy/ComfyUI/custom_nodes/ComfyUI_SparkTTS/requirements.txt ]; then pip install -r /root/comfy/ComfyUI/custom_nodes/ComfyUI_SparkTTS/requirements.txt; fi
# 创建符号链接
RUN mkdir -p /root/comfy/ComfyUI/models/ComfyUI-CustomNode/model && \
rm -rf /root/comfy/ComfyUI/custom_nodes/ComfyUI-CustomNode/model && \
ln -s /root/comfy/ComfyUI/models/ComfyUI-CustomNode/model /root/comfy/ComfyUI/custom_nodes/ComfyUI-CustomNode/model
RUN mkdir -p /root/comfy/ComfyUI/models/ComfyUI-LatentSync-Node/checkpoints && \
rm -rf /root/comfy/ComfyUI/custom_nodes/ComfyUI-LatentSync-Node/checkpoints && \
ln -s /root/comfy/ComfyUI/models/ComfyUI-LatentSync-Node/checkpoints /root/comfy/ComfyUI/custom_nodes/ComfyUI-LatentSync-Node/checkpoints
RUN mkdir -p /root/comfy/ComfyUI/models/CosyVoice-ComfyUI/pretrained_models/CosyVoice-300M && \
mkdir -p /root/comfy/ComfyUI/custom_nodes/CosyVoice-ComfyUI/pretrained_models/CosyVoice-300M && \
rm -rf /root/comfy/ComfyUI/custom_nodes/CosyVoice-ComfyUI/pretrained_models/CosyVoice-300M && \
ln -s /root/comfy/ComfyUI/models/CosyVoice-ComfyUI/pretrained_models/CosyVoice-300M /root/comfy/ComfyUI/custom_nodes/CosyVoice-ComfyUI/pretrained_models/CosyVoice-300M
RUN mkdir -p /root/comfy/ComfyUI/models/.cache/torch/hub/checkpoints && \
mkdir -p /root/.cache/torch/hub/checkpoints && \
rm -rf /root/.cache/torch/hub/checkpoints && \
ln -s /root/comfy/ComfyUI/models/.cache/torch/hub/checkpoints /root/.cache/torch/hub/checkpoints
RUN mkdir -p /root/comfy/ComfyUI/models/stabilityai && \
ln -s /root/comfy/ComfyUI/models/stabilityai /root/comfy/ComfyUI/stabilityai
RUN rm -rf /root/comfy/ComfyUI/models
# 安装 ffmpeg
RUN apt-get update && apt-get install -y ffmpeg && ffmpeg -version
# 添加本地配置文件
COPY config.yaml /root/comfy/ComfyUI/custom_nodes/ComfyUI-CustomNode/config.yaml
WORKDIR /root/comfy
CMD ["comfy", "--here", "launch", "--", "--listen", "0.0.0.0", "--port", "8188"]

View File

@@ -5,7 +5,7 @@ pkg_path = site.getsitepackages()[0]
sys.path.append(f"{pkg_path}/heygem/code") sys.path.append(f"{pkg_path}/heygem/code")
p = subprocess.Popen(f"cd {pkg_path}/heygem/code && " p = subprocess.Popen(f"cd {pkg_path}/heygem/code && "
"python3 app_local.py", shell=True) "python3.8 app_local.py", shell=True)
p.communicate() p.communicate()

149
server.py
View File

@@ -1,149 +0,0 @@
import json
import os
import subprocess
import uuid
from pathlib import Path
from typing import Dict
import modal
image = ( # build up a Modal Image to run ComfyUI, step by step
modal.Image.debian_slim( # start from basic Linux with Python
python_version="3.10"
)
.apt_install("git") # install git to clone ComfyUI
.pip_install("fastapi[standard]==0.115.4") # install web dependencies
.pip_install("comfy-cli==1.3.5") # install comfy-cli
.pip_install("cos-python-sdk-v5")
.pip_install("sqlalchemy")
.pip_install("ultralytics")
.pip_install("tencentcloud-sdk-python")
.pip_install("pymysql")
.pip_install("Pillow")
.pip_install("ffmpy")
.pip_install("opencv-python")
.run_commands( # use comfy-cli to install ComfyUI and its dependencies
"comfy --skip-prompt install --nvidia --version 0.3.10"
).add_local_file("snapshot.json","/root/snapshot.json", copy=True)
)
image = (
image.run_commands("comfy node install https://github.com/M1kep/ComfyLiterals")
.run_commands("comfy node install https://github.com/evanspearman/ComfyMath")
.run_commands("comfy node install https://github.com/Kosinkadink/ComfyUI-AnimateDiff-Evolved")
.run_commands("comfy node install https://e.coding.net/g-ldyi2063/dev/ComfyUI-Bowong.git")
.run_commands("comfy node install https://github.com/crystian/ComfyUI-Crystools")
.run_commands("comfy node install https://github.com/pythongosssss/ComfyUI-Custom-Scripts")
.run_commands("comfy node install https://e.coding.net/g-ldyi2063/dev/ComfyUI-CustomNode.git")
.run_commands("comfy node install https://github.com/BennyKok/comfyui-deploy")
.run_commands("comfy node install https://github.com/yolain/ComfyUI-Easy-Use")
.run_commands("comfy node install https://e.coding.net/g-ldyi2063/dev/ComfyUI-LatentSync-Node.git")
.run_commands("comfy node install https://e.coding.net/g-ldyi2063/dev/ComfyUI-VideoHelperSuite.git")
.run_commands("comfy node install https://e.coding.net/g-ldyi2063/dev/CosyVoice-ComfyUI.git")
.run_commands("comfy node install https://github.com/jags111/efficiency-nodes-comfyui")
.run_commands("comfy node install https://github.com/WASasquatch/was-node-suite-comfyui")
.run_commands("comfy node install https://github.com/rgthree/rgthree-comfy")
.run_commands("comfy node install https://github.com/cubiq/ComfyUI_essentials")
.run_commands("comfy node install https://github.com/melMass/comfy_mtb")
.run_commands(
"mkdir -p /root/comfy/ComfyUI/models/ComfyUI-CustomNode/model && rm -rf /root/comfy/ComfyUI/custom_nodes/ComfyUI-CustomNode/model && ln -s /root/comfy/ComfyUI/models/ComfyUI-CustomNode/model /root/comfy/ComfyUI/custom_nodes/ComfyUI-CustomNode/model"
).run_commands(
"mkdir -p /root/comfy/ComfyUI/models/ComfyUI-LatentSync-Node/checkpoints && rm -rf /root/comfy/ComfyUI/custom_nodes/ComfyUI-LatentSync-Node/checkpoints && ln -s /root/comfy/ComfyUI/models/ComfyUI-LatentSync-Node/checkpoints /root/comfy/ComfyUI/custom_nodes/ComfyUI-LatentSync-Node/checkpoints"
).run_commands(
"mkdir -p /root/comfy/ComfyUI/models/CosyVoice-ComfyUI/pretrained_models/CosyVoice-300M-SFT && mkdir -p /root/comfy/ComfyUI/custom_nodes/CosyVoice-ComfyUI/pretrained_models/CosyVoice-300M-SFT && rm -rf /root/comfy/ComfyUI/custom_nodes/CosyVoice-ComfyUI/pretrained_models/CosyVoice-300M-SFT && ln -s /root/comfy/ComfyUI/models/CosyVoice-ComfyUI/pretrained_models/CosyVoice-300M-SFT /root/comfy/ComfyUI/custom_nodes/CosyVoice-ComfyUI/pretrained_models/CosyVoice-300M-SFT"
).run_commands(
"mkdir -p /root/comfy/ComfyUI/models/.cache/torch/hub/checkpoints && mkdir -p /root/.cache/torch/hub/checkpoints && rm -rf /root/.cache/torch/hub/checkpoints && ln -s /root/comfy/ComfyUI/models/.cache/torch/hub/checkpoints /root/.cache/torch/hub/checkpoints"
).run_commands(
"mkdir -p /root/comfy/ComfyUI/models/stabilityai && ln -s /root/comfy/ComfyUI/models/stabilityai /root/comfy/ComfyUI/stabilityai"
).run_commands(
"rm -rf /root/comfy/ComfyUI/models"
).run_commands(
"apt update && apt install -y ffmpeg && ffmpeg -version"
).pip_install("av")
# Add .run_commands(...) calls for any other custom nodes you want to download
)
image = image.add_local_file(
"highlight_v0.113_api.json", "/root/workflow_api.json"
)
app = modal.App(name="highlight-comfyui", image=image)
vol = modal.Volume.from_name("comfyui-model", create_if_missing=True)
secret = modal.Secret.from_name("aws-s3-secret")
@app.cls(
allow_concurrent_inputs=1, # allow 10 concurrent API calls
concurrency_limit=50,
container_idle_timeout=120, # 5 minute container keep alive after it processes an input; increasing this value is a great way to reduce ComfyUI cold start times
timeout=800,
gpu="T4",
secrets=[secret],
volumes={
"/root/comfy/ComfyUI/models": vol,
# "/root/comfy/ComfyUI/input": modal.CloudBucketMount(
# bucket_name=os.environ["BUCKET_INPUT"],
# # bucket_endpoint_url="https://s3.%s.amazonaws.com" % os.environ["AWS_REGION"],
# secret=secret,
# key_prefix="comfyui"
# ),
# "/root/comfy/ComfyUI/output": modal.CloudBucketMount(
# bucket_name=os.environ["BUCKET_OUTPUT"],
# # bucket_endpoint_url="https://s3.%s.amazonaws.com" % os.environ["AWS_REGION"],
# secret=secret,
# key_prefix="comfyui"
# ),
},
)
class ComfyUI:
@modal.enter()
def launch_comfy_background(self):
# starts the ComfyUI server in the background exactly once when the first input is received
cmd = "comfy launch --background"
subprocess.run(cmd, shell=True, check=True)
@modal.method()
def infer(self, workflow_json: str = ""):
# runs the comfy run --workflow command as a subprocess
session_id = str(uuid.uuid4())
with open(f"/root/{session_id}.json", "w", encoding="utf-8") as f:
f.write(workflow_json)
cmd = f"comfy run --workflow /root/{session_id}.json --wait --timeout 1200"
# TODO 接收返回值
subprocess.run(cmd, shell=True, check=True)
# completed workflows write output images to this directory
output_dir = "/root/comfy/ComfyUI/output"
# looks up the name of the output image file based on the workflow
workflow = json.loads(workflow_json)
file_prefix = [
node.get("inputs")
for node in workflow.values()
if node.get("class_type") == "VHS_VideoCombine"
][0]["filename_prefix"]
# returns the image as bytes
file_list = os.listdir(output_dir)
# 获取按照文件时间创建排序的列表,默认是按时间升序
new_file_list = sorted(file_list, key=lambda file: os.path.getctime(os.path.join(output_dir, file)), reverse=True)
print("file_list", new_file_list)
for f in new_file_list:
if f.startswith(file_prefix):
return f
@modal.web_endpoint(method="POST")
def api(self, item: Dict):
from fastapi import Response
# save this updated workflow to a new file
new_workflow_file = item["prompt"]
# run inference on the currently running container
fname = self.infer.local(new_workflow_file)
j = {
"file_name": fname
}
print("json", j)
return Response(content=json.dumps(j), media_type="application/json")

284
server_pure_heygem.py Normal file
View File

@@ -0,0 +1,284 @@
# HeyGem模板
import json
import os
import shutil
import socket
import subprocess
import time
import traceback
import uuid
from pathlib import Path
from typing import Any
import httpx
import loguru
import modal
from fastapi import Depends, HTTPException, status, UploadFile
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from fastapi.responses import Response, FileResponse
image = (
modal.Image.debian_slim( # start from basic Linux with Python
python_version="3.10"
).apt_install("git")
.apt_install("gcc")
.pip_install("loguru")
.pip_install("fastapi[standard]")
.run_commands(
"apt update && apt install -y ffmpeg && ffmpeg -version"
)
# 添加Python3.8 HeyGem
.run_commands("apt update && apt install -y curl build-essential libssl-dev zlib1g-dev libncurses5-dev libncursesw5-dev libreadline-dev libsqlite3-dev libgdbm-dev libdb5.3-dev libbz2-dev libexpat1-dev lzma liblzma-dev tk-dev libffi-dev")
.run_commands("curl -O https://www.python.org/ftp/python/3.8.12/Python-3.8.12.tar.xz&&tar -xf Python-3.8.12.tar.xz")
.run_commands("cd Python-3.8.12 && ./configure --enable-optimizations && make -j 10 && make altinstall")
.add_local_file("heygem-1.0-py3-none-any.whl","/root/heygem-1.0-py3-none-any.whl", copy=True)
.shell(["/bin/bash", "-c"])
.run_commands("python3.8 -m pip install /root/heygem-1.0-py3-none-any.whl")
.env({"LD_LIBRARY_PATH":"/usr/local/lib/python3.8/site-packages/nvidia/cuda_nvrtc/lib"})
.run_commands("ln -s /usr/local/lib/python3.8/site-packages/nvidia/cuda_nvrtc/lib/libnvrtc.so.11.2 /usr/local/lib/python3.8/site-packages/nvidia/cuda_nvrtc/lib/libnvrtc.so")
.run_commands("python3.8 -m pip install https://github.com/pydata/numexpr/releases/download/v2.8.6/numexpr-2.8.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl")
.add_local_file("heygem.py", "/root/heygem.py", copy=True)
.add_local_file("config.yaml", "/root/config.yaml", copy=True)
.workdir("/root").pip_install("boto3").pip_install("PyYAML")
)
app = modal.App(name="heygem", image=image)
secret = modal.Secret.from_name("aws-s3-secret")
auth_scheme = HTTPBearer()
bucket_output = "bw-heygem-output"
@app.cls(
allow_concurrent_inputs=1, # required for UI startup process which runs several API calls concurrently
max_containers=25, # limit interactive session to 1 container
gpu="T4", # good starter GPU for inference
cpu=(2,32),
memory=(6144, 32768),
timeout=2160,
scaledown_window=240,
secrets=[secret, modal.Secret.from_name("web_auth_token")],
volumes={
"/code/data/final": modal.CloudBucketMount(
bucket_name=bucket_output,
# bucket_endpoint_url="https://s3.%s.amazonaws.com" % os.environ["AWS_REGION"],
secret=secret,
key_prefix="/"
),
"/root/logs": modal.CloudBucketMount(
bucket_name=bucket_output,
# bucket_endpoint_url="https://s3.%s.amazonaws.com" % os.environ["AWS_REGION"],
secret=secret,
key_prefix="/logs/"
)
}, # mounts our cached models
)
class HeyGem:
@modal.enter()
def start(self):
def check_port_in_use(port, host='127.0.0.1'):
s = None
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(1)
s.connect((host, int(port)))
return True
except socket.error:
return False
finally:
if s:
s.close()
uid = str(uuid.uuid4())
cmd = "echo client_id: %s && nohup python3.8 /root/heygem.py >> /root/logs/%s.log 2>&1 &" % (uid, uid)
subprocess.run(cmd, shell=True, check=True)
timeout = 30
while timeout > 0:
check = check_port_in_use(8383)
if check:
loguru.logger.success("HeyGem Server started on port 8383")
break
else:
timeout -= 1
time.sleep(2)
if timeout == 0:
raise TimeoutError("HeyGem Server Start timed out")
@modal.method()
def infer(self, code, video_path, audio_path) -> str:
def task_submit(uid, video, audio, heygem_url) -> dict[str, Any]:
"""Submit a task to the API"""
task_submit_api = f'{heygem_url}/easy/submit'
result_json = {
'status': False, 'data': {}, 'msg': ''
}
try:
data = {
"code": uid,
"video_url": video,
"audio_url": audio,
"chaofen": 1,
"watermark_switch": 0,
"pn": 1
}
loguru.logger.info(f'data={data}')
with httpx.Client() as client:
resp = client.post(task_submit_api, json=data)
resp_dict = resp.json()
loguru.logger.info(f'submit data: {resp_dict}')
if resp_dict['code'] != 10000:
result_json['status'] = False
result_json['msg'] = result_json['msg']
else:
result_json['status'] = True
result_json['data'] = uid
result_json['msg'] = '任务提交成功'
return result_json
except Exception as e:
loguru.logger.info(f'submit task fail case by:{str(e)}')
raise RuntimeError(str(e))
def query_task_progress(heygem_url, heygem_temp_path, task_id: str, interval: int = 10, timeout: int = 60 * 35) -> dict[str, Any]:
"""Query task progress and wait for completion"""
result_json = {'status': False, 'data': {}, 'msg': ''}
def query_result(t_id: str):
tmp_dict = {'status': True, 'data': dict(), 'msg': ''}
try:
query_task_url = f'{heygem_url}/easy/query'
params = {
'code': t_id
}
with httpx.Client() as client:
resp = client.get(query_task_url, params=params)
resp_dict = resp.json()
loguru.logger.info(f'query task data: {json.dumps(resp_dict)}')
status_code = resp_dict['code']
if status_code in (9999, 10002, 10003, 10001):
tmp_dict['status'] = False
tmp_dict['msg'] = resp_dict['msg']
elif status_code == 10000:
status_code = resp_dict['data'].get('status', 1)
if status_code == 3:
tmp_dict['status'] = False
tmp_dict['msg'] = resp_dict['data']['msg']
else:
process = resp_dict['data'].get('progress', 20)
if status_code == 2:
process = 100
else:
process = process
result = resp_dict['data'].get('result', '')
tmp_dict['data'] = {'progress': process,
'path': result,
}
else:
pass
except Exception as e:
loguru.logger.info(f'query task fail case by:{str(e)}')
raise RuntimeError(str(e))
return tmp_dict
end = time.time() + timeout
while time.time() < end:
tmp_result = query_result(task_id)
if not tmp_result['status'] or tmp_result['data'].__eq__({}):
result_json['status'] = False
result_json['msg'] = tmp_result['msg']
break
else:
process = tmp_result['data']['progress']
loguru.logger.info(f'query task progress :{process}')
if tmp_result['data']['progress'] < 100:
time.sleep(interval)
loguru.logger.info(f'wait next interval:{interval}')
else:
p = tmp_result['data']['path']
p = p.replace('/', '').replace('\\', '')
result_json['data'] = "%s/%s" % (heygem_temp_path, p)
result_json['status'] = True
break
return result_json
try:
heygem_url = "http://127.0.0.1:8383"
heygem_temp_path = "/code/data/temp"
submit_result = task_submit(code, video_path, audio_path, heygem_url)
if not submit_result['status']:
raise Exception(f"Task submission failed: {submit_result['msg']}")
task_id = submit_result['data']
loguru.logger.info(f'Submitted task: {task_id}')
# Query task progress
progress_result = query_task_progress(heygem_url, heygem_temp_path, task_id, interval=5)
if not progress_result['status']:
raise RuntimeError(f"Task processing failed: {progress_result['msg']}")
# Return the file for download
file_path = progress_result['data']
if not os.path.exists(file_path):
raise FileNotFoundError(f"Output file not found at {file_path}")
return file_path
except Exception as e:
loguru.logger.error(f"Error processing request || {str(e)}")
raise Exception(str(e))
finally:
try:
os.remove(video_path)
os.remove(audio_path)
except:
pass
@modal.fastapi_endpoint(method="POST")
async def api(self, video_file: UploadFile, audio_file: UploadFile, token: HTTPAuthorizationCredentials = Depends(auth_scheme)) -> Response:
if token.credentials != os.environ["AUTH_TOKEN"]:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect bearer token",
headers={"WWW-Authenticate": "Bearer"},
)
code = str(uuid.uuid4())
try:
video_path = "/root/%s" % video_file.filename
with open(video_path, "wb") as f:
data = await video_file.read()
f.write(data)
audio_path = "/root/%s" % audio_file.filename
with open(audio_path, "wb") as f:
data = await audio_file.read()
f.write(data)
except Exception as e:
return Response({"status": "fail", "msg": f"video or audio file save failed: {str(e)}"})
loguru.logger.info(f"Enter Inference Module")
try:
file_path = self.infer.local(code, video_path, audio_path)
try:
shutil.copy(file_path, os.path.join("/code/data/final",file_path.split(os.path.sep)[-1]))
except Exception as e:
loguru.logger.info(f"Moving {file_path} to S3 Failed: {str(e)}")
try:
print("Try move file to S3 manually")
# S3 Fallback
import boto3
import yaml
with open("/root/config.yaml", encoding="utf-8",
mode="r+") as config:
yaml_config = yaml.load(config, Loader=yaml.FullLoader)
awss3 = boto3.resource('s3', aws_access_key_id=yaml_config["aws_key_id"],
aws_secret_access_key=yaml_config["aws_access_key"])
awss3.meta.client.upload_file(file_path, bucket_output, file_path.split(os.path.sep)[-1])
except:
raise Exception("Failed to move file to S3 manually")
resp = FileResponse(path=file_path, filename=Path(file_path).name, status_code=200, headers={
"Content-Type": "application/octet-stream",
"Content-Disposition": f"attachment; filename={Path(file_path).name}",
})
return resp
except Exception as e:
traceback.print_exc()
return Response({"status": "fail", "msg": "Inference module failed: "+str(e)})

View File

@@ -1,11 +1,9 @@
# ComfyUI模板--Base 停止维护!!
import json import json
import os import os
import shutil import shutil
import subprocess import subprocess
import sys
import traceback
import uuid import uuid
from pathlib import Path
from typing import Dict from typing import Dict
import modal import modal

View File

@@ -1,11 +1,9 @@
# ComfyUI模板--Base Auth
import json import json
import os import os
import shutil import shutil
import subprocess import subprocess
import sys
import traceback
import uuid import uuid
from pathlib import Path
from typing import Dict from typing import Dict
import modal import modal

View File

@@ -1,11 +1,9 @@
# ComfyUI模板--Base Auth Heygem
import json import json
import os import os
import shutil import shutil
import subprocess import subprocess
import sys
import traceback
import uuid import uuid
from pathlib import Path
from typing import Dict from typing import Dict
import modal import modal
@@ -13,14 +11,14 @@ from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
image = ( # build up a Modal Image to run ComfyUI, step by step image = ( # build up a Modal Image to run ComfyUI, step by step
modal.Image.from_registry( # start from basic Linux with Python modal.Image.debian_slim( # start from basic Linux with Python
"ubuntu/python:3.12-24.04_stable" python_version="3.10"
).apt_install("python3.8-dev") )
.apt_install("git") # install git to clone ComfyUI .apt_install("git") # install git to clone ComfyUI
.apt_install("gcc") .apt_install("gcc")
.apt_install("libportaudio2") .apt_install("libportaudio2")
.pip_install("fastapi[standard]==0.115.4") # install web dependencies .pip_install("fastapi[standard]==0.115.4") # install web dependencies
.pip_install("comfy-cli==1.3.8") # install comfy-cli .pip_install("comfy-cli==1.3.5") # install comfy-cli
.pip_install("cos-python-sdk-v5") .pip_install("cos-python-sdk-v5")
.pip_install("sqlalchemy") .pip_install("sqlalchemy")
.pip_install("ultralytics") .pip_install("ultralytics")
@@ -56,7 +54,7 @@ image = (
.run_commands("comfy node install https://github.com/cubiq/ComfyUI_essentials") .run_commands("comfy node install https://github.com/cubiq/ComfyUI_essentials")
.run_commands("comfy node install https://github.com/melMass/comfy_mtb") .run_commands("comfy node install https://github.com/melMass/comfy_mtb")
.run_commands("echo 3 && comfy node install https://e.coding.net/g-ldyi2063/dev/ComfyUI_SparkTTS.git") .run_commands("echo 3 && comfy node install https://e.coding.net/g-ldyi2063/dev/ComfyUI_SparkTTS.git")
.run_commands("echo 1 && comfy node install https://e.coding.net/g-ldyi2063/dev/ComfyUI-CustomNode.git") .run_commands("echo 4 && comfy node install https://e.coding.net/g-ldyi2063/dev/ComfyUI-CustomNode.git")
.run_commands("comfy node install https://e.coding.net/g-ldyi2063/dev/cosyvoice_comfyui.git") .run_commands("comfy node install https://e.coding.net/g-ldyi2063/dev/cosyvoice_comfyui.git")
.run_commands("echo 4 && comfy node install https://e.coding.net/g-ldyi2063/dev/ComfyUI-LatentSync-Node.git") .run_commands("echo 4 && comfy node install https://e.coding.net/g-ldyi2063/dev/ComfyUI-LatentSync-Node.git")
.run_commands( .run_commands(
@@ -75,9 +73,17 @@ image = (
"apt update && apt install -y ffmpeg && ffmpeg -version" "apt update && apt install -y ffmpeg && ffmpeg -version"
).add_local_file("config.yaml", "/root/comfy/ComfyUI/custom_nodes/ComfyUI-CustomNode/config.yaml", copy=True ).add_local_file("config.yaml", "/root/comfy/ComfyUI/custom_nodes/ComfyUI-CustomNode/config.yaml", copy=True
).add_local_file("config.py", "/root/comfy/ComfyUI/custom_nodes/cosyvoice_comfyui/pretrained_models/tools/config.py", copy=True ).add_local_file("config.py", "/root/comfy/ComfyUI/custom_nodes/cosyvoice_comfyui/pretrained_models/tools/config.py", copy=True
).add_local_file("heygem-1.0-py3-none-any.whl","/root/comfy/heygem-1.0-py3-none-any.whl", copy=True ) # 添加Python3.8 HeyGem
).add_local_file("heygem.py", "/root/comfy/heygem.py", copy=True .run_commands("apt update && apt install -y curl build-essential libssl-dev zlib1g-dev libncurses5-dev libncursesw5-dev libreadline-dev libsqlite3-dev libgdbm-dev libdb5.3-dev libbz2-dev libexpat1-dev lzma liblzma-dev tk-dev libffi-dev")
).pip_install("/root/comfy/heygem-1.0-py3-none-any.whl") .run_commands("curl -O https://www.python.org/ftp/python/3.8.12/Python-3.8.12.tar.xz&&tar -xf Python-3.8.12.tar.xz")
.run_commands("cd Python-3.8.12 && ./configure --enable-optimizations && make -j 10 && make altinstall")
.add_local_file("heygem-1.0-py3-none-any.whl","/root/comfy/heygem-1.0-py3-none-any.whl", copy=True)
.shell(["/bin/bash", "-c"])
.run_commands("python3.8 -m pip install /root/comfy/heygem-1.0-py3-none-any.whl")
.env({"LD_LIBRARY_PATH":"/usr/local/lib/python3.8/site-packages/nvidia/cuda_nvrtc/lib"})
.run_commands("ln -s /usr/local/lib/python3.8/site-packages/nvidia/cuda_nvrtc/lib/libnvrtc.so.11.2 /usr/local/lib/python3.8/site-packages/nvidia/cuda_nvrtc/lib/libnvrtc.so")
.run_commands("python3.8 -m pip install https://github.com/pydata/numexpr/releases/download/v2.8.6/numexpr-2.8.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl")
.add_local_file("heygem.py", "/root/comfy/heygem.py", copy=True)
.workdir("/root/comfy") .workdir("/root/comfy")
# Add .run_commands(...) calls for any other custom nodes you want to download # Add .run_commands(...) calls for any other custom nodes you want to download
@@ -101,13 +107,12 @@ auth_scheme = HTTPBearer()
buffer_containers=0, buffer_containers=0,
scaledown_window=120, scaledown_window=120,
# 5 minute container keep alive after it processes an input; increasing this value is a great way to reduce ComfyUI cold start times # 5 minute container keep alive after it processes an input; increasing this value is a great way to reduce ComfyUI cold start times
timeout=900, timeout=1200,
gpu=["L4", "T4"], gpu=["L4", "T4"],
cpu=(2,16), cpu=(2,16),
# memory=(32768, 32768), # (内存预留量, 内存使用上限) # memory=(32768, 32768), # (内存预留量, 内存使用上限)
memory=(20480,40960), memory=(20480,81920),
retries=1, enable_memory_snapshot=False,
enable_memory_snapshot=True,
secrets=[secret, modal.Secret.from_name("web_auth_token")], secrets=[secret, modal.Secret.from_name("web_auth_token")],
volumes={ volumes={
"/root/comfy/ComfyUI/models": vol, "/root/comfy/ComfyUI/models": vol,
@@ -134,8 +139,10 @@ class ComfyUI:
" && rm -rf /root/comfy/ComfyUI/user" " && rm -rf /root/comfy/ComfyUI/user"
" && mkdir -p /root/comfy/ComfyUI/output_s3/logs/{}" " && mkdir -p /root/comfy/ComfyUI/output_s3/logs/{}"
" && ln -s /root/comfy/ComfyUI/output_s3/logs/{} /root/comfy/ComfyUI/user" " && ln -s /root/comfy/ComfyUI/output_s3/logs/{} /root/comfy/ComfyUI/user"
" && comfy launch --background" " && nohup python3.8 /root/comfy/heygem.py >> /root/comfy/ComfyUI/output_s3/logs/{}/heygem.log 2>&1 &"
" && nohup python heygem.py >> /root/comfy/ComfyUI/output_s3/logs/{}/heygem.log 2>&1 &").format(self.session_id,self.session_id, self.session_id, self.session_id) ).format(self.session_id,self.session_id, self.session_id, self.session_id)
subprocess.run(cmd, shell=True, check=True)
cmd = "comfy launch --background"
subprocess.run(cmd, shell=True, check=True) subprocess.run(cmd, shell=True, check=True)
@modal.method() @modal.method()
@@ -170,41 +177,78 @@ class ComfyUI:
node["inputs"]["default_value"] = node["inputs"]["default_value"].replace("input_s3", "input") node["inputs"]["default_value"] = node["inputs"]["default_value"].replace("input_s3", "input")
with open(f"/root/{self.prompt_uuid}.json", "w", encoding="utf-8") as fi: with open(f"/root/{self.prompt_uuid}.json", "w", encoding="utf-8") as fi:
fi.write(json.dumps(workflow, ensure_ascii=False)) fi.write(json.dumps(workflow, ensure_ascii=False))
cmd = f"comfy run --workflow /root/{self.prompt_uuid}.json --wait --timeout 890 --verbose" cmd = f"comfy run --workflow /root/{self.prompt_uuid}.json --wait --timeout 1190 --verbose"
subprocess.run(cmd, shell=True, check=True, timeout=895) subprocess.run(cmd, shell=True, check=True, timeout=1195)
# looks up the name of the output image file based on the workflow # looks up the name of the output image file based on the workflow
file_prefix = [ if "VHS_VideoCombine" in workflow_json:
node.get("inputs") file_prefix = [
for node in workflow.values() node.get("inputs")
if node.get("class_type") == "VHS_VideoCombine" for node in workflow.values()
][0]["filename_prefix"] if node.get("class_type") == "VHS_VideoCombine"
][0]["filename_prefix"]
# returns the image as bytes # returns the image as bytes
file_list = os.listdir(output_dir) file_list = os.listdir(output_dir)
# 获取按照文件时间创建排序的列表,默认是按时间升序 # 获取按照文件时间创建排序的列表,默认是按时间升序
new_file_list = sorted(file_list, key=lambda file: os.path.getctime(os.path.join(output_dir, file)), new_file_list = sorted(file_list, key=lambda file: os.path.getctime(os.path.join(output_dir, file)),
reverse=True) reverse=True)
# print("file_list", new_file_list) # print("file_list", new_file_list)
for f in new_file_list: for f in new_file_list:
if f.startswith(file_prefix): if f.startswith(file_prefix):
os.makedirs(os.path.dirname(os.path.join(output_dir.replace("output", "output_s3"), f)), exist_ok=True) os.makedirs(os.path.dirname(os.path.join(output_dir.replace("output", "output_s3"), f)), exist_ok=True)
try:
shutil.copy(os.path.join(output_dir, f), os.path.join(output_dir.replace("output", "output_s3"), f))
except:
try:
print("Try move file to S3 manually")
# S3 Fallback
import boto3
import yaml
with open("/root/comfy/ComfyUI/custom_nodes/ComfyUI-CustomNode/config.yaml", encoding="utf-8",mode="r+") as config:
yaml_config = yaml.load(config, Loader=yaml.FullLoader)
awss3 = boto3.resource('s3', aws_access_key_id=yaml_config["aws_key_id"],
aws_secret_access_key=yaml_config["aws_access_key"])
awss3.meta.client.upload_file(os.path.join(output_dir, f), bucket_output, f)
except:
raise Exception("Failed to move file to S3 manually")
return f
elif "HeyGemF2F" in workflow_json or "HeyGemF2FFromFile" in workflow_json:
heygem_dir = [
node.get("inputs")
for node in workflow.values()
if node.get("class_type") == "HeyGemF2F" or node.get("class_type") == "HeyGemF2FFromFile"
][0]["heygem_temp_path"]
# returns the image as bytes
file_list = os.listdir(heygem_dir)
# 获取按照文件时间创建排序的列表,默认是按时间升序
new_file_list = sorted(file_list, key=lambda file: os.path.getctime(os.path.join(heygem_dir, file)),
reverse=True)
if len(new_file_list) > 0:
try: try:
shutil.copy(os.path.join(output_dir, f), os.path.join(output_dir.replace("output", "output_s3"), f)) shutil.copy(os.path.join(heygem_dir, new_file_list[0]), os.path.join(output_dir.replace("output", "output_s3"), new_file_list[0]))
except: except:
try: try:
print("Try move file to S3 manually") print("Try move file to S3 manually")
# S3 Fallback # S3 Fallback
import boto3 import boto3
import yaml import yaml
with open("/root/comfy/ComfyUI/custom_nodes/ComfyUI-CustomNode/config.yaml", encoding="utf-8",mode="r+") as config: with open("/root/comfy/ComfyUI/custom_nodes/ComfyUI-CustomNode/config.yaml", encoding="utf-8",
mode="r+") as config:
yaml_config = yaml.load(config, Loader=yaml.FullLoader) yaml_config = yaml.load(config, Loader=yaml.FullLoader)
awss3 = boto3.resource('s3', aws_access_key_id=yaml_config["aws_key_id"], awss3 = boto3.resource('s3', aws_access_key_id=yaml_config["aws_key_id"],
aws_secret_access_key=yaml_config["aws_access_key"]) aws_secret_access_key=yaml_config["aws_access_key"])
awss3.meta.client.upload_file(os.path.join(output_dir, f), bucket_output, f) awss3.meta.client.upload_file(os.path.join(heygem_dir, new_file_list[0]), bucket_output, new_file_list[0])
except: except:
raise Exception("Failed to move file to S3 manually") raise Exception("Failed to move HeyGem file to S3 manually")
return f return new_file_list[0]
else:
raise FileNotFoundError("HeyGem generation file not found")
else:
raise Exception("Workflow does not contain VHS_VideoCombine/HeygemF2F node, cannot find output file")
@modal.fastapi_endpoint(method="POST") @modal.fastapi_endpoint(method="POST")
def api(self, item: Dict, token: HTTPAuthorizationCredentials = Depends(auth_scheme)): def api(self, item: Dict, token: HTTPAuthorizationCredentials = Depends(auth_scheme)):