From 3d2b47209bf8640fe2bbd67079d040bce90c469a Mon Sep 17 00:00:00 2001 From: xie236393 <2363939182@qq.com> Date: Wed, 26 Feb 2025 21:42:39 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=A1=B9=E7=9B=AE=E7=9B=AE?= =?UTF-8?q?=E5=BD=95=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/.gitkeep | 0 docs/github_setup.md | 30 +++ utils/.gitkeep | 0 utils/__init__.py | 10 + utils/github_client.py | 91 ++++++++ utils/image_processor.py | 37 ++++ utils/init.py | 7 + workflows/.gitkeep | 0 workflows/wan-api.json | 465 +++++++++++++++++++++++++++++++++++++++ 9 files changed, 640 insertions(+) create mode 100644 docs/.gitkeep create mode 100644 docs/github_setup.md create mode 100644 utils/.gitkeep create mode 100644 utils/__init__.py create mode 100644 utils/github_client.py create mode 100644 utils/image_processor.py create mode 100644 utils/init.py create mode 100644 workflows/.gitkeep create mode 100644 workflows/wan-api.json diff --git a/docs/.gitkeep b/docs/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docs/github_setup.md b/docs/github_setup.md new file mode 100644 index 0000000..0af16d1 --- /dev/null +++ b/docs/github_setup.md @@ -0,0 +1,30 @@ +# GitHub 仓库设置指南 + +## 自动设置(推荐) +1. 双击运行 `setup_github.bat` +2. 按照向导提示操作即可 + +## 手动设置 +如果自动设置失败,您可以: +1. 访问 https://github.com/settings/tokens/new +2. 创建新的访问令牌: + - 名称:WanAPI + - 有效期:永不过期 + - 权限:勾选 repo +3. 复制生成的token +4. 编辑 config.json: + ```json + { + "GITHUB_TOKEN": "你的token", + "GITHUB_REPO": "你的用户名/仓库名", + "GITHUB_BRANCH": "main" + } + ``` + +## 常见问题 +1. Token无效 + - 检查是否正确复制 + - 确保有 repo 权限 +2. 仓库创建失败 + - 检查仓库名是否已存在 + - 确保token权限正确 \ No newline at end of file diff --git a/utils/.gitkeep b/utils/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/utils/__init__.py b/utils/__init__.py new file mode 100644 index 0000000..23fc1cc --- /dev/null +++ b/utils/__init__.py @@ -0,0 +1,10 @@ +# 避免在顶层导入,改为按需导入 +def get_image_processor(): + from .image_processor import tensor_to_pil, validate_image_url + return tensor_to_pil, validate_image_url + +def get_github_client(): + from .github_client import GitHubUploader + return GitHubUploader + +print("✅ utils模块已加载") diff --git a/utils/github_client.py b/utils/github_client.py new file mode 100644 index 0000000..e9cf1f9 --- /dev/null +++ b/utils/github_client.py @@ -0,0 +1,91 @@ +import os +import time +import base64 +import requests +from pathlib import Path + +class GitHubUploader: + def __init__(self, repo, branch, token): + self.repo = repo + self.branch = branch + self.headers = { + "Authorization": f"token {token}", + "Accept": "application/vnd.github.v3+json" + } + + def _validate_permissions(self): + """验证Token权限""" + try: + # 验证Token + auth_check = requests.get( + "https://api.github.com/user", + headers=self.headers, + timeout=5 + ) + + if auth_check.status_code != 200: + print(f"❌ Token验证失败: {auth_check.json().get('message')}") + return False + + # 验证仓库访问权限 + repo_check = requests.get( + f"https://api.github.com/repos/{self.repo}", + headers=self.headers + ) + + if repo_check.status_code != 200: + print(f"❌ 仓库访问失败: {repo_check.json().get('message')}") + return False + + return True + + except Exception as e: + print(f"❌ 权限验证失败: {str(e)}") + return False + + def upload(self, file_path): + """上传文件到GitHub""" + if not self._validate_permissions(): + return None + + try: + # 读取文件内容 + with open(file_path, 'rb') as f: + content = base64.b64encode(f.read()).decode('utf-8') + + # 生成文件名 + filename = f"wanx_{int(time.time())}.jpg" + + # 上传到GitHub + response = requests.put( + f"https://api.github.com/repos/{self.repo}/contents/images/{filename}", + headers=self.headers, + json={ + "message": "Auto-upload from ComfyUI", + "content": content, + "branch": self.branch + }, + timeout=10 + ) + + # 处理响应 + if response.status_code == 201: + # 返回原始 GitHub URL 而不是 CDN URL + return f"https://raw.githubusercontent.com/{self.repo}/{self.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 + + print(f"❌ 上传失败: {response.status_code} - {response.text}") + return None + + except Exception as e: + print(f"❌ 上传失败: {str(e)}") + import traceback + print(f"🔍 详细错误:\n{traceback.format_exc()}") + return None \ No newline at end of file diff --git a/utils/image_processor.py b/utils/image_processor.py new file mode 100644 index 0000000..078f1cd --- /dev/null +++ b/utils/image_processor.py @@ -0,0 +1,37 @@ +import torch +import numpy as np +from PIL import Image +import traceback + +def tensor_to_pil(tensor): + """将图像张量转换为PIL图像(兼容4D/3D张量)""" + try: + # 转换为CPU张量 + image = tensor.cpu().detach() + + # 处理不同维度 + if len(image.shape) == 4: + image = image[0] # 取批次中第一张 + if len(image.shape) == 3 and image.shape[0] in [1, 3, 4]: + image = image.permute(1, 2, 0) # CHW -> HWC + + # 转换为numpy数组 + image = (image * 255).clamp(0, 255).byte().numpy() + + # 处理通道数 + if image.shape[-1] == 1: + image = np.concatenate([image]*3, axis=-1) + elif image.shape[-1] == 4: + image = image[..., :3] # 移除Alpha通道 + + return Image.fromarray(image) + except Exception as e: + print(f"❌ 图像处理失败: {traceback.format_exc()}") + return None + +def validate_image_url(url): + """验证图片URL格式""" + if not url.startswith(("http://", "https://")): + raise ValueError("图片URL必须以http/https开头") + if "cdn.jsdelivr.net" not in url and "aliyuncs.com" not in url: + print("⚠️ 警告:非受信图片源,可能影响生成稳定性") \ No newline at end of file diff --git a/utils/init.py b/utils/init.py new file mode 100644 index 0000000..e2e926f --- /dev/null +++ b/utils/init.py @@ -0,0 +1,7 @@ + ✅ utils模块已加载 + # 在ComfyUI的Python控制台中测试 + from Comfyui_wan_api.utils import image_processor, github_client + print("图像处理器模块:", dir(image_processor)) + print("GitHub客户端:", dir(github_client)) + 图像处理器模块: ['tensor_to_pil', 'validate_image_url'] + GitHub客户端: ['GitHubUploader'] \ No newline at end of file diff --git a/workflows/.gitkeep b/workflows/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/workflows/wan-api.json b/workflows/wan-api.json new file mode 100644 index 0000000..60d51ca --- /dev/null +++ b/workflows/wan-api.json @@ -0,0 +1,465 @@ +{ + "last_node_id": 7, + "last_link_id": 6, + "nodes": [ + { + "id": 3, + "type": "easy showAnything", + "pos": [ + -1191.599365234375, + 2210.471435546875 + ], + "size": [ + 210, + 76 + ], + "flags": {}, + "order": 2, + "mode": 0, + "inputs": [ + { + "name": "anything", + "type": "*", + "link": 2, + "shape": 7, + "label": "输入任何" + } + ], + "outputs": [ + { + "name": "output", + "type": "*", + "links": [ + 4 + ], + "slot_index": 0 + } + ], + "properties": { + "Node name for S&R": "easy showAnything" + }, + "widgets_values": [ + "https://raw.githubusercontent.com/penposs/image/main/images/wanx_1740562663.jpg" + ] + }, + { + "id": 5, + "type": "WanAPIImageToVideo", + "pos": [ + -877.1105346679688, + 2040.0242919921875 + ], + "size": [ + 400, + 226 + ], + "flags": {}, + "order": 3, + "mode": 0, + "inputs": [ + { + "name": "image_url", + "type": "STRING", + "link": 4, + "widget": { + "name": "image_url" + }, + "label": "image_url" + } + ], + "outputs": [ + { + "name": "video_url", + "type": "STRING", + "links": [ + 5, + 6 + ], + "slot_index": 0, + "label": "video_url" + } + ], + "properties": { + "Node name for S&R": "WanAPIImageToVideo" + }, + "widgets_values": [ + "", + "wanx2.1-i2v-plus", + 5, + "", + "动漫风格,镜头环绕,女人跳起挥剑,剑光闪耀,黑雾傀儡碎裂成无数碎片,碎片缓缓飘落,闪闪发光,头发衣服飘动。", + 896448203, + "randomize", + "False" + ] + }, + { + "id": 6, + "type": "ShowText|pysssss", + "pos": [ + -851.0746459960938, + 2318.310302734375 + ], + "size": [ + 315, + 476 + ], + "flags": {}, + "order": 4, + "mode": 0, + "inputs": [ + { + "name": "text", + "type": "STRING", + "link": 5, + "widget": { + "name": "text" + }, + "label": "文本" + } + ], + "outputs": [ + { + "name": "STRING", + "type": "STRING", + "links": null, + "shape": 6, + "label": "字符串" + } + ], + "properties": { + "Node name for S&R": "ShowText|pysssss" + }, + "widgets_values": [ + "", + "https://dashscope-result-sh.oss-cn-shanghai.aliyuncs.com/1d/51/20250226/72e40b15/a8624ac4-cdf8-433b-9803-d273f99638f1.mp4?Expires=1740648892&OSSAccessKeyId=LTAI5tQZd8AEcZX6KZV4G8qL&Signature=qkCtiHjGJXSoHrHqmtV%2FRqCHziE%3D" + ] + }, + { + "id": 7, + "type": "VHS_LoadVideoPath", + "pos": [ + -441.0176086425781, + 2044.71484375 + ], + "size": [ + 231.8896484375, + 238 + ], + "flags": {}, + "order": 5, + "mode": 0, + "inputs": [ + { + "name": "meta_batch", + "type": "VHS_BatchManager", + "link": null, + "shape": 7, + "label": "批次管理" + }, + { + "name": "vae", + "type": "VAE", + "link": null, + "shape": 7, + "label": "vae" + }, + { + "name": "video", + "type": "STRING", + "link": 6, + "widget": { + "name": "video" + }, + "label": "视频" + } + ], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 3 + ], + "slot_index": 0, + "label": "图像" + }, + { + "name": "frame_count", + "type": "INT", + "links": [], + "slot_index": 1, + "label": "帧计数" + }, + { + "name": "audio", + "type": "AUDIO", + "links": null, + "label": "音频" + }, + { + "name": "video_info", + "type": "VHS_VIDEOINFO", + "links": [], + "slot_index": 3, + "label": "视频信息" + } + ], + "properties": { + "Node name for S&R": "VHS_LoadVideoPath" + }, + "widgets_values": { + "video": "https://p1.a.kwimgs.com/bs2/upload-ylab-stunt/special-effect/output/HB1_PROD_ai_web_46554461/-2878350957757294165/output.mp4", + "force_rate": 0, + "force_size": "Disabled", + "custom_width": 512, + "custom_height": 512, + "frame_load_cap": 0, + "skip_first_frames": 0, + "select_every_nth": 1, + "videopreview": { + "hidden": false, + "paused": false, + "params": { + "frame_load_cap": 0, + "skip_first_frames": 0, + "force_rate": 0, + "filename": "https://p1.a.kwimgs.com/bs2/upload-ylab-stunt/special-effect/output/HB1_PROD_ai_web_46554461/-2878350957757294165/output.mp4", + "type": "path", + "format": "video/mp4", + "select_every_nth": 1 + }, + "muted": false + } + } + }, + { + "id": 4, + "type": "VHS_VideoCombine", + "pos": [ + -143.3603057861328, + 2037.040771484375 + ], + "size": [ + 247.27825927734375, + 657.9598999023438 + ], + "flags": {}, + "order": 6, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 3, + "label": "图像" + }, + { + "name": "audio", + "type": "AUDIO", + "link": null, + "shape": 7, + "label": "音频" + }, + { + "name": "meta_batch", + "type": "VHS_BatchManager", + "link": null, + "shape": 7, + "label": "批次管理" + }, + { + "name": "vae", + "type": "VAE", + "link": null, + "shape": 7, + "label": "vae" + } + ], + "outputs": [ + { + "name": "Filenames", + "type": "VHS_FILENAMES", + "links": [], + "slot_index": 0, + "label": "文件名" + } + ], + "properties": { + "Node name for S&R": "VHS_VideoCombine" + }, + "widgets_values": { + "frame_rate": 30, + "loop_count": 0, + "filename_prefix": "wan/test", + "format": "video/h264-mp4", + "pix_fmt": "yuv420p", + "crf": 19, + "save_metadata": true, + "pingpong": false, + "save_output": true, + "videopreview": { + "hidden": false, + "paused": false, + "params": { + "filename": "test_00002.mp4", + "subfolder": "wan", + "type": "output", + "format": "video/h264-mp4", + "frame_rate": 30, + "workflow": "test_00002.png", + "fullpath": "I:\\ComfyUI_windows_portable\\ComfyUI\\output\\wan\\test_00002.mp4" + }, + "muted": false + } + } + }, + { + "id": 1, + "type": "LoadImage", + "pos": [ + -1545.814208984375, + 1985.2684326171875 + ], + "size": [ + 315, + 314 + ], + "flags": {}, + "order": 0, + "mode": 0, + "inputs": [], + "outputs": [ + { + "name": "IMAGE", + "type": "IMAGE", + "links": [ + 1 + ], + "slot_index": 0, + "label": "图像" + }, + { + "name": "MASK", + "type": "MASK", + "links": null, + "label": "遮罩" + } + ], + "properties": { + "Node name for S&R": "LoadImage" + }, + "widgets_values": [ + "46.jpg", + "image" + ] + }, + { + "id": 2, + "type": "WanAPIImageUploader", + "pos": [ + -1187.8138427734375, + 1991.5435791015625 + ], + "size": [ + 231.67236328125, + 130 + ], + "flags": {}, + "order": 1, + "mode": 0, + "inputs": [ + { + "name": "images", + "type": "IMAGE", + "link": 1, + "label": "images" + } + ], + "outputs": [ + { + "name": "STRING", + "type": "STRING", + "links": [ + 2 + ], + "slot_index": 0, + "label": "STRING" + } + ], + "properties": { + "Node name for S&R": "WanAPIImageUploader" + }, + "widgets_values": [ + "GitHub", + "", + "main", + "" + ] + } + ], + "links": [ + [ + 1, + 1, + 0, + 2, + 0, + "IMAGE" + ], + [ + 2, + 2, + 0, + 3, + 0, + "*" + ], + [ + 3, + 7, + 0, + 4, + 0, + "IMAGE" + ], + [ + 4, + 3, + 0, + 5, + 0, + "STRING" + ], + [ + 5, + 5, + 0, + 6, + 0, + "STRING" + ], + [ + 6, + 5, + 0, + 7, + 2, + "STRING" + ] + ], + "groups": [], + "config": {}, + "extra": { + "ds": { + "scale": 1, + "offset": [ + 1584.863449468461, + -1739.5341228629436 + ] + }, + "groupNodes": {}, + "ue_links": [] + }, + "version": 0.4 +} \ No newline at end of file