From 4d2e77fc51367c2a9d8d1d9d4f0220c2d41e3eef Mon Sep 17 00:00:00 2001 From: InfinityPacer <160988576+InfinityPacer@users.noreply.github.com> Date: Tue, 17 Sep 2024 16:05:16 +0800 Subject: [PATCH 1/4] feat(plugin): improve plugin version upgrade and compatibility --- app/helper/plugin.py | 131 +++++++++++++++++++++++++++++++++---------- 1 file changed, 101 insertions(+), 30 deletions(-) diff --git a/app/helper/plugin.py b/app/helper/plugin.py index 9a000660..d468ea7f 100644 --- a/app/helper/plugin.py +++ b/app/helper/plugin.py @@ -58,6 +58,38 @@ class PluginHelper(metaclass=Singleton): logger.error(f"插件包数据解析失败:{res.text}") return {} + def get_plugin_package_version(self, pid: str, repo_url: str, version: str = None) -> Optional[str]: + """ + 检查并获取指定插件的可用版本,支持多版本优先级加载和版本兼容性检测 + 1. 如果未指定版本,则使用系统配置的默认版本(通过 settings.VERSION_FLAG 设置) + 2. 优先检查指定版本的插件(如 `package.v2.json`) + 3. 如果插件不存在于指定版本,检查 `package.json` 文件,查看该插件是否兼容指定版本 + 4. 如果插件不存在或不兼容指定版本,返回 `None` + :param pid: 插件 ID,用于在插件列表中查找 + :param repo_url: 插件仓库的 URL,指定用于获取插件信息的 GitHub 仓库地址 + :param version: 首选插件版本 (如 "v2", "v3"),如果为空则默认使用系统配置的版本 + :return: 返回可用的插件版本号 (如 "v2",如果指定版本不可用则返回空字符串表示 v1),如果插件不可用则返回 None + """ + # 如果没有指定版本,则使用当前系统配置的版本(如 "v2") + if not version: + version = settings.VERSION_FLAG + + # 优先检查指定版本的插件,即 package.v(x).json 文件中是否存在该插件,如果存在,返回该版本号 + plugins = self.get_plugins(repo_url, version) + if pid in plugins: + return version + + # 如果指定版本的插件不存在,检查全局 package.json 文件,查看插件是否兼容指定的版本 + global_plugins = self.get_plugins(repo_url) + plugin = global_plugins.get(pid, None) + + # 检查插件是否明确支持当前指定的版本(如 v2 或 v3),如果支持,返回空字符串表示使用 package.json(v1) + if plugin and plugin.get(version) is True: + return "" + + # 如果所有版本都不存在或插件不兼容,返回 None,表示插件不可用 + return None + @staticmethod def get_repo_info(repo_url: str) -> Tuple[Optional[str], Optional[str]]: """ @@ -116,16 +148,18 @@ class PluginHelper(metaclass=Singleton): json={"plugins": [{"plugin_id": plugin} for plugin in plugins]}) return True if res else False - def install(self, pid: str, repo_url: str) -> Tuple[bool, str]: + def install(self, pid: str, repo_url: str, version: str = None) -> Tuple[bool, str]: """ 安装插件,包括依赖安装和文件下载,相关资源支持自动降级策略 - 1. 从 GitHub 获取文件列表(包括 requirements.txt) - 2. 删除旧的插件目录 - 3. 下载并预安装 requirements.txt 中的依赖(如果存在) - 4. 下载并安装插件的其他文件 - 5. 再次尝试安装依赖(确保安装完整) + 1. 检查并获取插件的指定版本,确认版本兼容性。 + 2. 从 GitHub 获取文件列表(包括 requirements.txt) + 3. 删除旧的插件目录 + 4. 下载并预安装 requirements.txt 中的依赖(如果存在) + 5. 下载并安装插件的其他文件 + 6. 再次尝试安装依赖(确保安装完整) :param pid: 插件 ID :param repo_url: 插件仓库地址 + :param version: 首选插件版本 (如 "v2", "v3"),如果为空则默认使用系统配置的版本 :return: (是否成功, 错误信息) """ if SystemUtils.is_frozen(): @@ -142,15 +176,31 @@ class PluginHelper(metaclass=Singleton): user_repo = f"{user}/{repo}" - # 1. 获取插件文件列表(包括 requirements.txt) - file_list, msg = self.__get_file_list(pid.lower(), user_repo) + if not version: + version = settings.VERSION_FLAG + + # 1. 优先检查指定版本的插件 + package_version = self.get_plugin_package_version(pid, repo_url, version) + # 如果 package_version 为None,说明没有找到匹配的插件 + if package_version is None: + msg = f"{pid} 没有找到适用于当前 {version} 版本的插件" + logger.debug(msg) + return False, msg + # package_version 为空,表示从 package.json 中找到插件 + elif package_version == "": + logger.debug(f"{pid} 从 package.json 中找到适用于当前 {version} 版本插件") + else: + logger.debug(f"{pid} 从 package.{version}.json 中找到适用于当前 {version} 版本插件") + + # 2. 获取插件文件列表(包括 requirements.txt) + file_list, msg = self.__get_file_list(pid.lower(), user_repo, package_version) if not file_list: return False, msg - # 2. 删除旧的插件目录 + # 3. 删除旧的插件目录 self.__remove_old_plugin(pid.lower()) - # 3. 查找并安装 requirements.txt 中的依赖,确保插件环境的依赖尽可能完整。依赖安装可能失败且不影响插件安装,目前只记录日志 + # 4. 查找并安装 requirements.txt 中的依赖,确保插件环境的依赖尽可能完整。依赖安装可能失败且不影响插件安装,目前只记录日志 requirements_file_info = next((f for f in file_list if f.get("name") == "requirements.txt"), None) if requirements_file_info: logger.debug(f"{pid} 发现 requirements.txt,提前下载并预安装依赖") @@ -161,34 +211,41 @@ class PluginHelper(metaclass=Singleton): else: logger.debug(f"{pid} 依赖预安装成功") - # 4. 下载插件的其他文件 + # 5. 下载插件的其他文件 logger.info(f"{pid} 准备开始下载插件文件") - success, message = self.__download_files(pid.lower(), file_list, user_repo, True) + success, message = self.__download_files(pid.lower(), file_list, user_repo, package_version, True) if not success: logger.error(f"{pid} 下载插件文件失败:{message}") return False, message else: logger.info(f"{pid} 下载插件文件成功") - # 5. 插件文件安装成功后,再次尝试安装依赖,避免因为遗漏依赖导致的插件运行问题,目前依旧只记录日志 - success, message = self.__install_dependencies_if_required(pid) - if not success: - logger.error(f"{pid} 依赖安装失败:{message}") - else: - logger.info(f"{pid} 依赖安装成功") + # 6. 插件文件安装成功后,再次尝试安装依赖,避免因为遗漏依赖导致的插件运行问题,目前依旧只记录日志 + dependencies_exist, success, message = self.__install_dependencies_if_required(pid) + if dependencies_exist: + if not success: + logger.error(f"{pid} 依赖安装失败:{message}") + else: + logger.info(f"{pid} 依赖安装成功") # 插件安装成功后,统计安装信息 self.install_reg(pid) return True, "" - def __get_file_list(self, pid: str, user_repo: str) -> Tuple[Optional[list], Optional[str]]: + def __get_file_list(self, pid: str, user_repo: str, package_version: str = None) -> \ + Tuple[Optional[list], Optional[str]]: """ 获取插件的文件列表 :param pid: 插件 ID :param user_repo: GitHub 仓库的 user/repo 路径 :return: (文件列表, 错误信息) """ - file_api = f"https://api.github.com/repos/{user_repo}/contents/plugins/{pid}" + file_api = f"https://api.github.com/repos/{user_repo}/contents/plugins" + # 如果 package_version 存在(如 "v2"),则加上版本号 + if package_version: + file_api += f".{package_version}" + file_api += f"/{pid}" + res = self.__request_with_fallback(file_api, headers=settings.REPO_GITHUB_HEADERS(repo=user_repo), is_api=True, @@ -209,8 +266,8 @@ class PluginHelper(metaclass=Singleton): logger.error(f"插件数据解析失败:{res.text},{e}") return None, "插件数据解析失败" - def __download_files(self, pid: str, file_list: List[dict], user_repo: str, skip_requirements: bool = False) \ - -> Tuple[bool, str]: + def __download_files(self, pid: str, file_list: List[dict], user_repo: str, + package_version: str = None, skip_requirements: bool = False) -> Tuple[bool, str]: """ 下载插件文件 :param pid: 插件 ID @@ -242,15 +299,21 @@ class PluginHelper(metaclass=Singleton): elif res.status_code != 200: return False, f"下载文件 {item.get('path')} 失败:{res.status_code}" + # 确保文件路径不包含版本号(如 v2、v3),如果有 package_version,移除路径中的版本号 + relative_path = item.get("path") + if package_version: + relative_path = relative_path.replace(f"plugins.{package_version}", "plugins", 1) + # 创建插件文件夹并写入文件 - file_path = Path(settings.ROOT_PATH) / "app" / item.get("path") + file_path = Path(settings.ROOT_PATH) / "app" / relative_path file_path.parent.mkdir(parents=True, exist_ok=True) with open(file_path, "w", encoding="utf-8") as f: f.write(res.text) logger.debug(f"文件 {item.get('path')} 下载成功,保存路径:{file_path}") else: # 如果是子目录,则将子目录内容加入栈中继续处理 - sub_list, msg = self.__get_file_list(f"{current_pid}/{item.get('name')}", user_repo) + sub_list, msg = self.__get_file_list(f"{current_pid}/{item.get('name')}", user_repo, + package_version) if not sub_list: return False, msg stack.append((f"{current_pid}/{item.get('name')}", sub_list)) @@ -287,18 +350,26 @@ class PluginHelper(metaclass=Singleton): return True, "" # 如果 requirements.txt 为空,视作成功 - def __install_dependencies_if_required(self, pid: str) -> Tuple[bool, str]: + def __install_dependencies_if_required(self, pid: str) -> Tuple[bool, bool, str]: """ - 安装插件依赖 + 安装插件依赖。 :param pid: 插件 ID - :return: (是否成功, 错误信息) + :return: (是否存在依赖,安装是否成功, 错误信息) """ + # 定位插件目录和依赖文件 plugin_dir = Path(settings.ROOT_PATH) / "app" / "plugins" / pid.lower() requirements_file = plugin_dir / "requirements.txt" + + # 检查是否存在 requirements.txt 文件 if requirements_file.exists(): - logger.info(f"{pid} 存在依赖项,开始尝试安装依赖") - return self.__pip_install_with_fallback(requirements_file) - return True, "" + logger.info(f"{pid} 存在依赖,开始尝试安装依赖") + success, error_message = self.__pip_install_with_fallback(requirements_file) + if success: + return True, True, "" + else: + return True, False, error_message + + return False, False, "不存在依赖" @staticmethod def __remove_old_plugin(pid: str): From 8cc72f402bcebeff78027e252a8065781dda7fa9 Mon Sep 17 00:00:00 2001 From: InfinityPacer <160988576+InfinityPacer@users.noreply.github.com> Date: Tue, 17 Sep 2024 16:56:08 +0800 Subject: [PATCH 2/4] fix(config): make VERSION_FLAG a read-only property --- app/core/config.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/core/config.py b/app/core/config.py index a39b1d41..b9cada12 100644 --- a/app/core/config.py +++ b/app/core/config.py @@ -3,7 +3,6 @@ import sys import threading from pathlib import Path from typing import Optional, List -from urllib.parse import urlparse from dotenv import set_key from pydantic import BaseSettings, validator @@ -18,8 +17,6 @@ class Settings(BaseSettings): """ # 项目名称 PROJECT_NAME = "MoviePilot" - # 版本标识,用来区分重大版本,为空则为v1 - VERSION_FLAG = "v2" # 域名 格式;https://movie-pilot.org APP_DOMAIN: str = "" # API路径 @@ -213,6 +210,13 @@ class Settings(BaseSettings): logger.warning("API_TOKEN 长度不足 16 个字符,存在安全隐患,建议尽快更换为更复杂的密钥!") return v + @property + def VERSION_FLAG(self) -> str: + """ + 版本标识,用来区分重大版本,为空则为v1,不允许外部修改 + """ + return "v2" + @property def INNER_CONFIG_PATH(self): return self.ROOT_PATH / "config" From 513669861734463150ea991da7e74fd5912d8cac Mon Sep 17 00:00:00 2001 From: InfinityPacer <160988576+InfinityPacer@users.noreply.github.com> Date: Tue, 17 Sep 2024 17:26:06 +0800 Subject: [PATCH 3/4] feat(plugin): improve online plugin retrieval --- app/core/plugin.py | 237 ++++++++++++++++++++++++------------------- app/helper/plugin.py | 36 +++---- 2 files changed, 149 insertions(+), 124 deletions(-) diff --git a/app/core/plugin.py b/app/core/plugin.py index 6b977774..8def04a5 100644 --- a/app/core/plugin.py +++ b/app/core/plugin.py @@ -542,133 +542,59 @@ class PluginManager(metaclass=Singleton): """ 获取所有在线插件信息 """ - - def __get_plugin_info(market: str, version: str = None) -> Optional[List[schemas.Plugin]]: - """ - 获取插件信息 - """ - online_plugins = self.pluginhelper.get_plugins(repo_url=market, version=version) or {} - if not online_plugins: - if not version: - logger.warn(f"获取插件库失败:{market}") - return - ret_plugins = [] - add_time = len(online_plugins) - for pid, plugin_info in online_plugins.items(): - # 版本兼容性控制 - if not version: - if hasattr(settings, 'VERSION_FLAG') \ - and not plugin_info.get(settings.VERSION_FLAG): - # 插件当前版本不兼容 - continue - # 运行状插件 - plugin_obj = self._running_plugins.get(pid) - # 非运行态插件 - plugin_static = self._plugins.get(pid) - # 基本属性 - plugin = schemas.Plugin() - # ID - plugin.id = pid - # 安装状态 - if pid in installed_apps and plugin_static: - plugin.installed = True - else: - plugin.installed = False - # 是否有新版本 - plugin.has_update = False - if plugin_static: - installed_version = getattr(plugin_static, "plugin_version") - if StringUtils.compare_version(installed_version, plugin_info.get("version")) < 0: - # 需要更新 - plugin.has_update = True - # 运行状态 - if plugin_obj and hasattr(plugin_obj, "get_state"): - try: - state = plugin_obj.get_state() - except Exception as e: - logger.error(f"获取插件 {pid} 状态出错:{str(e)}") - state = False - plugin.state = state - else: - plugin.state = False - # 是否有详情页面 - plugin.has_page = False - if plugin_obj and hasattr(plugin_obj, "get_page"): - if ObjectUtils.check_method(plugin_obj.get_page): - plugin.has_page = True - # 公钥 - if plugin_info.get("key"): - plugin.plugin_public_key = plugin_info.get("key") - # 权限 - if not self.__set_and_check_auth_level(plugin=plugin, source=plugin_info): - continue - # 名称 - if plugin_info.get("name"): - plugin.plugin_name = plugin_info.get("name") - # 描述 - if plugin_info.get("description"): - plugin.plugin_desc = plugin_info.get("description") - # 版本 - if plugin_info.get("version"): - plugin.plugin_version = plugin_info.get("version") - # 图标 - if plugin_info.get("icon"): - plugin.plugin_icon = plugin_info.get("icon") - # 标签 - if plugin_info.get("labels"): - plugin.plugin_label = plugin_info.get("labels") - # 作者 - if plugin_info.get("author"): - plugin.plugin_author = plugin_info.get("author") - # 更新历史 - if plugin_info.get("history"): - plugin.history = plugin_info.get("history") - # 仓库链接 - plugin.repo_url = market - # 本地标志 - plugin.is_local = False - # 添加顺序 - plugin.add_time = add_time - # 汇总 - ret_plugins.append(plugin) - add_time -= 1 - - return ret_plugins - if not settings.PLUGIN_MARKET: return [] + # 返回值 all_plugins = [] - # 已安装插件 - installed_apps = self.systemconfig.get(SystemConfigKey.UserInstalledPlugins) or [] + # 用于存储高于 v1 版本的插件(如 v2, v3 等) + higher_version_plugins = [] + # 用于存储 v1 版本插件 + base_version_plugins = [] + # 使用多线程获取线上插件 with concurrent.futures.ThreadPoolExecutor() as executor: - futures = [] + futures_to_version = {} for m in settings.PLUGIN_MARKET.split(","): if not m: continue - # v1版本插件 - futures.append(executor.submit(__get_plugin_info, m, None)) - # v2+版本插件 + # 提交任务获取 v1 版本插件,存储 future 到 version 的映射 + base_future = executor.submit(self.get_plugins_from_market, m, None) + futures_to_version[base_future] = "base_version" + + # 提交任务获取高版本插件(如 v2、v3),存储 future 到 version 的映射 if settings.VERSION_FLAG: - futures.append(executor.submit(__get_plugin_info, m, settings.VERSION_FLAG)) - for future in concurrent.futures.as_completed(futures): + higher_version_future = executor.submit(self.get_plugins_from_market, m, settings.VERSION_FLAG) + futures_to_version[higher_version_future] = "higher_version" + + # 按照完成顺序处理结果 + for future in concurrent.futures.as_completed(futures_to_version): plugins = future.result() + version = futures_to_version[future] + if plugins: - all_plugins.extend(plugins) + if version == "higher_version": + higher_version_plugins.extend(plugins) # 收集高版本插件 + else: + base_version_plugins.extend(plugins) # 收集 v1 版本插件 + + # 优先处理高版本插件 + all_plugins.extend(higher_version_plugins) + # 将未出现在高版本插件列表中的 v1 插件加入 all_plugins + higher_plugin_ids = {f"{p.id}{p.plugin_version}" for p in higher_version_plugins} + all_plugins.extend([p for p in base_version_plugins if f"{p.id}{p.plugin_version}" not in higher_plugin_ids]) # 去重 all_plugins = list({f"{p.id}{p.plugin_version}": p for p in all_plugins}.values()) - # 所有插件按repo在设置中的顺序排序 + # 所有插件按 repo 在设置中的顺序排序 all_plugins.sort( key=lambda x: settings.PLUGIN_MARKET.split(",").index(x.repo_url) if x.repo_url else 0 ) - # 相同ID的插件保留版本号最大版本 + # 相同 ID 的插件保留版本号最大的版本 max_versions = {} for p in all_plugins: if p.id not in max_versions or StringUtils.compare_version(p.plugin_version, max_versions[p.id]) > 0: max_versions[p.id] = p.plugin_version - result = [p for p in all_plugins if - p.plugin_version == max_versions[p.id]] + result = [p for p in all_plugins if p.plugin_version == max_versions[p.id]] logger.info(f"共获取到 {len(result)} 个线上插件") return result @@ -764,6 +690,105 @@ class PluginManager(metaclass=Singleton): logger.debug(f"获取插件是否在本地包中存在失败,{e}") return False + def get_plugins_from_market(self, market: str, package_version: str = None) -> Optional[List[schemas.Plugin]]: + """ + 从指定的市场获取插件信息 + :param market: 市场的 URL 或标识 + :param package_version: 首选插件版本 (如 "v2", "v3"),如果不指定则获取 v1 版本 + :return: 返回插件的列表,若获取失败返回 [] + """ + if not market: + return [] + # 已安装插件 + installed_apps = self.systemconfig.get(SystemConfigKey.UserInstalledPlugins) or [] + # 获取在线插件 + online_plugins = self.pluginhelper.get_plugins(repo_url=market, package_version=package_version) or {} + if not online_plugins: + if not package_version: + logger.warning(f"获取插件库失败:{market},请检查 GitHub 网络连接") + return [] + ret_plugins = [] + add_time = len(online_plugins) + for pid, plugin_info in online_plugins.items(): + # 如 package_version 为空,则需要判断插件是否兼容当前版本 + if not package_version: + if plugin_info.get(settings.VERSION_FLAG) is not True: + # 插件当前版本不兼容 + continue + # 运行状插件 + plugin_obj = self._running_plugins.get(pid) + # 非运行态插件 + plugin_static = self._plugins.get(pid) + # 基本属性 + plugin = schemas.Plugin() + # ID + plugin.id = pid + # 安装状态 + if pid in installed_apps and plugin_static: + plugin.installed = True + else: + plugin.installed = False + # 是否有新版本 + plugin.has_update = False + if plugin_static: + installed_version = getattr(plugin_static, "plugin_version") + if StringUtils.compare_version(installed_version, plugin_info.get("version")) < 0: + # 需要更新 + plugin.has_update = True + # 运行状态 + if plugin_obj and hasattr(plugin_obj, "get_state"): + try: + state = plugin_obj.get_state() + except Exception as e: + logger.error(f"获取插件 {pid} 状态出错:{str(e)}") + state = False + plugin.state = state + else: + plugin.state = False + # 是否有详情页面 + plugin.has_page = False + if plugin_obj and hasattr(plugin_obj, "get_page"): + if ObjectUtils.check_method(plugin_obj.get_page): + plugin.has_page = True + # 公钥 + if plugin_info.get("key"): + plugin.plugin_public_key = plugin_info.get("key") + # 权限 + if not self.__set_and_check_auth_level(plugin=plugin, source=plugin_info): + continue + # 名称 + if plugin_info.get("name"): + plugin.plugin_name = plugin_info.get("name") + # 描述 + if plugin_info.get("description"): + plugin.plugin_desc = plugin_info.get("description") + # 版本 + if plugin_info.get("version"): + plugin.plugin_version = plugin_info.get("version") + # 图标 + if plugin_info.get("icon"): + plugin.plugin_icon = plugin_info.get("icon") + # 标签 + if plugin_info.get("labels"): + plugin.plugin_label = plugin_info.get("labels") + # 作者 + if plugin_info.get("author"): + plugin.plugin_author = plugin_info.get("author") + # 更新历史 + if plugin_info.get("history"): + plugin.history = plugin_info.get("history") + # 仓库链接 + plugin.repo_url = market + # 本地标志 + plugin.is_local = False + # 添加顺序 + plugin.add_time = add_time + # 汇总 + ret_plugins.append(plugin) + add_time -= 1 + + return ret_plugins + def __set_and_check_auth_level(self, plugin: Union[schemas.Plugin, Type[Any]], source: Optional[Union[dict, Type[Any]]] = None) -> bool: """ diff --git a/app/helper/plugin.py b/app/helper/plugin.py index d468ea7f..7471e17e 100644 --- a/app/helper/plugin.py +++ b/app/helper/plugin.py @@ -34,11 +34,11 @@ class PluginHelper(metaclass=Singleton): self.systemconfig.set(SystemConfigKey.PluginInstallReport, "1") @cached(cache=TTLCache(maxsize=1000, ttl=1800)) - def get_plugins(self, repo_url: str, version: str = None) -> Dict[str, dict]: + def get_plugins(self, repo_url: str, package_version: str = None) -> Dict[str, dict]: """ 获取Github所有最新插件列表 :param repo_url: Github仓库地址 - :param version: 版本 + :param package_version: 首选插件版本 (如 "v2", "v3"),如果不指定则获取 v1 版本 """ if not repo_url: return {} @@ -48,7 +48,7 @@ class PluginHelper(metaclass=Singleton): return {} raw_url = self._base_url.format(user=user, repo=repo) - package_url = f"{raw_url}package.{version}.json" if version else f"{raw_url}package.json" + package_url = f"{raw_url}package.{package_version}.json" if package_version else f"{raw_url}package.json" res = self.__request_with_fallback(package_url, headers=settings.REPO_GITHUB_HEADERS(repo=f"{user}/{repo}")) if res: @@ -58,7 +58,7 @@ class PluginHelper(metaclass=Singleton): logger.error(f"插件包数据解析失败:{res.text}") return {} - def get_plugin_package_version(self, pid: str, repo_url: str, version: str = None) -> Optional[str]: + def get_plugin_package_version(self, pid: str, repo_url: str, package_version: str = None) -> Optional[str]: """ 检查并获取指定插件的可用版本,支持多版本优先级加载和版本兼容性检测 1. 如果未指定版本,则使用系统配置的默认版本(通过 settings.VERSION_FLAG 设置) @@ -67,24 +67,24 @@ class PluginHelper(metaclass=Singleton): 4. 如果插件不存在或不兼容指定版本,返回 `None` :param pid: 插件 ID,用于在插件列表中查找 :param repo_url: 插件仓库的 URL,指定用于获取插件信息的 GitHub 仓库地址 - :param version: 首选插件版本 (如 "v2", "v3"),如果为空则默认使用系统配置的版本 + :param package_version: 首选插件版本 (如 "v2", "v3"),如不指定则默认使用系统配置的版本 :return: 返回可用的插件版本号 (如 "v2",如果指定版本不可用则返回空字符串表示 v1),如果插件不可用则返回 None """ # 如果没有指定版本,则使用当前系统配置的版本(如 "v2") - if not version: - version = settings.VERSION_FLAG + if not package_version: + package_version = settings.VERSION_FLAG # 优先检查指定版本的插件,即 package.v(x).json 文件中是否存在该插件,如果存在,返回该版本号 - plugins = self.get_plugins(repo_url, version) + plugins = self.get_plugins(repo_url, package_version) if pid in plugins: - return version + return package_version # 如果指定版本的插件不存在,检查全局 package.json 文件,查看插件是否兼容指定的版本 global_plugins = self.get_plugins(repo_url) plugin = global_plugins.get(pid, None) # 检查插件是否明确支持当前指定的版本(如 v2 或 v3),如果支持,返回空字符串表示使用 package.json(v1) - if plugin and plugin.get(version) is True: + if plugin and plugin.get(package_version) is True: return "" # 如果所有版本都不存在或插件不兼容,返回 None,表示插件不可用 @@ -148,7 +148,7 @@ class PluginHelper(metaclass=Singleton): json={"plugins": [{"plugin_id": plugin} for plugin in plugins]}) return True if res else False - def install(self, pid: str, repo_url: str, version: str = None) -> Tuple[bool, str]: + def install(self, pid: str, repo_url: str, package_version: str = None) -> Tuple[bool, str]: """ 安装插件,包括依赖安装和文件下载,相关资源支持自动降级策略 1. 检查并获取插件的指定版本,确认版本兼容性。 @@ -159,7 +159,7 @@ class PluginHelper(metaclass=Singleton): 6. 再次尝试安装依赖(确保安装完整) :param pid: 插件 ID :param repo_url: 插件仓库地址 - :param version: 首选插件版本 (如 "v2", "v3"),如果为空则默认使用系统配置的版本 + :param package_version: 首选插件版本 (如 "v2", "v3"),如不指定则默认使用系统配置的版本 :return: (是否成功, 错误信息) """ if SystemUtils.is_frozen(): @@ -176,21 +176,21 @@ class PluginHelper(metaclass=Singleton): user_repo = f"{user}/{repo}" - if not version: - version = settings.VERSION_FLAG + if not package_version: + package_version = settings.VERSION_FLAG # 1. 优先检查指定版本的插件 - package_version = self.get_plugin_package_version(pid, repo_url, version) + package_version = self.get_plugin_package_version(pid, repo_url, package_version) # 如果 package_version 为None,说明没有找到匹配的插件 if package_version is None: - msg = f"{pid} 没有找到适用于当前 {version} 版本的插件" + msg = f"{pid} 没有找到适用于当前 {package_version} 版本的插件" logger.debug(msg) return False, msg # package_version 为空,表示从 package.json 中找到插件 elif package_version == "": - logger.debug(f"{pid} 从 package.json 中找到适用于当前 {version} 版本插件") + logger.debug(f"{pid} 从 package.json 中找到适用于当前 {package_version} 版本插件") else: - logger.debug(f"{pid} 从 package.{version}.json 中找到适用于当前 {version} 版本插件") + logger.debug(f"{pid} 从 package.{package_version}.json 中找到适用于当前 {package_version} 版本插件") # 2. 获取插件文件列表(包括 requirements.txt) file_list, msg = self.__get_file_list(pid.lower(), user_repo, package_version) From 28f7a409f9b239fdf3b76ccb881a02e10e0889ee Mon Sep 17 00:00:00 2001 From: InfinityPacer <160988576+InfinityPacer@users.noreply.github.com> Date: Tue, 17 Sep 2024 18:31:27 +0800 Subject: [PATCH 4/4] fix(plugin): improve logging --- app/helper/plugin.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/helper/plugin.py b/app/helper/plugin.py index 7471e17e..d501c4b1 100644 --- a/app/helper/plugin.py +++ b/app/helper/plugin.py @@ -183,14 +183,14 @@ class PluginHelper(metaclass=Singleton): package_version = self.get_plugin_package_version(pid, repo_url, package_version) # 如果 package_version 为None,说明没有找到匹配的插件 if package_version is None: - msg = f"{pid} 没有找到适用于当前 {package_version} 版本的插件" + msg = f"{pid} 没有找到适用于当前版本的插件" logger.debug(msg) return False, msg # package_version 为空,表示从 package.json 中找到插件 elif package_version == "": - logger.debug(f"{pid} 从 package.json 中找到适用于当前 {package_version} 版本插件") + logger.debug(f"{pid} 从 package.json 中找到适用于当前版本的插件") else: - logger.debug(f"{pid} 从 package.{package_version}.json 中找到适用于当前 {package_version} 版本插件") + logger.debug(f"{pid} 从 package.{package_version}.json 中找到适用于当前版本的插件") # 2. 获取插件文件列表(包括 requirements.txt) file_list, msg = self.__get_file_list(pid.lower(), user_repo, package_version)