第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
521

被折叠的 条评论
为什么被折叠?



