【剪映小助手源码精讲】14_模板模式与扩展机制

第14章:模板模式与扩展机制

14.1 模板模式概述

模板模式是剪映小助手中实现代码复用和扩展性的核心设计模式。通过定义算法的骨架,将具体实现延迟到子类中,模板模式使得系统能够在保持整体结构稳定的同时,灵活地扩展各种功能。本章将详细介绍模板模式在剪映小助手中的应用和实现。

14.1.1 模板模式的核心思想

模板模式(Template Method Pattern)是一种行为设计模式,它定义了一个操作中的算法骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。

在视频编辑软件中,模板模式的应用场景包括:

  • 渲染管线:定义渲染的基本流程,具体的渲染算法由子类实现
  • 效果处理:定义效果处理的基本步骤,具体的效果算法由子类实现
  • 文件导入导出:定义文件处理的基本流程,具体的格式处理由子类实现
  • 用户界面:定义UI组件的基本行为,具体的界面实现由子类完成

14.1.2 模板模式的优势

代码复用:通过将公共代码提取到父类中,避免了代码重复
扩展性强:新的功能可以通过继承和重写模板方法来实现
维护性好:算法的整体结构集中在父类中,便于维护和理解
灵活性高:可以在运行时选择不同的具体实现

14.2 模板模式基础架构

14.2.1 抽象模板基类

首先,我们定义一个抽象模板基类,为所有的模板实现提供统一的接口:

from abc import ABC, abstractmethod
from typing import Dict, Any, Optional, List, Callable
from enum import Enum
import time
import logging
from datetime import datetime

class TemplateStatus(Enum):
    """模板状态枚举"""
    IDLE = "idle"
    PREPARING = "preparing"
    PROCESSING = "processing"
    COMPLETED = "completed"
    FAILED = "failed"
    CANCELLED = "cancelled"

class TemplatePriority(Enum):
    """模板优先级枚举"""
    LOW = 1
    NORMAL = 2
    HIGH = 3
    CRITICAL = 4

class TemplateContext:
    """模板上下文"""
    
    def __init__(self, template_id: str, **kwargs):
        self.template_id = template_id
        self.start_time: Optional[datetime] = None
        self.end_time: Optional[datetime] = None
        self.duration: float = 0.0
        self.status = TemplateStatus.IDLE
        self.priority = TemplatePriority.NORMAL
        self.input_data: Dict[str, Any] = kwargs
        self.output_data: Dict[str, Any] = {}
        self.metadata: Dict[str, Any] = {}
        self.errors: List[str] = []
        self.warnings: List[str] = []
        self.progress: float = 0.0
        self.step_name: str = ""
        self.step_progress: float = 0.0
        self.memory_usage: int = 0
        self.cpu_usage: float = 0.0
        self.custom_metrics: Dict[str, Any] = {}
        self.user_data: Dict[str, Any] = {}
    
    def set_status(self, status: TemplateStatus) -> None:
        """设置状态"""
        old_status = self.status
        self.status = status
        
        # 更新时间戳
        if status == TemplateStatus.PROCESSING and not self.start_time:
            self.start_time = datetime.now()
        elif status in [TemplateStatus.COMPLETED, TemplateStatus.FAILED, TemplateStatus.CANCELLED]:
            self.end_time = datetime.now()
            if self.start_time:
                self.duration = (self.end_time - self.start_time).total_seconds()
    
    def add_error(self, error: str) -> None:
        """添加错误"""
        self.errors.append(error)
        self.set_status(TemplateStatus.FAILED)
    
    def add_warning(self, warning: str) -> None:
        """添加警告"""
        self.warnings.append(warning)
    
    def set_progress(self, progress: float) -> None:
        """设置进度"""
        self.progress = max(0.0, min(1.0, progress))
    
    def set_step_progress(self, step_name: str, progress: float) -> None:
        """设置步骤进度"""
        self.step_name = step_name
        self.step_progress = max(0.0, min(1.0, progress))
    
    def update_metrics(self, memory_usage: int = 0, cpu_usage: float = 0.0) -> None:
        """更新性能指标"""
        if memory_usage > 0:
            self.memory_usage = memory_usage
        if cpu_usage > 0:
            self.cpu_usage = cpu_usage
    
    def add_custom_metric(self, name: str, value: Any) -> None:
        """添加自定义指标"""
        self.custom_metrics[name] = value
    
    def get_metric(self, name: str, default: Any = None) -> Any:
        """获取指标"""
        return self.custom_metrics.get(name, default)
    
    def to_dict(self) -> Dict[str, Any]:
        """转换为字典"""
        return {
            "template_id": self.template_id,
            "start_time": self.start_time.isoformat() if self.start_time else None,
            "end_time": self.end_time.isoformat() if self.end_time else None,
            "duration": self.duration,
            "status": self.status.value,
            "priority": self.priority.value,
            "input_data": self.input_data,
            "output_data": self.output_data,
            "metadata": self.metadata,
            "errors": self.errors,
            "warnings": self.warnings,
            "progress": self.progress,
            "step_name": self.step_name,
            "step_progress": self.step_progress,
            "memory_usage": self.memory_usage,
            "cpu_usage": self.cpu_usage,
            "custom_metrics": self.custom_metrics,
            "user_data": self.user_data
        }

