mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-05 23:47:41 +08:00
fix:优化插件分身性能
feat:分身插件删除时清理文件
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
import mimetypes
|
import mimetypes
|
||||||
|
import shutil
|
||||||
from typing import Annotated, Any, List, Optional
|
from typing import Annotated, Any, List, Optional
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, Header, HTTPException
|
from fastapi import APIRouter, Depends, Header, HTTPException
|
||||||
@@ -331,10 +332,11 @@ def reset_plugin(plugin_id: str,
|
|||||||
"""
|
"""
|
||||||
根据插件ID重置插件配置及数据
|
根据插件ID重置插件配置及数据
|
||||||
"""
|
"""
|
||||||
|
plugin_manager = PluginManager()
|
||||||
# 删除配置
|
# 删除配置
|
||||||
PluginManager().delete_plugin_config(plugin_id)
|
plugin_manager.delete_plugin_config(plugin_id)
|
||||||
# 删除插件所有数据
|
# 删除插件所有数据
|
||||||
PluginManager().delete_plugin_data(plugin_id)
|
plugin_manager.delete_plugin_data(plugin_id)
|
||||||
# 重新加载插件
|
# 重新加载插件
|
||||||
reload_plugin(plugin_id)
|
reload_plugin(plugin_id)
|
||||||
return schemas.Response(success=True)
|
return schemas.Response(success=True)
|
||||||
@@ -455,10 +457,11 @@ def set_plugin_config(plugin_id: str, conf: dict,
|
|||||||
"""
|
"""
|
||||||
更新插件配置
|
更新插件配置
|
||||||
"""
|
"""
|
||||||
|
plugin_manager = PluginManager()
|
||||||
# 保存配置
|
# 保存配置
|
||||||
PluginManager().save_plugin_config(plugin_id, conf)
|
plugin_manager.save_plugin_config(plugin_id, conf)
|
||||||
# 重新生效插件
|
# 重新生效插件
|
||||||
PluginManager().init_plugin(plugin_id, conf)
|
plugin_manager.init_plugin(plugin_id, conf)
|
||||||
# 注册插件服务
|
# 注册插件服务
|
||||||
register_plugin(plugin_id)
|
register_plugin(plugin_id)
|
||||||
return schemas.Response(success=True)
|
return schemas.Response(success=True)
|
||||||
@@ -470,20 +473,34 @@ def uninstall_plugin(plugin_id: str,
|
|||||||
"""
|
"""
|
||||||
卸载插件
|
卸载插件
|
||||||
"""
|
"""
|
||||||
|
config_oper = SystemConfigOper()
|
||||||
# 删除已安装信息
|
# 删除已安装信息
|
||||||
install_plugins = SystemConfigOper().get(SystemConfigKey.UserInstalledPlugins) or []
|
install_plugins = config_oper.get(SystemConfigKey.UserInstalledPlugins) or []
|
||||||
for plugin in install_plugins:
|
for plugin in install_plugins:
|
||||||
if plugin == plugin_id:
|
if plugin == plugin_id:
|
||||||
install_plugins.remove(plugin)
|
install_plugins.remove(plugin)
|
||||||
break
|
break
|
||||||
# 保存
|
config_oper.set(SystemConfigKey.UserInstalledPlugins, install_plugins)
|
||||||
SystemConfigOper().set(SystemConfigKey.UserInstalledPlugins, install_plugins)
|
|
||||||
# 移除插件API
|
# 移除插件API
|
||||||
remove_plugin_api(plugin_id)
|
remove_plugin_api(plugin_id)
|
||||||
# 移除插件服务
|
# 移除插件服务
|
||||||
Scheduler().remove_plugin_job(plugin_id)
|
Scheduler().remove_plugin_job(plugin_id)
|
||||||
|
# 判断是否为分身
|
||||||
|
plugin_manager = PluginManager()
|
||||||
|
plugin_class = plugin_manager.plugins.get(plugin_id)
|
||||||
|
if getattr(plugin_class, "is_clone", False):
|
||||||
|
# 如果是分身插件,则删除分身数据和配置
|
||||||
|
plugin_manager.delete_plugin_config(plugin_id)
|
||||||
|
plugin_manager.delete_plugin_data(plugin_id)
|
||||||
|
# 删除分身文件
|
||||||
|
plugin_base_dir = settings.ROOT_PATH / "app" / "plugins" / plugin_id.lower()
|
||||||
|
if plugin_base_dir.exists():
|
||||||
|
try:
|
||||||
|
shutil.rmtree(plugin_base_dir)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"删除插件分身目录 {plugin_base_dir} 失败: {str(e)}")
|
||||||
# 移除插件
|
# 移除插件
|
||||||
PluginManager().remove_plugin(plugin_id)
|
plugin_manager.remove_plugin(plugin_id)
|
||||||
return schemas.Response(success=True)
|
return schemas.Response(success=True)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+50
-25
@@ -226,13 +226,21 @@ class PluginManager(metaclass=Singleton):
|
|||||||
logger.info("插件停止完成")
|
logger.info("插件停止完成")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def running_plugins(self):
|
def running_plugins(self) -> Dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
获取运行态插件列表
|
获取运行态插件列表
|
||||||
:return: 运行态插件列表
|
:return: 运行态插件列表
|
||||||
"""
|
"""
|
||||||
return self._running_plugins
|
return self._running_plugins
|
||||||
|
|
||||||
|
@property
|
||||||
|
def plugins(self) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
获取插件列表
|
||||||
|
:return: 插件列表
|
||||||
|
"""
|
||||||
|
return self._plugins
|
||||||
|
|
||||||
def reload_monitor(self):
|
def reload_monitor(self):
|
||||||
"""
|
"""
|
||||||
重新加载插件文件修改监测
|
重新加载插件文件修改监测
|
||||||
@@ -1019,7 +1027,7 @@ class PluginManager(metaclass=Singleton):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def clone_plugin(self, plugin_id: str, suffix: str, name: str, description: str,
|
def clone_plugin(self, plugin_id: str, suffix: str, name: str, description: str,
|
||||||
version: str = None, icon: str = None) -> Tuple[bool, str]:
|
version: str = None, icon: str = None) -> Tuple[bool, str]:
|
||||||
"""
|
"""
|
||||||
创建插件分身
|
创建插件分身
|
||||||
:param plugin_id: 原插件ID
|
:param plugin_id: 原插件ID
|
||||||
@@ -1061,7 +1069,13 @@ class PluginManager(metaclass=Singleton):
|
|||||||
|
|
||||||
# 修改插件文件内容
|
# 修改插件文件内容
|
||||||
success, msg = self._modify_plugin_files(
|
success, msg = self._modify_plugin_files(
|
||||||
clone_plugin_dir, plugin_id, clone_id, suffix, name, description, version, icon
|
plugin_dir=clone_plugin_dir,
|
||||||
|
original_id=plugin_id,
|
||||||
|
suffix=suffix,
|
||||||
|
name=name,
|
||||||
|
description=description,
|
||||||
|
version=version,
|
||||||
|
icon=icon
|
||||||
)
|
)
|
||||||
|
|
||||||
if not success:
|
if not success:
|
||||||
@@ -1083,16 +1097,17 @@ class PluginManager(metaclass=Singleton):
|
|||||||
# 复制原插件配置作为分身插件的初始配置
|
# 复制原插件配置作为分身插件的初始配置
|
||||||
clone_config = original_config.copy()
|
clone_config = original_config.copy()
|
||||||
# 可以在这里修改一些默认值,比如禁用分身插件
|
# 可以在这里修改一些默认值,比如禁用分身插件
|
||||||
clone_config['enable'] = False # 默认禁用分身插件,让用户手动配置
|
# 默认禁用分身插件,让用户手动配置
|
||||||
|
clone_config['enable'] = False
|
||||||
|
clone_config['enabled'] = False
|
||||||
self.save_plugin_config(clone_id, clone_config)
|
self.save_plugin_config(clone_id, clone_config)
|
||||||
logger.info(f"已为分身插件 {clone_id} 设置初始配置")
|
logger.info(f"已为分身插件 {clone_id} 设置初始配置")
|
||||||
else:
|
else:
|
||||||
logger.info(f"原插件 {plugin_id} 没有配置,分身插件 {clone_id} 将使用默认配置")
|
logger.info(f"原插件 {plugin_id} 没有配置,分身插件 {clone_id} 将使用默认配置")
|
||||||
|
|
||||||
# 重新初始化插件系统以完全注册新插件
|
# 注册分身插件的API和服务
|
||||||
logger.info(f"正在重新注册插件系统以识别分身插件 {clone_id}...")
|
logger.info(f"正在注册分身插件 {clone_id} ...")
|
||||||
self.init_config()
|
PluginManager().reload_plugin(plugin_id)
|
||||||
|
|
||||||
# 确保分身插件正确初始化配置
|
# 确保分身插件正确初始化配置
|
||||||
if clone_id in self._running_plugins:
|
if clone_id in self._running_plugins:
|
||||||
clone_instance = self._running_plugins[clone_id]
|
clone_instance = self._running_plugins[clone_id]
|
||||||
@@ -1102,11 +1117,6 @@ class PluginManager(metaclass=Singleton):
|
|||||||
clone_instance.init_plugin(clone_config)
|
clone_instance.init_plugin(clone_config)
|
||||||
logger.info(f"分身插件 {clone_id} 配置重新初始化完成")
|
logger.info(f"分身插件 {clone_id} 配置重新初始化完成")
|
||||||
|
|
||||||
# 注册分身插件的API和服务
|
|
||||||
logger.info(f"正在注册分身插件 {clone_id} 的API和服务...")
|
|
||||||
from app.api.endpoints.plugin import register_plugin
|
|
||||||
register_plugin(clone_id)
|
|
||||||
|
|
||||||
logger.info(f"插件分身 {clone_id} 创建成功")
|
logger.info(f"插件分身 {clone_id} 创建成功")
|
||||||
return True, "插件分身创建成功"
|
return True, "插件分身创建成功"
|
||||||
|
|
||||||
@@ -1114,14 +1124,13 @@ class PluginManager(metaclass=Singleton):
|
|||||||
logger.error(f"创建插件分身失败:{str(e)}")
|
logger.error(f"创建插件分身失败:{str(e)}")
|
||||||
return False, f"创建插件分身失败:{str(e)}"
|
return False, f"创建插件分身失败:{str(e)}"
|
||||||
|
|
||||||
def _modify_plugin_files(self, plugin_dir: Path, original_id: str, clone_id: str,
|
def _modify_plugin_files(self, plugin_dir: Path, original_id: str, suffix: str,
|
||||||
suffix: str, name: str, description: str, version: str = None,
|
name: str, description: str, version: str = None,
|
||||||
icon: str = None) -> Tuple[bool, str]:
|
icon: str = None) -> Tuple[bool, str]:
|
||||||
"""
|
"""
|
||||||
修改插件文件中的类名和相关信息
|
修改插件文件中的类名和相关信息
|
||||||
:param plugin_dir: 插件目录
|
:param plugin_dir: 插件目录
|
||||||
:param original_id: 原插件ID
|
:param original_id: 原插件ID
|
||||||
:param clone_id: 分身插件ID
|
|
||||||
:param suffix: 分身后缀
|
:param suffix: 分身后缀
|
||||||
:param name: 分身名称
|
:param name: 分身名称
|
||||||
:param description: 分身描述
|
:param description: 分身描述
|
||||||
@@ -1143,7 +1152,13 @@ class PluginManager(metaclass=Singleton):
|
|||||||
init_file = plugin_dir / "__init__.py"
|
init_file = plugin_dir / "__init__.py"
|
||||||
if init_file.exists():
|
if init_file.exists():
|
||||||
success, msg = self._modify_python_file(
|
success, msg = self._modify_python_file(
|
||||||
init_file, original_class_name, clone_class_name, name, description, version, icon
|
file_path=init_file,
|
||||||
|
original_class_name=original_class_name,
|
||||||
|
clone_class_name=clone_class_name,
|
||||||
|
name=name,
|
||||||
|
description=description,
|
||||||
|
version=version,
|
||||||
|
icon=icon
|
||||||
)
|
)
|
||||||
if not success:
|
if not success:
|
||||||
return False, msg
|
return False, msg
|
||||||
@@ -1152,7 +1167,9 @@ class PluginManager(metaclass=Singleton):
|
|||||||
dist_dir = plugin_dir / "dist"
|
dist_dir = plugin_dir / "dist"
|
||||||
if dist_dir.exists():
|
if dist_dir.exists():
|
||||||
success, msg = self._modify_federation_files(
|
success, msg = self._modify_federation_files(
|
||||||
dist_dir, original_class_name, clone_class_name
|
dist_dir=dist_dir,
|
||||||
|
original_class_name=original_class_name,
|
||||||
|
clone_class_name=clone_class_name
|
||||||
)
|
)
|
||||||
if not success:
|
if not success:
|
||||||
return False, msg
|
return False, msg
|
||||||
@@ -1163,9 +1180,10 @@ class PluginManager(metaclass=Singleton):
|
|||||||
logger.error(f"修改插件文件失败:{str(e)}")
|
logger.error(f"修改插件文件失败:{str(e)}")
|
||||||
return False, f"修改插件文件失败:{str(e)}"
|
return False, f"修改插件文件失败:{str(e)}"
|
||||||
|
|
||||||
def _modify_python_file(self, file_path: Path, original_class_name: str,
|
@staticmethod
|
||||||
clone_class_name: str, name: str, description: str,
|
def _modify_python_file(file_path: Path, original_class_name: str,
|
||||||
version: str = None, icon: str = None) -> Tuple[bool, str]:
|
clone_class_name: str, name: str, description: str,
|
||||||
|
version: str = None, icon: str = None) -> Tuple[bool, str]:
|
||||||
"""
|
"""
|
||||||
修改Python文件中的类名和插件信息
|
修改Python文件中的类名和插件信息
|
||||||
"""
|
"""
|
||||||
@@ -1225,6 +1243,12 @@ class PluginManager(metaclass=Singleton):
|
|||||||
else:
|
else:
|
||||||
logger.info("未提供自定义图标,保持原插件图标")
|
logger.info("未提供自定义图标,保持原插件图标")
|
||||||
|
|
||||||
|
# 添加分身标志
|
||||||
|
if "def init_plugin(self" in content:
|
||||||
|
init_index = content.index("def init_plugin(self")
|
||||||
|
# 在 def init_plugin(self 前添加 is_clone = True
|
||||||
|
content = content[:init_index] + "is_clone = True\n\n " + content[init_index:]
|
||||||
|
|
||||||
with open(file_path, 'w', encoding='utf-8') as f:
|
with open(file_path, 'w', encoding='utf-8') as f:
|
||||||
f.write(content)
|
f.write(content)
|
||||||
|
|
||||||
@@ -1236,7 +1260,7 @@ class PluginManager(metaclass=Singleton):
|
|||||||
return False, f"修改Python文件失败:{str(e)}"
|
return False, f"修改Python文件失败:{str(e)}"
|
||||||
|
|
||||||
def _modify_federation_files(self, dist_dir: Path, original_class_name: str,
|
def _modify_federation_files(self, dist_dir: Path, original_class_name: str,
|
||||||
clone_class_name: str) -> Tuple[bool, str]:
|
clone_class_name: str) -> Tuple[bool, str]:
|
||||||
"""
|
"""
|
||||||
修改联邦插件的前端文件
|
修改联邦插件的前端文件
|
||||||
"""
|
"""
|
||||||
@@ -1303,7 +1327,8 @@ class PluginManager(metaclass=Singleton):
|
|||||||
logger.error(f"修改联邦插件文件失败:{str(e)}")
|
logger.error(f"修改联邦插件文件失败:{str(e)}")
|
||||||
return False, f"修改联邦插件文件失败:{str(e)}"
|
return False, f"修改联邦插件文件失败:{str(e)}"
|
||||||
|
|
||||||
def _rename_federation_assets(self, dist_dir: Path, original_class_name: str, clone_class_name: str):
|
@staticmethod
|
||||||
|
def _rename_federation_assets(dist_dir: Path, original_class_name: str, clone_class_name: str):
|
||||||
"""
|
"""
|
||||||
重命名联邦插件的资源文件,避免文件名冲突
|
重命名联邦插件的资源文件,避免文件名冲突
|
||||||
"""
|
"""
|
||||||
@@ -1328,5 +1353,5 @@ class PluginManager(metaclass=Singleton):
|
|||||||
logger.debug(f"重命名联邦插件文件:{file_name} -> {new_name}")
|
logger.debug(f"重命名联邦插件文件:{file_name} -> {new_name}")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"重命名联邦插件资源文件失败:{str(e)}")
|
|
||||||
# 重命名失败不影响整体流程
|
# 重命名失败不影响整体流程
|
||||||
|
logger.warning(f"重命名联邦插件资源文件失败:{str(e)}")
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ class _PluginBase(metaclass=ABCMeta):
|
|||||||
plugin_desc: Optional[str] = ""
|
plugin_desc: Optional[str] = ""
|
||||||
# 插件顺序
|
# 插件顺序
|
||||||
plugin_order: Optional[int] = 9999
|
plugin_order: Optional[int] = 9999
|
||||||
|
# 是否为插件分身
|
||||||
|
is_clone: bool = False
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
# 插件数据
|
# 插件数据
|
||||||
|
|||||||
Reference in New Issue
Block a user