342 lines
12 KiB
Python
342 lines
12 KiB
Python
import os
|
|
import sys
|
|
import json
|
|
from pathlib import Path
|
|
|
|
# 基础导入
|
|
import time
|
|
import base64
|
|
import requests
|
|
import torch
|
|
import numpy as np
|
|
from PIL import Image
|
|
|
|
# 直接从constants导入所需变量
|
|
from .constants import comfy_root, get_project_name, get_project_category
|
|
|
|
# 移除动态导入相关代码
|
|
try:
|
|
from .utils.image_processor import tensor_to_pil
|
|
from .utils.github_client import GitHubUploader
|
|
print("✅ utils模块已加载")
|
|
except ImportError as e:
|
|
print(f"❌ 导入失败:{str(e)}")
|
|
raise
|
|
|
|
# 获取当前文件的绝对路径
|
|
current_file = Path(__file__).resolve()
|
|
package_dir = current_file.parent
|
|
|
|
print(f"📂 当前文件:{current_file}")
|
|
print(f"📁 包目录:{package_dir}")
|
|
|
|
# 添加包路径
|
|
if str(package_dir) not in sys.path:
|
|
sys.path.insert(0, str(package_dir))
|
|
|
|
# 强制刷新模块缓存
|
|
if 'Comfyui_wan_api' in sys.modules:
|
|
del sys.modules['Comfyui_wan_api']
|
|
if 'Comfyui_wan_api.utils' in sys.modules:
|
|
del sys.modules['Comfyui_wan_api.utils']
|
|
|
|
# 使用精确的绝对导入
|
|
try:
|
|
from .utils.image_processor import tensor_to_pil
|
|
from .utils.github_client import GitHubUploader
|
|
print("✅ 模块导入成功")
|
|
except ImportError as e:
|
|
print(f"❌ 导入失败:{str(e)}")
|
|
print(f"当前系统路径:{sys.path}")
|
|
raise
|
|
|
|
# 配置文件路径
|
|
config_file = os.path.join(os.path.dirname(__file__), 'config.json')
|
|
|
|
class WanAPIImageUploader:
|
|
"""WanAPI图片上传节点"""
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
return {
|
|
"required": {
|
|
"images": ("IMAGE",),
|
|
"upload_method": (["GitHub", "ModelScope"],),
|
|
},
|
|
"optional": {
|
|
"github_repo": ("STRING", {"default": ""}),
|
|
"github_branch": ("STRING", {"default": "main"}),
|
|
"github_token": ("STRING", {"default": ""})
|
|
}
|
|
}
|
|
|
|
RETURN_TYPES = ("STRING",)
|
|
FUNCTION = "upload_image"
|
|
CATEGORY = "WanAPI"
|
|
|
|
def __init__(self):
|
|
try:
|
|
# 直接导入所需函数和类
|
|
from .utils.image_processor import tensor_to_pil
|
|
from .utils.github_client import GitHubUploader
|
|
self.tensor_to_pil = tensor_to_pil
|
|
self.github_uploader = GitHubUploader
|
|
|
|
# 读取配置文件
|
|
self.config = self._load_config()
|
|
print("✅ 模块导入成功")
|
|
print(f"📝 当前配置:{self.config}")
|
|
except ImportError as e:
|
|
print(f"❌ 导入失败:{str(e)}")
|
|
raise
|
|
|
|
def _load_config(self):
|
|
"""加载配置文件"""
|
|
config_path = os.path.join(os.path.dirname(__file__), 'config.json')
|
|
try:
|
|
with open(config_path, 'r', encoding='utf-8') as f:
|
|
return json.load(f)
|
|
except Exception as e:
|
|
print(f"⚠️ 配置文件加载失败:{str(e)}")
|
|
return {}
|
|
|
|
def upload_image(self, images, upload_method="GitHub", github_repo="", github_branch="main", github_token=""):
|
|
"""上传图片"""
|
|
try:
|
|
print("\n=== 开始处理图片 ===")
|
|
print(f"📊 输入图片类型:{type(images)}")
|
|
|
|
# 处理批次维度
|
|
if len(images.shape) == 4:
|
|
print(f"📊 输入张量形状:{images.shape}, 类型:{images.dtype}")
|
|
print("⚙️ 处理批次维度")
|
|
images = images[0]
|
|
|
|
# 处理通道维度
|
|
if images.shape[2] != 3:
|
|
print("⚙️ 处理通道维度")
|
|
images = images.permute(2, 0, 1)
|
|
|
|
# 转换为numpy数组
|
|
print("⚙️ 转换为numpy数组")
|
|
images = images.cpu().numpy()
|
|
images = (images * 255).astype(np.uint8)
|
|
print(f"📊 numpy数组形状:{images.shape}, 类型:{images.dtype}")
|
|
|
|
# 创建PIL图像
|
|
print("⚙️ 创建PIL图像")
|
|
pil_image = Image.fromarray(images)
|
|
print(f"📊 PIL图像大小:{pil_image.size}, 模式:{pil_image.mode}")
|
|
|
|
# 创建临时目录
|
|
temp_dir = os.path.join(os.path.dirname(__file__), 'temp')
|
|
os.makedirs(temp_dir, exist_ok=True)
|
|
|
|
# 保存临时文件
|
|
temp_path = os.path.join(temp_dir, f"upload_{int(time.time())}.jpg")
|
|
print(f"💾 保存临时文件:{temp_path}")
|
|
pil_image.save(temp_path, quality=95)
|
|
|
|
# 选择上传方式
|
|
if upload_method == "GitHub":
|
|
# 优先使用参数值,然后是配置文件值
|
|
repo = github_repo or self.config.get("GITHUB_REPO", "")
|
|
branch = github_branch or self.config.get("GITHUB_BRANCH", "main")
|
|
token = github_token or self.config.get("GITHUB_TOKEN", "")
|
|
|
|
print(f"🔧 GitHub配置:repo={repo}, branch={branch}, token={'已设置' if token else '未设置'}")
|
|
|
|
if not repo or not token:
|
|
raise ValueError(f"GitHub配置不完整:repo={repo}, token={'已设置' if token else '未设置'}")
|
|
|
|
print("⚙️ 初始化GitHub上传器")
|
|
uploader = self.github_uploader(repo=repo, branch=branch, token=token)
|
|
print("⚙️ 开始上传文件")
|
|
url = uploader.upload(temp_path)
|
|
|
|
# 清理临时文件
|
|
try:
|
|
os.remove(temp_path)
|
|
print("🧹 清理临时文件成功")
|
|
except Exception as e:
|
|
print(f"⚠️ 清理临时文件失败:{str(e)}")
|
|
|
|
if url:
|
|
print(f"✅ 上传成功:{url}")
|
|
return (url,)
|
|
else:
|
|
raise ValueError("上传失败")
|
|
else:
|
|
raise ValueError("暂不支持的上传方式")
|
|
|
|
except Exception as e:
|
|
print(f"❌ 上传失败:{str(e)}")
|
|
import traceback
|
|
print(f"🔍 详细错误:\n{traceback.format_exc()}")
|
|
return ("",)
|
|
|
|
def upload_to_github(self, image_path, repo, branch, token):
|
|
"""GitHub上传实现"""
|
|
try:
|
|
# 添加权限验证
|
|
headers = {
|
|
"Authorization": f"token {token}",
|
|
"Accept": "application/vnd.github.v3+json"
|
|
}
|
|
|
|
# 验证Token权限
|
|
auth_check = requests.get(
|
|
"https://api.github.com/user",
|
|
headers=headers,
|
|
timeout=5
|
|
)
|
|
|
|
if auth_check.status_code != 200:
|
|
print(f"❌ Token权限验证失败: {auth_check.json().get('message')}")
|
|
return None
|
|
|
|
# 检查是否有写权限
|
|
repo_info = requests.get(
|
|
f"https://api.github.com/repos/{repo}",
|
|
headers=headers
|
|
)
|
|
|
|
if repo_info.status_code != 200:
|
|
print(f"❌ 仓库访问失败: {repo_info.json().get('message')}")
|
|
return None
|
|
|
|
filename = f"wanx_{int(time.time())}.jpg"
|
|
with open(image_path, "rb") as f:
|
|
content = base64.b64encode(f.read()).decode()
|
|
|
|
response = requests.put(
|
|
f"https://api.github.com/repos/{repo}/contents/images/{filename}",
|
|
json={
|
|
"message": "Auto-upload from ComfyUI",
|
|
"content": content,
|
|
"branch": branch
|
|
},
|
|
headers=headers,
|
|
timeout=10
|
|
)
|
|
|
|
if response.status_code == 201:
|
|
return f"https://raw.githubusercontent.com/{repo}/{branch}/images/{filename}"
|
|
elif response.status_code == 403:
|
|
error_msg = response.json().get('message', '')
|
|
if 'resource is not accessible' in error_msg:
|
|
print("""
|
|
❌ 权限不足,请检查:
|
|
1. Token是否具有repo读写权限
|
|
2. 仓库是否设置为私有但未授权
|
|
3. Token是否已过期
|
|
""")
|
|
return None
|
|
return None
|
|
except Exception as e:
|
|
print(f"GitHub上传失败: {str(e)}")
|
|
return None
|
|
|
|
def upload_to_modelscope(self, image_path, api_key):
|
|
"""ModelScope上传实现"""
|
|
try:
|
|
api = HubApi()
|
|
api.login(api_key)
|
|
|
|
upload_result = api.upload_file(
|
|
file_path=image_path,
|
|
repo_type="dataset",
|
|
repo_name="temp_uploads",
|
|
revision=DEFAULT_MODEL_REVISION
|
|
)
|
|
|
|
return upload_result['url']
|
|
except Exception as e:
|
|
print(f"ModelScope上传失败: {str(e)}")
|
|
return None
|
|
|
|
def upload(self, image, upload_method, github_repo="", github_branch=""):
|
|
try:
|
|
# 处理图像
|
|
pil_image = self.tensor_to_pil(image)
|
|
if not pil_image:
|
|
return ("",)
|
|
|
|
# 保存临时文件
|
|
temp_dir = os.path.join(comfy_root, "temp_uploads")
|
|
os.makedirs(temp_dir, exist_ok=True)
|
|
temp_path = os.path.join(temp_dir, f"upload_{int(time.time())}.jpg")
|
|
pil_image.save(temp_path, quality=95)
|
|
|
|
# 选择上传方式
|
|
url = ""
|
|
if upload_method == "GitHub":
|
|
uploader = self.github_uploader(
|
|
repo=github_repo or os.getenv("GITHUB_REPO"),
|
|
branch=github_branch or os.getenv("GITHUB_BRANCH"),
|
|
token=os.getenv("GITHUB_TOKEN")
|
|
)
|
|
url = uploader.upload(pil_image)
|
|
else:
|
|
url = self.upload_to_modelscope(temp_path, os.getenv("DASHSCOPE_API_KEY"))
|
|
|
|
# 清理临时文件
|
|
try:
|
|
os.remove(temp_path)
|
|
except:
|
|
pass
|
|
|
|
return (url or "https://cdn.translate.alibaba.com/r/wanx-demo-1.png",)
|
|
|
|
except Exception as e:
|
|
print(f"上传失败: {str(e)}")
|
|
return ("",)
|
|
|
|
# 上传成功后自动清理历史文件
|
|
def clean_old_files(self, repo, branch, token, keep=10):
|
|
# 获取仓库文件列表
|
|
response = requests.get(
|
|
f"https://api.github.com/repos/{repo}/contents/images",
|
|
headers={"Authorization": f"token {token}"}
|
|
)
|
|
|
|
# 保留最新的10个文件
|
|
files = sorted(response.json(), key=lambda x: x['name'], reverse=True)[keep:]
|
|
|
|
# 删除旧文件
|
|
for file in files:
|
|
requests.delete(
|
|
f"https://api.github.com/repos/{repo}/contents/images/{file['name']}",
|
|
json={
|
|
"message": "Auto-clean old files",
|
|
"sha": file['sha'],
|
|
"branch": branch
|
|
},
|
|
headers={"Authorization": f"token {token}"}
|
|
)
|
|
|
|
def check_repo_structure(self, repo, branch, token):
|
|
"""自动创建缺失的images目录"""
|
|
try:
|
|
headers = {"Authorization": f"token {token}"}
|
|
|
|
# 检查images目录是否存在
|
|
check_url = f"https://api.github.com/repos/{repo}/contents/images?ref={branch}"
|
|
response = requests.get(check_url, headers=headers)
|
|
|
|
if response.status_code == 404:
|
|
# 创建images目录
|
|
data = {
|
|
"message": "Create images directory",
|
|
"content": base64.b64encode(b"placeholder").decode(),
|
|
"branch": branch
|
|
}
|
|
requests.put(
|
|
f"https://api.github.com/repos/{repo}/contents/images/.gitkeep",
|
|
headers=headers,
|
|
json=data
|
|
)
|
|
print("✅ 自动创建images目录成功")
|
|
|
|
except Exception as e:
|
|
print(f"目录检查失败: {str(e)}") |