class BaseTemplate(ABC):
    """抽象模板基类"""
    
    def __init__(self, template_id: str, name: str = "", description: str = ""):
        self.template_id = template_id
        self.name = name
        self.description = description
        self.version = "1.0.0"
        self.created_at = datetime.now()
        self.updated_at = datetime.now()
        self.is_enabled = True
        self.priority = TemplatePriority.NORMAL
        self.dependencies: List[str] = []
        self.required_resources: List[str] = []
        self.supported_formats: List[str] = []
        self.logger = logging.getLogger(f"{__name__}.{self.__class__.__name__}")
        
        # 性能配置
        self.timeout_seconds = 300  # 5分钟超时
        self.retry_count = 3
        self.retry_delay = 1.0
        self.memory_limit_mb = 1024  # 1GB内存限制
        self.cpu_limit_percent = 80  # 80% CPU限制
        
        # 回调函数
        self.progress_callback: Optional[Callable[[TemplateContext], None]] = None
        self.completion_callback: Optional[Callable[[TemplateContext], None]] = None
        self.error_callback: Optional[Callable[[TemplateContext, Exception], None]] = None
    
    def execute(self, context: TemplateContext) -> bool:
        """执行模板(模板方法)"""
        self.logger.info(f"开始执行模板: {self.name} (ID: {self.template_id})")
        
        try:
            # 步骤1:预处理
            context.set_status(TemplateStatus.PREPARING)
            context.set_step_progress("preprocessing", 0.1)
            if not self._preprocess(context):
                return False
            
            # 步骤2:验证输入
            context.set_step_progress("validation", 0.2)
            if not self._validate_input(context):
                return False
            
            # 步骤3:准备资源
            context.set_step_progress("resource_preparation", 0.3)
            if not self._prepare_resources(context):
                return False
            
            # 步骤4:执行主要逻辑
            context.set_status(TemplateStatus.PROCESSING)
            context.set_step_progress("main_processing", 0.5)
            if not self._main_process(context):
                return False
            
            # 步骤5:后处理
            context.set_step_progress("postprocessing", 0.8)
            if not self._postprocess(context):
                return False
            
            # 步骤6:验证输出
            context.set_step_progress("output_validation", 0.9)
            if not self._validate_output(context):
                return False
            
            # 完成
            context.set_status(TemplateStatus.COMPLETED)
            context.set_progress(1.0)
            context.set_step_progress("completed", 1.0)
            
            self.logger.info(f"模板执行成功: {self.name} (ID: {self.template_id})")
            
            # 调用完成回调
            if self.completion_callback:
                self.completion_callback(context)
            
            return True
        
        except Exception as e:
            self.logger.error(f"模板执行失败: {self.name} (ID: {self.template_id}), 错误: {e}")
            context.add_error(str(e))
            context.set_status(TemplateStatus.FAILED)
            
            # 调用错误回调
            if self.error_callback:
                self.error_callback(context, e)
            
            return False
    
    def _preprocess(self, context: TemplateContext) -> bool:
        """预处理步骤"""
        self.logger.debug("执行预处理")
        
        # 检查依赖
        if not self._check_dependencies(context):
            return False
        
        # 检查资源
        if not self._check_resources(context):
            return False
        
        # 初始化性能监控
        context.update_metrics()
        
        return True
    
    def _validate_input(self, context: TemplateContext) -> bool:
        """验证输入数据"""
        self.logger.debug("验证输入数据")
        
        # 检查必需参数
        required_params = self.get_required_parameters()
        for param in required_params:
            if param not in context.input_data:
                context.add_error(f"缺少必需参数: {param}")
                return False
        
        # 验证参数格式
        return self._validate_parameters(context)
    
    def _prepare_resources(self, context: TemplateContext) -> bool:
        """准备资源"""
        self.logger.debug("准备资源")
        
        # 分配内存
        if not self._allocate_memory(context):
            return False
        
        # 加载必需资源
        for resource in self.required_resources:
            if not self._load_resource(context, resource):
                context.add_error(f"资源加载失败: {resource}")
                return False
        
        return True
    
    @abstractmethod
    def _main_process(self, context: TemplateContext) -> bool:
        """主要处理逻辑(抽象方法)"""
        pass
    
    def _postprocess(self, context: TemplateContext) -> bool:
        """后处理步骤"""
        self.logger.debug("执行后处理")
        
        # 释放资源
        self._release_resources(context)
        
        # 清理临时数据
        self._cleanup(context)
        
        # 更新性能指标
        context.update_metrics()
        
        return True
    
    def _validate_output(self, context: TemplateContext) -> bool:
        """验证输出数据"""
        self.logger.debug("验证输出数据")
        
        # 检查输出格式
        if not self._check_output_format(context):
            return False
        
        # 验证输出完整性
        return self._validate_output_integrity(context)
    
    def _check_dependencies(self, context: TemplateContext) -> bool:
        """检查依赖"""
        for dependency in self.dependencies:
            if not self._check_dependency(context, dependency):
                context.add_error(f"依赖检查失败: {dependency}")
                return False
        return True
    
    def _check_resources(self, context: TemplateContext) -> bool:
        """检查资源"""
        # 检查内存限制
        if context.memory_usage > self.memory_limit_mb * 1024 * 1024:
            context.add_error(f"内存使用超出限制: {context.memory_usage} > {self.memory_limit_mb}MB")
            return False
        
        return True
    
    def _allocate_memory(self, context: TemplateContext) -> bool:
        """分配内存"""
        try:
            # 估算内存需求
            estimated_memory = self._estimate_memory_usage(context)
            
            # 检查内存限制
            if estimated_memory > self.memory_limit_mb * 1024 * 1024:
                context.add_error(f"预估内存使用超出限制: {estimated_memory} > {self.memory_limit_mb}MB")
                return False
            
            # 实际分配内存的逻辑在这里实现
            context.update_metrics(memory_usage=estimated_memory)
            return True
        
        except Exception as e:
            context.add_error(f"内存分配失败: {e}")
            return False
    
    def _load_resource(self, context: TemplateContext, resource: str) -> bool:
        """加载资源"""
        try:
            # 资源加载的具体实现
            self.logger.debug(f"加载资源: {resource}")
            return True
        except Exception as e:
            self.logger.error(f"资源加载失败: {resource}, 错误: {e}")
            return False
    
    def _release_resources(self, context: TemplateContext) -> None:
        """释放资源"""
        self.logger.debug("释放资源")
        # 资源释放的具体实现
    
    def _cleanup(self, context: TemplateContext) -> None:
        """清理临时数据"""
        self.logger.debug("清理临时数据")
        # 清理逻辑的具体实现
    
    def _check_output_format(self, context: TemplateContext) -> bool:
        """检查输出格式"""
        # 检查输出格式是否符合要求
        return True
    
    def _validate_output_integrity(self, context: TemplateContext) -> bool:
        """验证输出完整性"""
        # 验证输出数据的完整性
        return True
    
    def _validate_parameters(self, context: TemplateContext) -> bool:
        """验证参数"""
        # 参数验证的具体实现
        return True
    
    def _estimate_memory_usage(self, context: TemplateContext) -> int:
        """估算内存使用"""
        # 内存使用估算的具体实现
        return 0
    
    def _check_dependency(self, context: TemplateContext, dependency: str) -> bool:
        """检查单个依赖"""
        # 依赖检查的具体实现
        return True
    
    def get_required_parameters(self) -> List[str]:
        """获取必需参数列表"""
        return []
    
    def set_progress_callback(self, callback: Callable[[TemplateContext], None]) -> None:
        """设置进度回调"""
        self.progress_callback = callback
    
    def set_completion_callback(self, callback: Callable[[TemplateContext], None]) -> None:
        """设置完成回调"""
        self.completion_callback = callback
    
    def set_error_callback(self, callback: Callable[[TemplateContext, Exception], None]) -> None:
        """设置错误回调"""
        self.error_callback = callback
    
    def cancel(self, context: TemplateContext) -> None:
        """取消执行"""
        context.set_status(TemplateStatus.CANCELLED)
        self.logger.info(f"模板执行已取消: {self.name}")
    
    def is_supported_format(self, format_name: str) -> bool:
        """检查是否支持指定格式"""
        return format_name in self.supported_formats
    
    def add_dependency(self, dependency: str) -> None:
        """添加依赖"""
        if dependency not in self.dependencies:
            self.dependencies.append(dependency)
    
    def remove_dependency(self, dependency: str) -> None:
        """移除依赖"""
        if dependency in self.dependencies:
            self.dependencies.remove(dependency)
    
    def add_required_resource(self, resource: str) -> None:
        """添加必需资源"""
        if resource not in self.required_resources:
            self.required_resources.append(resource)
    
    def remove_required_resource(self, resource: str) -> None:
        """移除必需资源"""
        if resource in self.required_resources:
            self.required_resources.remove(resource)
    
    def add_supported_format(self, format_name: str) -> None:
        """添加支持的格式"""
        if format_name not in self.supported_formats:
            self.supported_formats.append(format_name)
    
    def remove_supported_format(self, format_name: str) -> None:
        """移除支持的格式"""
        if format_name in self.supported_formats:
            self.supported_formats.remove(format_name)
    
    def to_dict(self) -> Dict[str, Any]:
        """转换为字典"""
        return {
            "template_id": self.template_id,
            "name": self.name,
            "description": self.description,
            "version": self.version,
            "created_at": self.created_at.isoformat(),
            "updated_at": self.updated_at.isoformat(),
            "is_enabled": self.is_enabled,
            "priority": self.priority.value,
            "dependencies": self.dependencies,
            "required_resources": self.required_resources,
            "supported_formats": self.supported_formats,
            "timeout_seconds": self.timeout_seconds,
            "retry_count": self.retry_count,
            "retry_delay": self.retry_delay,
            "memory_limit_mb": self.memory_limit_mb,
            "cpu_limit_percent": self.cpu_limit_percent
        }

14.2.2 模板管理器实现

模板管理器负责管理所有的模板实例,提供模板的注册、查找和执行功能:

import threading
from typing import Dict, List, Optional, Type, Any
from concurrent.futures import ThreadPoolExecutor, Future
import uuid

