【剪映小助手源码精讲】第34章:视频任务管理

第34章:视频任务管理

34.1 概述

视频任务管理系统是剪映小助手的核心组件,负责管理视频生成任务的提交、执行、状态跟踪和结果获取。该系统采用异步任务队列架构,支持任务的并发处理、状态监控和错误处理,确保视频生成过程的可靠性和高效性。

34.2 核心组件设计

34.2.1 任务状态枚举

系统定义了完整的任务状态生命周期:

python 复制代码
class TaskStatus(Enum):
    PENDING = "pending"        # 等待处理
    PROCESSING = "processing"    # 正在处理
    COMPLETED = "completed"      # 处理完成
    FAILED = "failed"            # 处理失败

状态转换流程:

  • PENDING → PROCESSING:任务开始执行
  • PROCESSING → COMPLETED:任务成功完成
  • PROCESSING → FAILED:任务执行失败

34.2.2 视频生成任务数据类

VideoGenTask类封装了视频生成任务的所有信息:

python 复制代码
@dataclass
class VideoGenTask:
    task_id: str                    # 任务唯一标识
    draft_url: str                  # 草稿文件URL
    status: TaskStatus             # 任务状态
    result: Optional[dict] = None   # 任务结果
    error: Optional[str] = None     # 错误信息
    created_at: datetime = field(default_factory=datetime.now)
    updated_at: datetime = field(default_factory=datetime.now)

34.2.3 任务管理器单例类

VideoGenTaskManager采用单例模式,确保系统中只有一个任务管理器实例:

python 复制代码
class VideoGenTaskManager:
    _instance = None
    _lock = threading.Lock()
    
    def __new__(cls):
        if cls._instance is None:
            with cls._lock:
                if cls._instance is None:
                    cls._instance = super().__new__(cls)
        return cls._instance

34.3 任务生命周期管理

34.3.1 任务提交

任务提交接口负责接收新的视频生成请求:

python 复制代码
def submit_task(self, draft_url: str) -> str:
    """提交视频生成任务"""
    task_id = str(uuid.uuid4())
    task = VideoGenTask(
        task_id=task_id,
        draft_url=draft_url,
        status=TaskStatus.PENDING
    )
    
    with self._lock:
        self._tasks[task_id] = task
        self._task_queue.put(task_id)
    
    logger.info(f"提交视频生成任务: {task_id}, 草稿URL: {draft_url}")
    return task_id

34.3.2 任务状态查询

提供任务状态查询接口:

python 复制代码
def get_task_status(self, task_id: str) -> Optional[dict]:
    """获取任务状态"""
    with self._lock:
        task = self._tasks.get(task_id)
        if not task:
            return None
        
        return {
            "task_id": task.task_id,
            "status": task.status.value,
            "result": task.result,
            "error": task.error,
            "created_at": task.created_at.isoformat(),
            "updated_at": task.updated_at.isoformat()
        }

34.3.3 工作线程循环

工作线程负责处理任务队列中的任务:

python 复制代码
def _worker_loop(self):
    """工作线程主循环"""
    logger.info("视频生成工作线程启动")
    
    while self._running:
        try:
            # 从队列获取任务ID,超时1秒
            task_id = self._task_queue.get(timeout=1)
            self._process_task(task_id)
            self._task_queue.task_done()
        except queue.Empty:
            continue
        except Exception as e:
            logger.error(f"工作线程异常: {e}", exc_info=True)

34.4 视频生成核心逻辑

34.4.1 任务处理流程

任务处理是系统的核心功能:

python 复制代码
def _process_task(self, task_id: str):
    """处理单个任务"""
    with self._lock:
        task = self._tasks.get(task_id)
        if not task or task.status != TaskStatus.PENDING:
            return
        
        # 更新状态为处理中
        task.status = TaskStatus.PROCESSING
        task.updated_at = datetime.now()
    
    logger.info(f"开始处理任务: {task_id}")
    
    try:
        # 执行视频生成
        result = self._generate_video(task.draft_url)
        
        # 更新任务状态
        with self._lock:
            task.status = TaskStatus.COMPLETED
            task.result = result
            task.updated_at = datetime.now()
        
        logger.info(f"任务处理完成: {task_id}")
        
    except Exception as e:
        logger.error(f"任务处理失败: {task_id}, 错误: {e}", exc_info=True)
        
        # 更新任务状态为失败
        with self._lock:
            task.status = TaskStatus.FAILED
            task.error = str(e)
            task.updated_at = datetime.now()

