# -*- coding: utf-8 -*- """ 导出插件加载器 负责扫描和加载内置及外部插件 """ import os import sys import logging import importlib.util from typing import Dict, List, Type from .base import ExportPlugin from .simple_export import SimpleExportPlugin from .utau_oto_export import UTAUOtoExportPlugin logger = logging.getLogger(__name__) def get_builtin_plugins() -> List[Type[ExportPlugin]]: """获取内置插件列表""" return [SimpleExportPlugin, UTAUOtoExportPlugin] def load_plugins(plugins_dir: str = None) -> Dict[str, ExportPlugin]: """ 加载所有插件 参数: plugins_dir: 外部插件目录路径,默认为 export_plugins 同级目录 返回: {插件名称: 插件实例} 字典 """ plugins: Dict[str, ExportPlugin] = {} # 加载内置插件 for plugin_cls in get_builtin_plugins(): try: instance = plugin_cls() plugins[instance.name] = instance logger.info(f"加载内置插件: {instance.name}") except Exception as e: logger.error(f"加载内置插件失败: {plugin_cls.__name__}, {e}") # 加载外部插件 if plugins_dir and os.path.exists(plugins_dir): for filename in os.listdir(plugins_dir): if filename.endswith('.py') and not filename.startswith('_'): plugin_path = os.path.join(plugins_dir, filename) try: plugin = _load_plugin_from_file(plugin_path) if plugin: plugins[plugin.name] = plugin logger.info(f"加载外部插件: {plugin.name} ({filename})") except Exception as e: logger.error(f"加载外部插件失败: {filename}, {e}") return plugins def _load_plugin_from_file(filepath: str) -> ExportPlugin: """ 从文件加载插件 参数: filepath: 插件文件路径 返回: 插件实例,加载失败返回None """ try: module_name = os.path.splitext(os.path.basename(filepath))[0] spec = importlib.util.spec_from_file_location(module_name, filepath) if spec is None or spec.loader is None: return None module = importlib.util.module_from_spec(spec) sys.modules[module_name] = module spec.loader.exec_module(module) # 查找 ExportPlugin 子类 for attr_name in dir(module): attr = getattr(module, attr_name) if (isinstance(attr, type) and issubclass(attr, ExportPlugin) and attr is not ExportPlugin): return attr() return None except Exception as e: logger.error(f"加载插件文件失败: {filepath}, {e}", exc_info=True) return None