class TemplateManager:
    """模板管理器"""
    
    def __init__(self, max_workers: int = 4):
        self.templates: Dict[str, BaseTemplate] = {}
        self.template_types: Dict[str, Type[BaseTemplate]] = {}
        self.active_executions: Dict[str, TemplateContext] = {}
        self.execution_history: List[TemplateContext] = []
        
        # 线程安全
        self.templates_lock = threading.RLock()
        self.executions_lock = threading.RLock()
        self.history_lock = threading.RLock()
        
        # 线程池
        self.executor = ThreadPoolExecutor(max_workers=max_workers)
        
        # 统计信息
        self.stats = {
            "total_executions": 0,
            "successful_executions": 0,
            "failed_executions": 0,
            "cancelled_executions": 0,
            "average_execution_time": 0.0,
            "active_templates": 0
        }
        
        # 日志
        self.logger = logging.getLogger(__name__)
    
    def register_template(self, template: BaseTemplate) -> bool:
        """注册模板"""
        try:
            with self.templates_lock:
                if template.template_id in self.templates:
                    self.logger.warning(f"模板已存在: {template.template_id}")
                    return False
                
                self.templates[template.template_id] = template
                self.stats["active_templates"] = len(self.templates)
                
                self.logger.info(f"模板注册成功: {template.name} (ID: {template.template_id})")
                return True
        
        except Exception as e:
            self.logger.error(f"模板注册失败: {e}")
            return False
    
    def register_template_type(self, template_type: Type[BaseTemplate], 
                              type_name: str) -> bool:
        """注册模板类型"""
        try:
            with self.templates_lock:
                if type_name in self.template_types:
                    self.logger.warning(f"模板类型已存在: {type_name}")
                    return False
                
                self.template_types[type_name] = template_type
                self.logger.info(f"模板类型注册成功: {type_name}")
                return True
        
        except Exception as e:
            self.logger.error(f"模板类型注册失败: {e}")
            return False
    
    def create_template(self, template_type: str, template_id: str, 
                       name: str = "", description: str = "") -> Optional[BaseTemplate]:
        """创建模板实例"""
        try:
            with self.templates_lock:
                if template_type not in self.template_types:
                    self.logger.error(f"模板类型不存在: {template_type}")
                    return None
                
                template_class = self.template_types[template_type]
                template = template_class(template_id, name, description)
                
                if self.register_template(template):
                    return template
                else:
                    return None
        
        except Exception as e:
            self.logger.error(f"模板创建失败: {e}")
            return None
    
    def get_template(self, template_id: str) -> Optional[BaseTemplate]:
        """获取模板"""
        try:
            with self.templates_lock:
                return self.templates.get(template_id)
        
        except Exception as e:
            self.logger.error(f"模板获取失败: {e}")
            return None
    
    def get_templates_by_type(self, template_type: str) -> List[BaseTemplate]:
        """按类型获取模板"""
        try:
            with self.templates_lock:
                return [t for t in self.templates.values() if t.__class__.__name__ == template_type]
        
        except Exception as e:
            self.logger.error(f"按类型获取模板失败: {e}")
            return []
    
    def execute_template(self, template_id: str, input_data: Dict[str, Any],
                        priority: TemplatePriority = TemplatePriority.NORMAL,
                        async_execution: bool = True) -> Optional[TemplateContext]:
        """执行模板"""
        try:
            # 获取模板
            template = self.get_template(template_id)
            if not template:
                self.logger.error(f"模板不存在: {template_id}")
                return None
            
            # 创建上下文
            context = TemplateContext(template_id, **input_data)
            context.priority = priority
            
            # 记录执行开始
            with self.executions_lock:
                execution_id = str(uuid.uuid4())
                self.active_executions[execution_id] = context
            
            # 执行模板
            if async_execution:
                # 异步执行
                future = self.executor.submit(self._execute_template_internal, template, context, execution_id)
                context.metadata["future"] = future
                return context
            else:
                # 同步执行
                success = self._execute_template_internal(template, context, execution_id)
                return context if success else None
        
        except Exception as e:
            self.logger.error(f"模板执行失败: {e}")
            return None
    
    def _execute_template_internal(self, template: BaseTemplate, 
                                 context: TemplateContext, execution_id: str) -> bool:
        """内部模板执行"""
        try:
            self.stats["total_executions"] += 1
            start_time = time.time()
            
            # 设置进度回调
            def progress_callback(ctx: TemplateContext):
                self.logger.debug(f"模板进度更新: {ctx.template_id} - {ctx.progress:.2%}")
            
            template.set_progress_callback(progress_callback)
            
            # 执行模板
            success = template.execute(context)
            
            # 更新统计
            execution_time = time.time() - start_time
            self._update_stats(success, execution_time)
            
            # 记录执行历史
            with self.history_lock:
                self.execution_history.append(context)
                # 限制历史记录数量
                if len(self.execution_history) > 1000:
                    self.execution_history = self.execution_history[-1000:]
            
            # 从活动执行中移除
            with self.executions_lock:
                if execution_id in self.active_executions:
                    del self.active_executions[execution_id]
            
            return success
        
        except Exception as e:
            self.logger.error(f"模板内部执行失败: {e}")
            context.add_error(str(e))
            context.set_status(TemplateStatus.FAILED)
            
            # 从活动执行中移除
            with self.executions_lock:
                if execution_id in self.active_executions:
                    del self.active_executions[execution_id]
            
            return False
    
    def cancel_execution(self, execution_id: str) -> bool:
        """取消执行"""
        try:
            with self.executions_lock:
                if execution_id not in self.active_executions:
                    return False
                
                context = self.active_executions[execution_id]
                template = self.get_template(context.template_id)
                
                if template:
                    template.cancel(context)
                
                context.set_status(TemplateStatus.CANCELLED)
                del self.active_executions[execution_id]
                
                self.stats["cancelled_executions"] += 1
                return True
        
        except Exception as e:
            self.logger.error(f"取消执行失败: {e}")
            return False
    
    def get_execution_status(self, execution_id: str) -> Optional[TemplateContext]:
        """获取执行状态"""
        try:
            with self.executions_lock:
                return self.active_executions.get(execution_id)
        
        except Exception as e:
            self.logger.error(f"获取执行状态失败: {e}")
            return None
    
    def get_active_executions(self) -> Dict[str, TemplateContext]:
        """获取所有活动执行"""
        try:
            with self.executions_lock:
                return self.active_executions.copy()
        
        except Exception as e:
            self.logger.error(f"获取活动执行失败: {e}")
            return {}
    
    def get_execution_history(self, limit: int = 100) -> List[TemplateContext]:
        """获取执行历史"""
        try:
            with self.history_lock:
                return self.execution_history[-limit:]
        
        except Exception as e:
            self.logger.error(f"获取执行历史失败: {e}")
            return []
    
    def _update_stats(self, success: bool, execution_time: float) -> None:
        """更新统计信息"""
        if success:
            self.stats["successful_executions"] += 1
        else:
            self.stats["failed_executions"] += 1
        
        # 更新平均执行时间
        current_avg = self.stats["average_execution_time"]
        total_executions = self.stats["total_executions"]
        self.stats["average_execution_time"] = (current_avg * (total_executions - 1) + execution_time) / total_executions
    
    def get_stats(self) -> Dict[str, Any]:
        """获取统计信息"""
        return self.stats.copy()
    
    def get_template_stats(self, template_id: str) -> Dict[str, Any]:
        """获取模板统计信息"""
        try:
            template_stats = {
                "total_executions": 0,
                "successful_executions": 0,
                "failed_executions": 0,
                "average_execution_time": 0.0,
                "last_execution": None
            }
            
            with self.history_lock:
                relevant_history = [ctx for ctx in self.execution_history if ctx.template_id == template_id]
            
            if not relevant_history:
                return template_stats
            
            total_time = 0.0
            for context in relevant_history:
                template_stats["total_executions"] += 1
                total_time += context.duration
                
                if context.status == TemplateStatus.COMPLETED:
                    template_stats["successful_executions"] += 1
                elif context.status == TemplateStatus.FAILED:
                    template_stats["failed_executions"] += 1
            
            template_stats["average_execution_time"] = total_time / len(relevant_history)
            template_stats["last_execution"] = relevant_history[-1].end_time
            
            return template_stats
        
        except Exception as e:
            self.logger.error(f"获取模板统计信息失败: {e}")
            return {}
    
    def unregister_template(self, template_id: str) -> bool:
        """注销模板"""
        try:
            with self.templates_lock:
                if template_id not in self.templates:
                    return False
                
                del self.templates[template_id]
                self.stats["active_templates"] = len(self.templates)
                
                self.logger.info(f"模板注销成功: {template_id}")
                return True
        
        except Exception as e:
            self.logger.error(f"模板注销失败: {e}")
            return False
    
    def clear_history(self) -> None:
        """清空执行历史"""
        try:
            with self.history_lock:
                self.execution_history.clear()
            
            self.logger.info("执行历史已清空")
        
        except Exception as e:
            self.logger.error(f"清空执行历史失败: {e}")
    
    def shutdown(self) -> None:
        """关闭管理器"""
        try:
            # 取消所有活动执行
            with self.executions_lock:
                for execution_id in list(self.active_executions.keys()):
                    self.cancel_execution(execution_id)
            
            # 关闭线程池
            self.executor.shutdown(wait=True)
            
            # 清空缓存
            self.templates.clear()
            self.template_types.clear()
            
            self.logger.info("模板管理器已关闭")
        
        except Exception as e:
            self.logger.error(f"关闭模板管理器失败: {e}")