34.4.2 视频生成实现

视频生成逻辑调用剪映的导出功能:

python 复制代码
def _generate_video(self, draft_url: str) -> dict:
    """生成视频"""
    logger.info(f"开始生成视频,草稿URL: {draft_url}")
    
    # 提取草稿ID
    draft_id = self._extract_draft_id(draft_url)
    if not draft_id:
        raise ValueError(f"无法从URL提取草稿ID: {draft_url}")
    
    logger.info(f"提取到草稿ID: {draft_id}")
    
    # 调用剪映导出功能
    # 这里模拟实际的视频生成过程
    export_result = self._export_video(draft_id)
    
    if not export_result.get("success"):
        raise RuntimeError(f"视频导出失败: {export_result.get('error', '未知错误')}")
    
    return {
        "video_url": export_result.get("video_url"),
        "duration": export_result.get("duration"),
        "file_size": export_result.get("file_size"),
        "draft_id": draft_id
    }

34.4.3 草稿ID提取

从URL中提取草稿ID:

python 复制代码
def _extract_draft_id(self, draft_url: str) -> Optional[str]:
    """从草稿URL提取草稿ID"""
    try:
        # 解析URL
        parsed = urlparse(draft_url)
        
        # 尝试从查询参数获取
        query_params = parse_qs(parsed.query)
        if 'draft_id' in query_params:
            return query_params['draft_id'][0]
        
        # 尝试从路径获取
        path_parts = parsed.path.strip('/').split('/')
        if len(path_parts) >= 2 and path_parts[-2] == 'draft':
            return path_parts[-1]
        
        # 尝试从文件名获取
        if parsed.path.endswith('.json'):
            filename = os.path.basename(parsed.path)
            return filename[:-5]  # 移除.json后缀
        
        return None
        
    except Exception as e:
        logger.error(f"提取草稿ID失败: {e}")
        return None

34.5 错误处理与重试机制

34.5.1 异常分类处理

python 复制代码
def _process_task_with_retry(self, task_id: str, max_retries: int = 3):
    """带重试的任务处理"""
    for attempt in range(max_retries):
        try:
            self._process_task(task_id)
            return  # 成功则返回
        except NetworkError as e:
            logger.warning(f"网络错误,尝试重试 {attempt + 1}/{max_retries}: {e}")
            if attempt < max_retries - 1:
                time.sleep(2 ** attempt)  # 指数退避
            else:
                raise
        except Exception as e:
            logger.error(f"任务处理失败,不再重试: {e}")
            raise

34.5.2 任务超时处理

python 复制代码
def _process_task_with_timeout(self, task_id: str, timeout: int = 300):
    """带超时的任务处理"""
    def timeout_handler(signum, frame):
        raise TimeoutError("任务处理超时")
    
    # 设置超时信号
    signal.signal(signal.SIGALRM, timeout_handler)
    signal.alarm(timeout)
    
    try:
        self._process_task(task_id)
    finally:
        signal.alarm(0)  # 取消超时

34.6 并发控制与资源管理

34.6.1 并发任务限制

python 复制代码
class VideoGenTaskManager:
    def __init__(self, max_concurrent_tasks: int = 5):
        self.max_concurrent_tasks = max_concurrent_tasks
        self.active_tasks = 0
        self.task_semaphore = threading.Semaphore(max_concurrent_tasks)
    
    def _process_task(self, task_id: str):
        """处理任务(带并发控制)"""
        with self.task_semaphore:
            # 实际的业务处理逻辑
            self._do_process_task(task_id)

34.6.2 资源清理

python 复制代码
def cleanup_completed_tasks(self, max_age_hours: int = 24):
    """清理已完成的任务"""
    current_time = datetime.now()
    cutoff_time = current_time - timedelta(hours=max_age_hours)
    
    with self._lock:
        completed_tasks = [
            task_id for task_id, task in self._tasks.items()
            if task.status in [TaskStatus.COMPLETED, TaskStatus.FAILED]
            and task.updated_at < cutoff_time
        ]
        
        for task_id in completed_tasks:
            del self._tasks[task_id]
            logger.info(f"清理完成任务: {task_id}")

34.7 监控与统计

34.7.1 任务统计

