From 02bcc439be32443ff4f8cedeb313db5e334df18d Mon Sep 17 00:00:00 2001 From: WWWEN8 <142774653+WWWEN8@users.noreply.github.com> Date: Mon, 20 Oct 2025 12:35:39 +0800 Subject: [PATCH] Update node display names to English --- ccx_downloader_node.py | 366 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 366 insertions(+) create mode 100644 ccx_downloader_node.py diff --git a/ccx_downloader_node.py b/ccx_downloader_node.py new file mode 100644 index 0000000..36858ea --- /dev/null +++ b/ccx_downloader_node.py @@ -0,0 +1,366 @@ +import os +import shutil +import json +import zipfile +import requests +import hashlib +from datetime import datetime +from urllib.parse import urlparse + +__version__ = "2.0" + +class CCXManagerNode: + """CCX管理器核心类,负责配置加载、保存和自动运行检查""" + # 修复1: 添加配置文件名参数 + def __init__(self, config_filename="config.json"): + self.config_path = os.path.join(os.path.dirname(__file__), config_filename) + self.config = self.load_config() + self.status = "未运行" + self.temp_dir = os.path.join(os.path.dirname(__file__), "temp") + # 创建临时目录 + os.makedirs(self.temp_dir, exist_ok=True) + + def load_config(self): + """加载配置文件,如果不存在则创建默认配置""" + default_config = { + "source_path": "", + "target_path": "", + "ccx_filename": "", + "auto_run_on_restart": False, + "last_run_time": "", + "github_repo_url": "https://github.com/zombieyang/sd-ppp.git", + "last_commit_hash": "", + "version": __version__ + } + try: + if os.path.exists(self.config_path): + with open(self.config_path, 'r', encoding='utf-8') as f: + return json.load(f) + else: + with open(self.config_path, 'w', encoding='utf-8') as f: + json.dump(default_config, f, indent=4, ensure_ascii=False) + return default_config + except Exception as e: + self.status = f"加载配置失败: {str(e)}" + print(f"[CCXManager] 加载配置失败: {str(e)}") + return default_config + + def save_config(self): + """保存配置文件""" + try: + with open(self.config_path, 'w', encoding='utf-8') as f: + json.dump(self.config, f, indent=4, ensure_ascii=False) + except Exception as e: + self.status = f"保存配置失败: {str(e)}" + print(f"[CCXManager] 保存配置失败: {str(e)}") + + def download_from_url(self, url): + """从URL下载CCX文件并返回本地临时路径""" + try: + # 添加时间戳参数 + timestamp = int(datetime.now().timestamp() * 1000) + if '?' in url: + url += f"&_={timestamp}" + else: + url += f"?_={timestamp}" + + # 发送请求 + print(f"[CCXManager] 开始下载: {url}") + response = requests.get(url, stream=True, timeout=30) + response.raise_for_status() + + # 获取文件名 + filename = os.path.basename(urlparse(url).path) + if not filename.endswith('.ccx'): + self.status = "URL不是有效的CCX文件" + return None, None + + temp_path = os.path.join(self.temp_dir, filename) + + # 保存文件 + with open(temp_path, 'wb') as f: + for chunk in response.iter_content(chunk_size=8192): + f.write(chunk) + + print(f"[CCXManager] 下载完成: {temp_path}") + return temp_path, filename + except Exception as e: + self.status = f"下载失败: {str(e)}" + print(f"[CCXManager] 下载失败: {str(e)}") + return None, None + + def unzip_ccx(self, source_path, target_path): + """解压CCX文件到目标路径""" + try: + with zipfile.ZipFile(source_path, 'r') as zip_ref: + zip_ref.extractall(target_path) + return True + except Exception as e: + self.status = f"解压失败: {str(e)}" + print(f"[CCXManager] 解压失败: {str(e)}") + return False + + def clean_temp_files(self): + """清理临时文件""" + try: + if os.path.exists(self.temp_dir): + shutil.rmtree(self.temp_dir) + os.makedirs(self.temp_dir, exist_ok=True) + except Exception as e: + print(f"[CCXManager] 清理临时文件失败: {str(e)}") + + def get_github_latest_commit(self, repo_url): + """获取GitHub仓库最新commit哈希值,支持多种URL格式和分支""" + try: + # 解析仓库路径和分支 + branch = "main" # 默认分支 + + # 处理不同格式的URL + if repo_url.startswith('git@github.com:'): + # SSH格式: git@github.com:owner/repo.git + path = repo_url.replace('git@github.com:', '') + if ':' in path: + # 包含分支: git@github.com:owner/repo.git:branch + path, branch = path.split(':', 1) + owner, repo = path.split('/', 1) + repo = repo.replace('.git', '') + elif repo_url.startswith(('http://', 'https://')): + # HTTP/HTTPS格式 + parsed_url = urlparse(repo_url) + path_parts = parsed_url.path.strip('/').split('/') + if len(path_parts) < 2: + return None + owner, repo = path_parts[0], path_parts[1].replace('.git', '') + # 检查URL中是否包含分支信息 + if len(path_parts) >= 4 and path_parts[2] == 'tree': + branch = path_parts[3] + else: + # 不支持的URL格式 + print(f"[CCXManager] 不支持的GitHub URL格式: {repo_url}") + return None + + # 构建API URL + api_url = f"https://api.github.com/repos/{owner}/{repo}/commits/{branch}" + print(f"[CCXManager] 获取GitHub最新commit: {api_url}") + + # 发送请求 + response = requests.get(api_url, timeout=10) + response.raise_for_status() + commit_data = response.json() + return commit_data['sha'] + except Exception as e: + print(f"[CCXManager] 获取GitHub最新commit失败: {str(e)}") + return None + + def check_github_update(self): + """检查GitHub仓库是否有更新""" + repo_url = self.config.get("github_repo_url", "") + if not repo_url: + return False + + current_hash = self.config.get("last_commit_hash", "") + latest_hash = self.get_github_latest_commit(repo_url) + + if not latest_hash: + return False + + if current_hash != latest_hash: + self.config["last_commit_hash"] = latest_hash + self.save_config() + return True + return False + + def run(self, source_path, target_path, auto_run=False): + """执行CCX文件处理流程""" + self.status = "处理中" + self.config["source_path"] = source_path + self.config["target_path"] = target_path + self.config["auto_run_on_restart"] = auto_run + self.config["last_run_time"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + self.save_config() + + # 验证目标路径 + if not target_path: + self.status = "错误: 目标路径未设置" + return self.status + os.makedirs(target_path, exist_ok=True) + + # 清空目标文件夹内容 + try: + if os.path.exists(target_path): + for item in os.listdir(target_path): + item_path = os.path.join(target_path, item) + if os.path.isfile(item_path) or os.path.islink(item_path): + os.unlink(item_path) + elif os.path.isdir(item_path): + shutil.rmtree(item_path) + print(f"[CCXManager] 目标文件夹已清空: {target_path}") + except Exception as e: + self.status = f"清理目标文件夹失败: {str(e)}" + print(f"[CCXManager] 清理目标文件夹失败: {str(e)}") + return self.status + + # 处理URL下载 + local_path = source_path + ccx_filename = None + is_url = source_path.startswith(('http://', 'https://')) + + if is_url: + local_path, ccx_filename = self.download_from_url(source_path) + if not local_path: + return self.status + else: + # 验证本地文件 + if not os.path.exists(local_path): + self.status = "错误: 源文件不存在" + return self.status + ccx_filename = os.path.basename(local_path) + + # 解压文件 + if self.unzip_ccx(local_path, target_path): + self.status = f"成功: {ccx_filename} 已解压到 {target_path}" + else: + self.status = f"失败: 无法解压 {ccx_filename}" + + # 清理临时文件 + if is_url: + self.clean_temp_files() + + print(f"[CCXManager] {self.status}") + return self.status + + def auto_run(self): + """重启后自动运行""" + # 检查GitHub是否有更新 + has_update = self.check_github_update() + auto_run_enabled = self.config.get("auto_run_on_restart", False) + + # 只有在检测到更新或启用了自动运行时才执行 + if has_update or auto_run_enabled: + print(f"[CCXManager] 检测到{'更新' if has_update else '自动运行配置'},正在执行CCX更新...") + if all([self.config.get("source_path"), self.config.get("target_path")]): + # 运行后禁用自动运行,除非是由更新触发的 + auto_run_after = has_update # 如果是更新触发的,则保持启用状态 + self.run(self.config["source_path"], self.config["target_path"], auto_run=auto_run_after) + else: + print(f"[CCXManager] 自动运行失败: 配置不完整") + else: + print(f"[CCXManager] 没有检测到更新且自动运行已禁用") + +# 主节点 +class CCXManager: + @classmethod + def INPUT_TYPES(s): + # 加载配置以获取保存的target_path + config_path = os.path.join(os.path.dirname(__file__), "config.json") + default_target_path = "" + default_github_url = "https://github.com/zombieyang/sd-ppp.git" + try: + if os.path.exists(config_path): + with open(config_path, 'r', encoding='utf-8') as f: + config = json.load(f) + default_target_path = config.get("target_path", "") + default_github_url = config.get("github_repo_url", default_github_url) + except Exception as e: + print(f"[CCXManager] 加载配置失败: {str(e)}") + + return { + "required": { + "source_type": (["local_path", "url"], {"default": "url"}), + "source_path": ("STRING", {"default": "https://gitee.com/zombieyang/sd-ppp/raw/main/static/sd-ppp2_PS.ccx", "placeholder": "本地文件路径或URL"}), + "target_path": ("STRING", {"default": default_target_path, "placeholder": "目标解压路径,例如:i:/311/ComfyUI/custom_nodes"}), + "github_repo_url": ("STRING", {"default": default_github_url, "placeholder": "GitHub仓库URL,留空禁用更新检测"}), + "auto_run_on_restart": (["enable", "disable"], {"default": "enable"}), + }, + } + + RETURN_TYPES = ("STRING",) + RETURN_NAMES = ("运行状态",) + FUNCTION = "process_ccx" + CATEGORY = "SD-PPP插件更新" + TITLE = "SDPPP插件更新助手2.0最新" + + def __init__(self): + self.manager = CCXManagerNode() + + def process_ccx(self, source_type, source_path, target_path, github_repo_url, auto_run_on_restart): + auto_run = auto_run_on_restart == "enable" + # 更新GitHub仓库URL配置 + self.manager.config["github_repo_url"] = github_repo_url + try: + self.manager.save_config() + if source_type == "url" and not source_path.startswith(("http://", "https://")): + return ("错误: URL格式不正确",) + status = self.manager.run(source_path, target_path, auto_run) + return (status,) + except Exception as e: + error_msg = f"处理失败: {str(e)}" + print(f"[CCXManager] {error_msg}") + return (error_msg,) + +# 分身节点 +class CCXManagerCopy: + @classmethod + def INPUT_TYPES(s): + # 加载配置以获取保存的target_path + config_path = os.path.join(os.path.dirname(__file__), "config_copy.json") + default_target_path = "" + default_github_url = "https://github.com/zombieyang/sd-ppp.git" + try: + if os.path.exists(config_path): + with open(config_path, 'r', encoding='utf-8') as f: + config = json.load(f) + default_target_path = config.get("target_path", "") + default_github_url = config.get("github_repo_url", default_github_url) + except Exception as e: + print(f"[CCXManagerCopy] 加载配置失败: {str(e)}") + + return { + "required": { + "source_type": (["local_path", "url"], {"default": "url"}), + "source_path": ("STRING", {"default": "https://gitee.com/zombieyang/sd-ppp/raw/main/static/sd-ppp_PS.ccx", "placeholder": "本地文件路径或URL"}), + "target_path": ("STRING", {"default": default_target_path, "placeholder": "目标解压路径,例如:i:/311/ComfyUI/custom_nodes"}), + "github_repo_url": ("STRING", {"default": default_github_url, "placeholder": "GitHub仓库URL,留空禁用更新检测"}), + "auto_run_on_restart": (["enable", "disable"], {"default": "enable"}), + }, + } + + RETURN_TYPES = ("STRING",) + RETURN_NAMES = ("运行状态",) + FUNCTION = "process_ccx" + CATEGORY = "SD-PPP插件更新" + TITLE = "SDPPP插件更新助手1.0最新" + + def __init__(self): + # 使用独立配置文件 + self.manager = CCXManagerNode(config_filename="config_copy.json") + + def process_ccx(self, source_type, source_path, target_path, github_repo_url, auto_run_on_restart): + auto_run = auto_run_on_restart == "enable" + # 更新GitHub仓库URL配置 + self.manager.config["github_repo_url"] = github_repo_url + self.manager.save_config() + if source_type == "url" and not source_path.startswith(("http://", "https://")): + return ("错误: URL格式不正确",) + status = self.manager.run(source_path, target_path, auto_run) + return (status,) + +# 节点注册 +NODE_CLASS_MAPPINGS = { + "CCXManager": CCXManager, + "CCXManagerCopy": CCXManagerCopy +} + +NODE_DISPLAY_NAME_MAPPINGS = { + "CCXManager": "Photoshop side automatic update SDPPP2.0", + "CCXManagerCopy": "Photoshop side automatic update SDPPP1.0" +} + +# 程序启动时自动检查并运行 +print(f"[CCXManager] SDPPP插件更新助手节点已加载 (版本: {__version__})") +# 主节点自动运行检查 +manager = CCXManagerNode() +manager.auto_run() +# 克隆节点自动运行检查 +manager_copy = CCXManagerNode(config_filename="config_copy.json") +manager_copy.auto_run() \ No newline at end of file