14.3 具体模板实现

14.3.1 视频渲染模板

视频渲染模板是剪映小助手中最重要的模板之一,它负责将编辑好的视频项目渲染成最终的视频文件:

class VideoRenderTemplate(BaseTemplate):
    """视频渲染模板"""
    
    def __init__(self, template_id: str, name: str = "视频渲染", description: str = ""):
        super().__init__(template_id, name, description)
        self.description = "将视频项目渲染为最终视频文件"
        
        # 渲染配置
        self.render_config = {
            "video_codec": "h264",
            "audio_codec": "aac",
            "video_bitrate": "1000k",
            "audio_bitrate": "128k",
            "resolution": "1920x1080",
            "frame_rate": 30,
            "pixel_format": "yuv420p",
            "preset": "medium",
            "crf": 23
        }
        
        # 支持的格式
        self.supported_formats = ["mp4", "mov", "avi", "mkv", "webm"]
        
        # 依赖
        self.dependencies = ["ffmpeg", "video_encoder", "audio_encoder"]
        
        # 必需资源
        self.required_resources = ["video_memory", "disk_space", "cpu_cores"]
    
    def get_required_parameters(self) -> List[str]:
        """获取必需参数"""
        return ["project_file", "output_path", "output_format"]
    
    def _validate_input(self, context: TemplateContext) -> bool:
        """验证输入"""
        if not super()._validate_input(context):
            return False
        
        # 验证项目文件
        project_file = context.input_data.get("project_file")
        if not project_file or not os.path.exists(project_file):
            context.add_error(f"项目文件不存在: {project_file}")
            return False
        
        # 验证输出格式
        output_format = context.input_data.get("output_format")
        if output_format not in self.supported_formats:
            context.add_error(f"不支持的输出格式: {output_format}")
            return False
        
        # 验证输出路径
        output_path = context.input_data.get("output_path")
        output_dir = os.path.dirname(output_path)
        if not os.path.exists(output_dir):
            try:
                os.makedirs(output_dir)
            except Exception as e:
                context.add_error(f"创建输出目录失败: {e}")
                return False
        
        return True
    
    def _main_process(self, context: TemplateContext) -> bool:
        """主要处理逻辑"""
        try:
            self.logger.info("开始视频渲染")
            
            # 加载项目
            context.set_step_progress("loading_project", 0.1)
            project_data = self._load_project(context)
            if not project_data:
                return False
            
            # 准备渲染管线
            context.set_step_progress("preparing_pipeline", 0.2)
            render_pipeline = self._prepare_render_pipeline(context, project_data)
            if not render_pipeline:
                return False
            
            # 渲染视频轨道
            context.set_step_progress("rendering_video", 0.4)
            video_frames = self._render_video_tracks(context, project_data, render_pipeline)
            if not video_frames:
                return False
            
            # 渲染音频轨道
            context.set_step_progress("rendering_audio", 0.6)
            audio_data = self._render_audio_tracks(context, project_data, render_pipeline)
            if audio_data is None:
                return False
            
            # 合成最终视频
            context.set_step_progress("compositing", 0.8)
            final_video = self._composite_final_video(context, video_frames, audio_data, render_pipeline)
            if not final_video:
                return False
            
            # 编码输出
            context.set_step_progress("encoding", 0.9)
            output_path = context.input_data.get("output_path")
            if not self._encode_video(context, final_video, audio_data, output_path, render_pipeline):
                return False
            
            # 设置输出数据
            context.output_data["output_file"] = output_path
            context.output_data["duration"] = project_data.get("duration", 0)
            context.output_data["file_size"] = os.path.getsize(output_path)
            
            self.logger.info("视频渲染完成")
            return True
        
        except Exception as e:
            self.logger.error(f"视频渲染失败: {e}")
            context.add_error(str(e))
            return False
    
    def _load_project(self, context: TemplateContext) -> Optional[Dict[str, Any]]:
        """加载项目"""
        try:
            project_file = context.input_data.get("project_file")
            
            with open(project_file, 'r', encoding='utf-8') as f:
                project_data = json.load(f)
            
            # 验证项目数据格式
            if not self._validate_project_data(project_data):
                context.add_error("项目数据格式无效")
                return None
            
            context.add_custom_metric("project_clips", len(project_data.get("clips", [])))
            context.add_custom_metric("project_tracks", len(project_data.get("tracks", [])))
            
            return project_data
        
        except Exception as e:
            self.logger.error(f"项目加载失败: {e}")
            context.add_error(f"项目加载失败: {e}")
            return None
    
    def _prepare_render_pipeline(self, context: TemplateContext, 
                               project_data: Dict[str, Any]) -> Optional[Dict[str, Any]]:
        """准备渲染管线"""
        try:
            # 获取渲染配置
            render_config = context.input_data.get("render_config", self.render_config)
            
            # 创建渲染管线配置
            pipeline = {
                "video_encoder": render_config.get("video_codec", "h264"),
                "audio_encoder": render_config.get("audio_codec", "aac"),
                "video_bitrate": render_config.get("video_bitrate", "1000k"),
                "audio_bitrate": render_config.get("audio_bitrate", "128k"),
                "resolution": self._parse_resolution(render_config.get("resolution", "1920x1080")),
                "frame_rate": render_config.get("frame_rate", 30),
                "pixel_format": render_config.get("pixel_format", "yuv420p"),
                "preset": render_config.get("preset", "medium"),
                "crf": render_config.get("crf", 23)
            }
            
            # 验证渲染配置
            if not self._validate_render_config(pipeline):
                context.add_error("渲染配置无效")
                return None
            
            context.add_custom_metric("render_config", pipeline)
            return pipeline
        
        except Exception as e:
            self.logger.error(f"渲染管线准备失败: {e}")
            context.add_error(f"渲染管线准备失败: {e}")
            return None
    
    def _render_video_tracks(self, context: TemplateContext, 
                           project_data: Dict[str, Any], 
                           render_pipeline: Dict[str, Any]) -> Optional[List]:
        """渲染视频轨道"""
        try:
            video_tracks = project_data.get("video_tracks", [])
            resolution = render_pipeline["resolution"]
            frame_rate = render_pipeline["frame_rate"]
            
            frames = []
            total_frames = int(project_data.get("duration", 0) * frame_rate)
            
            for frame_index in range(total_frames):
                # 更新进度
                progress = frame_index / total_frames
                context.set_progress(progress * 0.4 + 0.2)  # 20% 到 60%
                
                # 渲染当前帧
                frame = self._render_frame(context, video_tracks, frame_index, 
                                         resolution, render_pipeline)
                if frame is None:
                    context.add_error(f"帧渲染失败: {frame_index}")
                    return None
                
                frames.append(frame)
            
            context.add_custom_metric("rendered_frames", len(frames))
            return frames
        
        except Exception as e:
            self.logger.error(f"视频轨道渲染失败: {e}")
            context.add_error(f"视频轨道渲染失败: {e}")
            return None
    
    def _render_audio_tracks(self, context: TemplateContext, 
                           project_data: Dict[str, Any], 
                           render_pipeline: Dict[str, Any]) -> Optional:
        """渲染音频轨道"""
        try:
            audio_tracks = project_data.get("audio_tracks", [])
            if not audio_tracks:
                return None
            
            # 音频渲染的具体实现
            # 这里应该调用音频处理库来混合音频轨道
            
            context.add_custom_metric("audio_tracks", len(audio_tracks))
            return audio_tracks  # 简化实现
        
        except Exception as e:
            self.logger.error(f"音频轨道渲染失败: {e}")
            context.add_error(f"音频轨道渲染失败: {e}")
            return None
    
    def _composite_final_video(self, context: TemplateContext, 
                              video_frames: List, audio_data: Optional,
                              render_pipeline: Dict[str, Any]) -> Optional:
        """合成最终视频"""
        try:
            # 视频合成的具体实现
            # 这里应该将渲染好的帧和音频数据合成为最终的视频
            
            context.add_custom_metric("final_video_frames", len(video_frames))
            return video_frames  # 简化实现
        
        except Exception as e:
            self.logger.error(f"最终视频合成失败: {e}")
            context.add_error(f"最终视频合成失败: {e}")
            return None
    
    def _encode_video(self, context: TemplateContext, final_video: Any, 
                     audio_data: Optional, output_path: str, 
                     render_pipeline: Dict[str, Any]) -> bool:
        """编码视频"""
        try:
            # 视频编码的具体实现
            # 这里应该使用FFmpeg或其他编码库将视频数据编码为指定格式
            
            # 模拟编码过程
            context.set_progress(0.95)
            
            # 创建输出文件(模拟)
            with open(output_path, 'wb') as f:
                f.write(b"mock_video_data")
            
            context.add_custom_metric("output_file_size", os.path.getsize(output_path))
            return True
        
        except Exception as e:
            self.logger.error(f"视频编码失败: {e}")
            context.add_error(f"视频编码失败: {e}")
            return False
    
    def _validate_project_data(self, project_data: Dict[str, Any]) -> bool:
        """验证项目数据"""
        required_fields = ["duration", "video_tracks", "audio_tracks"]
        for field in required_fields:
            if field not in project_data:
                return False
        return True
    
    def _parse_resolution(self, resolution_str: str) -> Dict[str, int]:
        """解析分辨率字符串"""
        try:
            width, height = resolution_str.split('x')
            return {"width": int(width), "height": int(height)}
        except:
            return {"width": 1920, "height": 1080}
    
    def _validate_render_config(self, config: Dict[str, Any]) -> bool:
        """验证渲染配置"""
        required_fields = ["video_encoder", "audio_encoder", "resolution", "frame_rate"]
        for field in required_fields:
            if field not in config:
                return False
        return True
    
    def _render_frame(self, context: TemplateContext, video_tracks: List, 
                     frame_index: int, resolution: Dict[str, int], 
                     render_pipeline: Dict[str, Any]) -> Optional[bytes]:
        """渲染单个帧"""
        try:
            # 帧渲染的具体实现
            # 这里应该将所有视频轨道合成为一帧
            
            # 模拟帧数据
            frame_size = resolution["width"] * resolution["height"] * 3  # RGB
            return b"mock_frame_data" * (frame_size // 16)
        
        except Exception as e:
            self.logger.error(f"帧渲染失败: {frame_index}: {e}")
            return None

14.3.2 音频处理模板

音频处理模板负责处理音频相关的操作,如混音、降噪、音量调整等:

class AudioProcessTemplate(BaseTemplate):
    """音频处理模板"""
    
    def __init__(self, template_id: str, name: str = "音频处理", description: str = ""):
        super().__init__(template_id, name, description)
        self.description = "音频混音、降噪、音量调整等处理"
        
        # 音频处理配置
        self.audio_config = {
            "sample_rate": 44100,
            "bit_depth": 16,
            "channels": 2,
            "format": "float32"
        }
        
        # 支持的音频格式
        self.supported_formats = ["wav", "mp3", "aac", "flac", "ogg"]
        
        # 依赖
        self.dependencies = ["audio_library", "fft_library"]
        
        # 必需资源
        self.required_resources = ["audio_memory", "processing_power"]
    
    def get_required_parameters(self) -> List[str]:
        """获取必需参数"""
        return ["input_audio", "output_path", "process_type"]
    
    def _validate_input(self, context: TemplateContext) -> bool:
        """验证输入"""
        if not super()._validate_input(context):
            return False
        
        # 验证输入音频文件
        input_audio = context.input_data.get("input_audio")
        if isinstance(input_audio, str):
            if not os.path.exists(input_audio):
                context.add_error(f"输入音频文件不存在: {input_audio}")
                return False
        elif isinstance(input_audio, list):
            for audio_file in input_audio:
                if not os.path.exists(audio_file):
                    context.add_error(f"输入音频文件不存在: {audio_file}")
                    return False
        else:
            context.add_error("输入音频格式无效")
            return False
        
        # 验证处理类型
        process_type = context.input_data.get("process_type")
        valid_process_types = ["mix", "noise_reduction", "volume_adjust", "fade", "filter"]
        if process_type not in valid_process_types:
            context.add_error(f"无效的处理类型: {process_type}")
            return False
        
        return True
    
    def _main_process(self, context: TemplateContext) -> bool:
        """主要处理逻辑"""
        try:
            process_type = context.input_data.get("process_type")
            
            if process_type == "mix":
                return self._process_mix(context)
            elif process_type == "noise_reduction":
                return self._process_noise_reduction(context)
            elif process_type == "volume_adjust":
                return self._process_volume_adjust(context)
            elif process_type == "fade":
                return self._process_fade(context)
            elif process_type == "filter":
                return self._process_filter(context)
            else:
                context.add_error(f"不支持的处理类型: {process_type}")
                return False
        
        except Exception as e:
            self.logger.error(f"音频处理失败: {e}")
            context.add_error(str(e))
            return False
    
    def _process_mix(self, context: TemplateContext) -> bool:
        """音频混音处理"""
        try:
            input_audio = context.input_data.get("input_audio")
            output_path = context.input_data.get("output_path")
            
            if not isinstance(input_audio, list) or len(input_audio) < 2:
                context.add_error("混音需要至少两个音频文件")
                return False
            
            # 加载音频文件
            context.set_step_progress("loading_audio", 0.1)
            audio_tracks = []
            for i, audio_file in enumerate(input_audio):
                audio_data = self._load_audio_file(audio_file)
                if audio_data is None:
                    context.add_error(f"音频文件加载失败: {audio_file}")
                    return False
                audio_tracks.append(audio_data)
            
            # 音频格式标准化
            context.set_step_progress("normalizing", 0.3)
            normalized_tracks = self._normalize_audio_tracks(audio_tracks)
            
            # 混音处理
            context.set_step_progress("mixing", 0.6)
            mixed_audio = self._mix_audio_tracks(normalized_tracks, context)
            if mixed_audio is None:
                return False
            
            # 保存输出
            context.set_step_progress("saving", 0.9)
            if not self._save_audio_file(mixed_audio, output_path):
                return False
            
            # 设置输出数据
            context.output_data["output_file"] = output_path
            context.output_data["duration"] = mixed_audio.get("duration", 0)
            context.output_data["channels"] = mixed_audio.get("channels", 2)
            
            return True
        
        except Exception as e:
            self.logger.error(f"音频混音失败: {e}")
            context.add_error(str(e))
            return False
    
    def _process_noise_reduction(self, context: TemplateContext) -> bool:
        """降噪处理"""
        try:
            input_audio = context.input_data.get("input_audio")
            output_path = context.input_data.get("output_path")
            
            if isinstance(input_audio, list):
                input_audio = input_audio[0]
            
            # 加载音频
            context.set_step_progress("loading", 0.1)
            audio_data = self._load_audio_file(input_audio)
            if audio_data is None:
                return False
            
            # 降噪参数
            noise_reduction_params = context.input_data.get("noise_reduction", {})
            reduction_amount = noise_reduction_params.get("amount", 0.5)
            
            # 应用降噪
            context.set_step_progress("processing", 0.5)
            cleaned_audio = self._apply_noise_reduction(audio_data, reduction_amount, context)
            if cleaned_audio is None:
                return False
            
            # 保存结果
            context.set_step_progress("saving", 0.9)
            if not self._save_audio_file(cleaned_audio, output_path):
                return False
            
            context.output_data["output_file"] = output_path
            return True
        
        except Exception as e:
            self.logger.error(f"降噪处理失败: {e}")
            context.add_error(str(e))
            return False
    
    def _process_volume_adjust(self, context: TemplateContext) -> bool:
        """音量调整处理"""
        try:
            input_audio = context.input_data.get("input_audio")
            output_path = context.input_data.get("output_path")
            volume_adjust = context.input_data.get("volume_adjust", {})
            
            if isinstance(input_audio, list):
                input_audio = input_audio[0]
            
            # 加载音频
            audio_data = self._load_audio_file(input_audio)
            if audio_data is None:
                return False
            
            # 应用音量调整
            adjusted_audio = self._apply_volume_adjust(audio_data, volume_adjust, context)
            if adjusted_audio is None:
                return False
            
            # 保存结果
            if not self._save_audio_file(adjusted_audio, output_path):
                return False
            
            context.output_data["output_file"] = output_path
            return True
        
        except Exception as e:
            self.logger.error(f"音量调整失败: {e}")
            context.add_error(str(e))
            return False
    
    def _process_fade(self, context: TemplateContext) -> bool:
        """淡入淡出处理"""
        try:
            input_audio = context.input_data.get("input_audio")
            output_path = context.input_data.get("output_path")
            fade_params = context.input_data.get("fade", {})
            
            if isinstance(input_audio, list):
                input_audio = input_audio[0]
            
            # 加载音频
            audio_data = self._load_audio_file(input_audio)
            if audio_data is None:
                return False
            
            # 应用淡入淡出
            faded_audio = self._apply_fade(audio_data, fade_params, context)
            if faded_audio is None:
                return False
            
            # 保存结果
            if not self._save_audio_file(faded_audio, output_path):
                return False
            
            context.output_data["output_file"] = output_path
            return True
        
        except Exception as e:
            self.logger.error(f"淡入淡出处理失败: {e}")
            context.add_error(str(e))
            return False
    
    def _process_filter(self, context: TemplateContext) -> bool:
        """音频滤波处理"""
        try:
            input_audio = context.input_data.get("input_audio")
            output_path = context.input_data.get("output_path")
            filter_params = context.input_data.get("filter", {})
            
            if isinstance(input_audio, list):
                input_audio = input_audio[0]
            
            # 加载音频
            audio_data = self._load_audio_file(input_audio)
            if audio_data is None:
                return False
            
            # 应用滤波
            filtered_audio = self._apply_filter(audio_data, filter_params, context)
            if filtered_audio is None:
                return False
            
            # 保存结果
            if not self._save_audio_file(filtered_audio, output_path):
                return False
            
            context.output_data["output_file"] = output_path
            return True
        
        except Exception as e:
            self.logger.error(f"音频滤波处理失败: {e}")
            context.add_error(str(e))
            return False
    
    def _load_audio_file(self, file_path: str) -> Optional[Dict[str, Any]]:
        """加载音频文件"""
        try:
            # 这里应该使用实际的音频处理库
            # 简化实现
            return {
                "file_path": file_path,
                "sample_rate": self.audio_config["sample_rate"],
                "channels": self.audio_config["channels"],
                "duration": 60.0,  # 模拟60秒音频
                "data": b"mock_audio_data"
            }
        except Exception as e:
            self.logger.error(f"音频文件加载失败: {file_path}, {e}")
            return None
    
    def _save_audio_file(self, audio_data: Dict[str, Any], output_path: str) -> bool:
        """保存音频文件"""
        try:
            # 这里应该使用实际的音频处理库
            # 简化实现
            with open(output_path, 'wb') as f:
                f.write(audio_data.get("data", b"mock_audio_data"))
            
            return True
        except Exception as e:
            self.logger.error(f"音频文件保存失败: {output_path}, {e}")
            return False
    
    def _normalize_audio_tracks(self, audio_tracks: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
        """标准化音频轨道"""
        # 这里应该实现音频格式标准化逻辑
        return audio_tracks
    
    def _mix_audio_tracks(self, audio_tracks: List[Dict[str, Any]], 
                         context: TemplateContext) -> Optional[Dict[str, Any]]:
        """混音处理"""
        try:
            # 这里应该实现实际的混音算法
            # 简化实现
            mixed_audio = {
                "sample_rate": self.audio_config["sample_rate"],
                "channels": self.audio_config["channels"],
                "duration": min(track.get("duration", 0) for track in audio_tracks),
                "data": b"mixed_audio_data"
            }
            
            return mixed_audio
        except Exception as e:
            self.logger.error(f"混音处理失败: {e}")
            context.add_error(f"混音处理失败: {e}")
            return None
    
    def _apply_noise_reduction(self, audio_data: Dict[str, Any], 
                              reduction_amount: float, 
                              context: TemplateContext) -> Optional[Dict[str, Any]]:
        """应用降噪"""
        try:
            # 这里应该实现实际的降噪算法
            # 简化实现
            cleaned_audio = audio_data.copy()
            cleaned_audio["data"] = b"cleaned_audio_data"
            
            return cleaned_audio
        except Exception as e:
            self.logger.error(f"降噪处理失败: {e}")
            context.add_error(f"降噪处理失败: {e}")
            return None
    
    def _apply_volume_adjust(self, audio_data: Dict[str, Any], 
                           volume_params: Dict[str, Any], 
                           context: TemplateContext) -> Optional[Dict[str, Any]]:
        """应用音量调整"""
        try:
            # 这里应该实现实际的音量调整算法
            # 简化实现
            adjusted_audio = audio_data.copy()
            adjusted_audio["data"] = b"volume_adjusted_audio_data"
            
            return adjusted_audio
        except Exception as e:
            self.logger.error(f"音量调整失败: {e}")
            context.add_error(f"音量调整失败: {e}")
            return None
    
    def _apply_fade(self, audio_data: Dict[str, Any], 
                   fade_params: Dict[str, Any], 
                   context: TemplateContext) -> Optional[Dict[str, Any]]:
        """应用淡入淡出"""
        try:
            # 这里应该实现实际的淡入淡出算法
            # 简化实现
            faded_audio = audio_data.copy()
            faded_audio["data"] = b"faded_audio_data"
            
            return faded_audio
        except Exception as e:
            self.logger.error(f"淡入淡出处理失败: {e}")
            context.add_error(f"淡入淡出处理失败: {e}")
            return None
    
    def _apply_filter(self, audio_data: Dict[str, Any], 
                     filter_params: Dict[str, Any], 
                     context: TemplateContext) -> Optional[Dict[str, Any]]:
        """应用滤波"""
        try:
            # 这里应该实现实际的滤波算法
            # 简化实现
            filtered_audio = audio_data.copy()
            filtered_audio["data"] = b"filtered_audio_data"
            
            return filtered_audio
        except Exception as e:
            self.logger.error(f"滤波处理失败: {e}")
            context.add_error(f"滤波处理失败: {e}")
            return None
    
    def _check_type(self, value: Any, expected_type: type) -> bool:
        """检查类型"""
        try:
            if expected_type == int:
                int(value)
            elif expected_type == float:
                float(value)
            elif expected_type == str:
                str(value)
            elif expected_type == bool:
                bool(value)
            else:
                return isinstance(value, expected_type)
            return True
        except (ValueError, TypeError):
            return False
    
    def _apply_validation_rule(self, value: Any, rule: str) -> bool:
        """应用验证规则"""
        try:
            if rule.startswith("range:"):
                # 范围验证: range:min-max
                parts = rule[6:].split("-")
                if len(parts) == 2:
                    min_val, max_val = float(parts[0]), float(parts[1])
                    return min_val <= float(value) <= max_val
            elif rule.startswith("min:"):
                # 最小值验证: min:value
                min_val = float(rule[4:])
                return float(value) >= min_val
            elif rule.startswith("max:"):
                # 最大值验证: max:value
                max_val = float(rule[4:])
                return float(value) <= max_val
            elif rule == "positive":
                # 正数验证
                return float(value) > 0
            elif rule == "non_empty":
                # 非空验证
                return bool(value)
            elif rule.startswith("regex:"):
                # 正则表达式验证
                import re
                pattern = rule[6:]
                return bool(re.match(pattern, str(value)))
            
            return True
        except (ValueError, TypeError):
            return False

14.4 模板扩展机制

14.4.1 插件系统

插件系统允许动态加载和注册新的模板类型,提供了强大的扩展能力:

import importlib
import inspect
import os
from pathlib import Path
from typing import Dict, List, Type, Any, Optional

class PluginManager:
    """插件管理器"""
    
    def __init__(self, plugin_dirs: List[str] = None):
        self.plugin_dirs = plugin_dirs or ["plugins", "templates/plugins"]
        self.loaded_plugins: Dict[str, Dict[str, Any]] = {}
        self.plugin_types: Dict[str, Type[BaseTemplate]] = {}
        self.plugin_instances: Dict[str, BaseTemplate] = {}
        
        # 日志
        self.logger = logging.getLogger(__name__)
    
    def discover_plugins(self) -> List[str]:
        """发现插件"""
        discovered_plugins = []
        
        for plugin_dir in self.plugin_dirs:
            if not os.path.exists(plugin_dir):
                continue
            
            # 查找Python文件
            for file_path in Path(plugin_dir).glob("*.py"):
                if file_path.name.startswith("__"):
                    continue
                
                plugin_name = file_path.stem
                discovered_plugins.append(str(file_path))
                
                self.logger.info(f"发现插件: {plugin_name} at {file_path}")
        
        return discovered_plugins
    
    def load_plugin(self, plugin_path: str) -> bool:
        """加载插件"""
        try:
            plugin_name = Path(plugin_path).stem
            
            # 动态导入模块
            spec = importlib.util.spec_from_file_location(plugin_name, plugin_path)
            if not spec or not spec.loader:
                self.logger.error(f"无法加载插件规范: {plugin_path}")
                return False
            
            module = importlib.util.module_from_spec(spec)
            spec.loader.exec_module(module)
            
            # 查找模板类
            template_classes = []
            for name, obj in inspect.getmembers(module, inspect.isclass):
                if issubclass(obj, BaseTemplate) and obj != BaseTemplate:
                    template_classes.append(obj)
            
            if not template_classes:
                self.logger.warning(f"插件中没有找到模板类: {plugin_path}")
                return False
            
            # 注册模板类型
            for template_class in template_classes:
                class_name = template_class.__name__
                self.plugin_types[class_name] = template_class
                
                self.loaded_plugins[plugin_name] = {
                    "path": plugin_path,
                    "module": module,
                    "template_classes": template_classes,
                    "loaded_at": datetime.now()
                }
                
                self.logger.info(f"插件加载成功: {plugin_name} - {class_name}")
            
            return True
        
        except Exception as e:
            self.logger.error(f"插件加载失败: {plugin_path}: {e}")
            return False
    
    def load_all_plugins(self) -> int:
        """加载所有插件"""
        discovered_plugins = self.discover_plugins()
        loaded_count = 0
        
        for plugin_path in discovered_plugins:
            if self.load_plugin(plugin_path):
                loaded_count += 1
        
        self.logger.info(f"插件加载完成: {loaded_count}/{len(discovered_plugins)}")
        return loaded_count
    
    def create_plugin_instance(self, plugin_type: str, instance_id: str, 
                              name: str = "", description: str = "") -> Optional[BaseTemplate]:
        """创建插件实例"""
        try:
            if plugin_type not in self.plugin_types:
                self.logger.error(f"插件类型不存在: {plugin_type}")
                return None
            
            template_class = self.plugin_types[plugin_type]
            instance = template_class(instance_id, name, description)
            
            self.plugin_instances[instance_id] = instance
            self.logger.info(f"插件实例创建成功: {plugin_type} - {instance_id}")
            
            return instance
        
        except Exception as e:
            self.logger.error(f"插件实例创建失败: {plugin_type}: {e}")
            return None
    
    def get_plugin_instance(self, instance_id: str) -> Optional[BaseTemplate]:
        """获取插件实例"""
        return self.plugin_instances.get(instance_id)
    
    def get_loaded_plugins(self) -> Dict[str, Dict[str, Any]]:
        """获取已加载的插件"""
        return self.loaded_plugins.copy()
    
    def get_plugin_types(self) -> Dict[str, Type[BaseTemplate]]:
        """获取插件类型"""
        return self.plugin_types.copy()
    
    def unload_plugin(self, plugin_name: str) -> bool:
        """卸载插件"""
        try:
            if plugin_name not in self.loaded_plugins:
                return False
            
            plugin_info = self.loaded_plugins[plugin_name]
            template_classes = plugin_info["template_classes"]
            
            # 移除插件类型
            for template_class in template_classes:
                class_name = template_class.__name__
                if class_name in self.plugin_types:
                    del self.plugin_types[class_name]
            
            # 移除相关实例
            instances_to_remove = []
            for instance_id, instance in self.plugin_instances.items():
                if instance.__class__ in template_classes:
                    instances_to_remove.append(instance_id)
            
            for instance_id in instances_to_remove:
                del self.plugin_instances[instance_id]
            
            # 移除插件信息
            del self.loaded_plugins[plugin_name]
            
            self.logger.info(f"插件卸载成功: {plugin_name}")
            return True
        
        except Exception as e:
            self.logger.error(f"插件卸载失败: {plugin_name}: {e}")
            return False
    
    def reload_plugin(self, plugin_name: str) -> bool:
        """重新加载插件"""
        try:
            if plugin_name not in self.loaded_plugins:
                return False
            
            plugin_info = self.loaded_plugins[plugin_name]
            plugin_path = plugin_info["path"]
            
            # 先卸载
            self.unload_plugin(plugin_name)
            
            # 重新加载
            return self.load_plugin(plugin_path)
        
        except Exception as e:
            self.logger.error(f"插件重新加载失败: {plugin_name}: {e}")
            return False
    
    def get_plugin_info(self, plugin_name: str) -> Optional[Dict[str, Any]]:
        """获取插件信息"""
        return self.loaded_plugins.get(plugin_name)
    
    def get_all_plugins_info(self) -> Dict[str, Dict[str, Any]]:
        """获取所有插件信息"""
class TemplateConfigManager:
    """模板配置管理器"""
    
    def __init__(self, config_dir: str = "configs/templates"):
        self.config_dir = config_dir
        self.configs: Dict[str, Dict[str, Any]] = {}
        self.template_schemas: Dict[str, Dict[str, Any]] = {}
        self.logger = logging.getLogger(__name__)
        
        # 确保配置目录存在
        os.makedirs(config_dir, exist_ok=True)
        
        # 加载现有配置
        self.load_all_configs()
    
    def load_config(self, template_id: str) -> Optional[Dict[str, Any]]:
        """加载模板配置"""
        try:
            config_path = os.path.join(self.config_dir, f"{template_id}.json")
            
            if not os.path.exists(config_path):
                self.logger.warning(f"配置文件不存在: {config_path}")
                return None
            
            import json
            with open(config_path, 'r', encoding='utf-8') as f:
                config = json.load(f)
            
            self.configs[template_id] = config
            self.logger.info(f"配置加载成功: {template_id}")
            return config
        
        except Exception as e:
            self.logger.error(f"配置加载失败: {template_id}: {e}")
            return None
    
    def save_config(self, template_id: str, config: Dict[str, Any]) -> bool:
        """保存模板配置"""
        try:
            import json
            config_path = os.path.join(self.config_dir, f"{template_id}.json")
            
            with open(config_path, 'w', encoding='utf-8') as f:
                json.dump(config, f, ensure_ascii=False, indent=2)
            
            self.configs[template_id] = config
            self.logger.info(f"配置保存成功: {template_id}")
            return True
        
        except Exception as e:
            self.logger.error(f"配置保存失败: {template_id}: {e}")
            return False
    
    def load_all_configs(self) -> int:
        """加载所有配置"""
        loaded_count = 0
        
        try:
            for config_file in Path(self.config_dir).glob("*.json"):
                template_id = config_file.stem
                if self.load_config(template_id):
                    loaded_count += 1
            
            self.logger.info(f"配置加载完成: {loaded_count} 个")
            return loaded_count
        
        except Exception as e:
            self.logger.error(f"配置批量加载失败: {e}")
            return 0
    
    def get_config(self, template_id: str, default: Optional[Dict[str, Any]] = None) -> Optional[Dict[str, Any]]:
        """获取模板配置"""
        return self.configs.get(template_id, default)
    
    def update_config(self, template_id: str, updates: Dict[str, Any]) -> bool:
        """更新模板配置"""
        try:
            config = self.get_config(template_id, {})
            config.update(updates)
            return self.save_config(template_id, config)
        except Exception as e:
            self.logger.error(f"配置更新失败: {template_id}: {e}")
            return False
    
    def delete_config(self, template_id: str) -> bool:
        """删除模板配置"""
        try:
            config_path = os.path.join(self.config_dir, f"{template_id}.json")
            
            if os.path.exists(config_path):
                os.remove(config_path)
            
            if template_id in self.configs:
                del self.configs[template_id]
            
            self.logger.info(f"配置删除成功: {template_id}")
            return True
        
        except Exception as e:
            self.logger.error(f"配置删除失败: {template_id}: {e}")
            return False
    
    def create_default_config(self, template_id: str, template_type: str) -> Dict[str, Any]:
        """创建默认配置"""
        default_config = {
            "template_id": template_id,
            "template_type": template_type,
            "version": "1.0.0",
            "enabled": True,
            "priority": "normal",
            "timeout_seconds": 300,
            "retry_count": 3,
            "memory_limit_mb": 1024,
            "cpu_limit_percent": 80,
            "parameters": {},
            "metadata": {},
            "created_at": datetime.now().isoformat(),
            "updated_at": datetime.now().isoformat()
        }
        
        return default_config
    
    def validate_config(self, template_id: str, config: Dict[str, Any]) -> List[str]:
        """验证配置"""
        errors = []
        
        # 检查必需字段
        required_fields = ["template_id", "template_type", "version"]
        for field in required_fields:
            if field not in config:
                errors.append(f"缺少必需字段: {field}")
        
        # 检查字段类型
        if "timeout_seconds" in config and not isinstance(config["timeout_seconds"], int):
            errors.append("timeout_seconds 必须是整数")
        
        if "memory_limit_mb" in config and not isinstance(config["memory_limit_mb"], int):
            errors.append("memory_limit_mb 必须是整数")
        
        if "cpu_limit_percent" in config and not isinstance(config["cpu_limit_percent"], (int, float)):
            errors.append("cpu_limit_percent 必须是数字")
        
        if "enabled" in config and not isinstance(config["enabled"], bool):
            errors.append("enabled 必须是布尔值")
        
        return errors


## 14.5 模板模式总结

### 14.5.1 核心组件回顾

模板模式在剪映小助手中的实现包含以下核心组件:

**1. 抽象模板基类 (BaseTemplate)**
- 定义了模板执行的完整流程和算法骨架
- 提供了统一的模板接口和状态管理
- 实现了通用的预处理、验证、资源管理等逻辑
- 支持回调函数和事件通知机制

**2. 模板上下文 (TemplateContext)**
- 封装了模板执行过程中的所有状态和数据
- 提供了进度跟踪、性能监控、错误处理等功能
- 支持自定义指标和用户数据存储
- 记录了完整的执行历史和性能数据

**3. 具体模板实现**
- `VideoRenderTemplate`: 视频渲染模板,处理视频导出和格式转换
- `AudioProcessingTemplate`: 音频处理模板,实现音频效果和滤波处理
- 支持自定义模板类型扩展

**4. 插件系统 (PluginManager)**
- 支持动态加载和注册新的模板类型
- 提供了插件发现、加载、卸载、重载等完整生命周期管理
- 支持插件实例的创建和管理
- 具备完善的错误处理和日志记录

**5. 配置管理 (TemplateConfigManager)**
- 提供了模板配置的持久化管理
- 支持配置的创建、加载、更新、验证等操作
- 具备完善的错误处理和类型检查
- 支持默认配置的自动生成

### 14.5.2 技术特点

**代码复用性**
- 将公共代码提取到父类中,避免了重复实现
- 通过模板方法定义标准流程,确保一致性
- 支持通过继承快速扩展新的功能

**扩展性强**
- 新的模板类型可以通过继承抽象基类来实现
- 插件系统支持运行时动态扩展
- 配置系统允许灵活调整模板行为

**维护性好**
- 算法骨架集中在父类中,便于维护和理解
- 具体实现分离到子类,职责清晰
- 完善的日志和错误处理机制

**性能优化**
- 支持资源管理和内存限制
- 提供了超时控制和重试机制
- 具备性能监控和指标收集

**灵活性高**
- 支持运行时选择不同的具体实现
- 提供了丰富的配置选项
- 支持自定义回调和事件处理

### 14.5.3 应用场景

模板模式在剪映小助手中广泛应用于:

**视频处理流程**
- 视频导入导出的标准化流程
- 视频效果和滤镜的统一处理
- 视频渲染和编码的标准化

**音频处理流程**
- 音频效果的统一处理框架
- 音频格式转换的标准流程
- 音频滤波和增强的统一实现

**用户界面组件**
- UI组件的标准化行为定义
- 界面元素的统一处理逻辑
- 用户交互的标准化响应

**文件处理流程**
- 文件导入的标准化验证
- 文件导出的统一处理
- 格式转换的标准化流程

**插件扩展机制**
- 第三方插件的标准化接口
- 插件生命周期的统一管理
- 动态扩展的标准化框架

通过模板模式的应用,剪映小助手实现了高度可复用、可扩展、可维护的架构设计,为后续的功能扩展和系统演化提供了坚实的基础。

---

## 相关资源

- **GitHub代码仓库**: https://github.com/Hommy-master/capcut-mate
- **Gitee代码仓库**: https://gitee.com/taohongmin-gitee/capcut-mate
- **API文档地址**: https://docs.jcaigc.cn
            
            return self.load_plugin(plugin_path)
        
        except Exception as e:
            self.logger.error(f"插件重新加载失败: {plugin_name}: {e}")
            return False

14.4.2 模板配置系统

模板配置系统提供了灵活的配置管理机制,支持模板的参数化配置:

class TemplateConfig:
    """模板配置"""
    
    def __init__(self, config_id: str, name: str = "", description: str = ""):
        self.config_id = config_id
        self.name = name
        self.description = description
        self.version = "1.0.0"
        self.created_at = datetime.now()
        self.updated_at = datetime.now()
        
        # 配置参数
        self.parameters: Dict[str, Any] = {}
        self.parameter_definitions: Dict[str, Dict[str, Any]] = {}
        self.validation_rules: Dict[str, List[str]] = {}
        self.default_values: Dict[str, Any] = {}
        self.allowed_values: Dict[str, List[Any]] = {}
        self.parameter_groups: Dict[str, List[str]] = {}
        
        # 元数据
        self.metadata: Dict[str, Any] = {}
        self.tags: List[str] = []
        
        # 依赖和约束
        self.dependencies: List[str] = []
        self.constraints: List[str] = []
    
    def add_parameter(self, name: str, param_type: str, default_value: Any = None,
                     description: str = "", required: bool = True,
                     validation_rules: List[str] = None, allowed_values: List[Any] = None) -> None:
        """添加参数"""
        self.parameter_definitions[name] = {
            "type": param_type,
            "description": description,
            "required": required,
            "default_value": default_value
        }
        
        if default_value is not None:
            self.default_values[name] = default_value
        
        if validation_rules:
            self.validation_rules[name] = validation_rules
        
        if allowed_values:
            self.allowed_values[name] = allowed_values
        
        self.updated_at = datetime.now()
    
    def set_parameter(self, name: str, value: Any) -> bool:
        """设置参数值"""
        if name not in self.parameter_definitions:
            return False
        
        # 验证参数值
        if not self._validate_parameter(name, value):
            return False
        
        self.parameters[name] = value
        self.updated_at = datetime.now()
        return True
    
    def get_parameter(self, name: str, default: Any = None) -> Any:
        """获取参数值"""
        return self.parameters.get(name, self.default_values.get(name, default))
    
    def _validate_parameter(self, name: str, value: Any) -> bool:
        """验证参数"""
        if name not in self.parameter_definitions:
            return False
        
        param_def = self.parameter_definitions[name]
        param_type = param_def["type"]
        
        # 类型检查
        if not self._check_type(value, param_type):
            return False
        
        # 允许值检查
        if name in self.allowed_values:
            if value not in self.allowed_values[name]:
                return False
        
        # 验证规则检查
        if name in self.validation_rules:
            for rule in self.validation_rules[name]:
                if not self._apply_validation_rule(name, value, rule):
                    return False
        
        return True
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值