python 复制代码
def get_task_statistics(self) -> dict:
    """获取任务统计信息"""
    with self._lock:
        total_tasks = len(self._tasks)
        pending_tasks = sum(1 for task in self._tasks.values() 
                          if task.status == TaskStatus.PENDING)
        processing_tasks = sum(1 for task in self._tasks.values() 
                             if task.status == TaskStatus.PROCESSING)
        completed_tasks = sum(1 for task in self._tasks.values() 
                            if task.status == TaskStatus.COMPLETED)
        failed_tasks = sum(1 for task in self._tasks.values() 
                         if task.status == TaskStatus.FAILED)
    
    return {
        "total_tasks": total_tasks,
        "pending_tasks": pending_tasks,
        "processing_tasks": processing_tasks,
        "completed_tasks": completed_tasks,
        "failed_tasks": failed_tasks,
        "queue_size": self._task_queue.qsize()
    }

34.7.2 性能指标

python 复制代码
def get_performance_metrics(self) -> dict:
    """获取性能指标"""
    stats = self.get_task_statistics()
    
    # 计算成功率
    total_completed = stats["completed_tasks"] + stats["failed_tasks"]
    success_rate = (stats["completed_tasks"] / total_completed * 100 
                   if total_completed > 0 else 0)
    
    # 计算平均处理时间
    completed_tasks = [
        task for task in self._tasks.values()
        if task.status == TaskStatus.COMPLETED
    ]
    
    avg_processing_time = 0
    if completed_tasks:
        processing_times = [
            (task.updated_at - task.created_at).total_seconds()
            for task in completed_tasks
        ]
        avg_processing_time = sum(processing_times) / len(processing_times)
    
    return {
        "success_rate": round(success_rate, 2),
        "average_processing_time_seconds": round(avg_processing_time, 2),
        "active_worker_threads": threading.active_count() - 1  # 减去主线程
    }

34.8 扩展性设计

34.8.1 插件化架构

支持自定义任务处理器:

python 复制代码
class TaskProcessor(ABC):
    """任务处理器抽象类"""
    
    @abstractmethod
    def can_process(self, task: VideoGenTask) -> bool:
        """判断是否可处理该任务"""
        pass
    
    @abstractmethod
    def process(self, task: VideoGenTask) -> dict:
        """处理任务"""
        pass

class DefaultVideoProcessor(TaskProcessor):
    """默认视频处理器"""
    
    def can_process(self, task: VideoGenTask) -> bool:
        return task.draft_url.endswith('.json')
    
    def process(self, task: VideoGenTask) -> dict:
        return self._generate_video(task.draft_url)

34.8.2 分布式任务处理

支持多节点分布式处理:

python 复制代码
class DistributedTaskManager:
    """分布式任务管理器"""
    
    def __init__(self, redis_client, node_id: str):
        self.redis = redis_client
        self.node_id = node_id
        self.task_queue_key = "video_gen_tasks"
    
    def submit_task(self, draft_url: str) -> str:
        """提交任务到分布式队列"""
        task_id = str(uuid.uuid4())
        task_data = {
            "task_id": task_id,
            "draft_url": draft_url,
            "node_id": None,  # 未分配节点
            "status": TaskStatus.PENDING.value
        }
        
        # 将任务添加到Redis队列
        self.redis.lpush(self.task_queue_key, json.dumps(task_data))
        return task_id

附录

代码仓库地址:

  • GitHub: https://github.com/Hommy-master/capcut-mate
  • Gitee: https://gitee.com/taohongmin-gitee/capcut-mate

接口文档地址:

  • API文档地址: https://docs.jcaigc.cn
相关推荐
日日行不惧千万里2 小时前
Java中Lambda Stream详解
java·开发语言·python
2401_841495642 小时前
【LeetCode刷题】零钱兑换
数据结构·python·算法·leetcode·动态规划·数组·时间复杂度
山海青风2 小时前
人工智能基础与应用 - 数据处理、建模与预测流程 2 : 数据与问题类型
人工智能·python
moshuying2 小时前
RAG技术演进:从外部知识库到智能体核心记忆系统
python
IT·小灰灰3 小时前
探索即梦生图AI与AI Ping平台的创新融合:技术实践与代码实现
人工智能·python
deephub3 小时前
CALM自编码器:用连续向量替代离散token,生成效率提升4倍
人工智能·python·大语言模型
94621931zyn65 小时前
关于应用 - Cordova 与 OpenHarmony 混合开发实战
笔记·python
知远同学10 小时前
Anaconda的安装使用(为python管理虚拟环境)
开发语言·python
Blossom.11810 小时前
AI编译器实战:从零手写算子融合与自动调度系统
人工智能·python·深度学习·机器学习·flask·transformer·tornado