""" 任务数据模型 定义转换任务的数据结构 """ from enum import Enum from dataclasses import dataclass, field from typing import Optional, Dict, List, Any from datetime import datetime import uuid class TaskStatus(Enum): """任务状态枚举""" PENDING = "pending" # 任务已创建,等待处理 QUEUED = "queued" # 任务在队列中等待 PROCESSING = "processing" # 正在处理中 CONVERTING = "converting" # 正在转换音频 UPLOADING = "uploading" # 正在上传结果 COMPLETED = "completed" # 转换完成 FAILED = "failed" # 转换失败 CANCELLED = "cancelled" # 任务已取消 @dataclass class VoiceSettings: """语音设置""" language: str = "zh-CN" voice_name: str = "xiaoxiao" speed: float = 1.0 pitch: int = 0 volume: float = 0.8 @dataclass class ProcessingDetails: """处理详情""" total_segments: int = 0 completed_segments: int = 0 current_segment: str = "" audio_duration: str = "0分0秒" error_message: Optional[str] = None @dataclass class AudioFile: """音频文件信息""" title: str url: str duration: str file_size: int = 0 chapter_id: str = "" @dataclass class ConversionTask: """转换任务模型""" task_id: str = field(default_factory=lambda: f"audio_{uuid.uuid4()}") status: TaskStatus = TaskStatus.PENDING # 输入信息 file_url: str = "" original_filename: str = "" user_id: Optional[str] = None # 从文件URL解析的用户ID original_task_id: Optional[str] = None # 从文件URL解析的原始任务ID r2_file_path: Optional[str] = None # R2存储路径(新增) # 转换配置 voice_settings: VoiceSettings = field(default_factory=VoiceSettings) output_formats: List[str] = field(default_factory=lambda: ["mp3"]) audio_quality: str = "high" split_by_chapter: bool = True max_segment_length: int = 300 # 进度信息 progress: int = 0 current_stage: str = "" queue_position: int = 0 # 时间信息 created_at: datetime = field(default_factory=datetime.now) started_at: Optional[datetime] = None completed_at: Optional[datetime] = None estimated_completion: Optional[datetime] = None # 处理详情 processing_details: ProcessingDetails = field(default_factory=ProcessingDetails) # 结果信息 audio_files: Dict[str, Any] = field(default_factory=dict) metadata: Dict[str, Any] = field(default_factory=dict) # 错误信息 error_message: Optional[str] = None retry_count: int = 0 max_retries: int = 3 def to_dict(self) -> Dict[str, Any]: """转换为字典格式""" return { "task_id": self.task_id, "status": self.status.value, "progress": self.progress, "current_stage": self.current_stage, "created_at": self.created_at.isoformat(), "started_at": self.started_at.isoformat() if self.started_at else None, "completed_at": ( self.completed_at.isoformat() if self.completed_at else None ), "estimated_completion": ( self.estimated_completion.isoformat() if self.estimated_completion else None ), "queue_position": self.queue_position, "processing_details": { "total_segments": self.processing_details.total_segments, "completed_segments": self.processing_details.completed_segments, "current_segment": self.processing_details.current_segment, "audio_duration": self.processing_details.audio_duration, }, "original_filename": self.original_filename, "user_id": self.user_id, "original_task_id": self.original_task_id, "r2_file_path": self.r2_file_path, "audio_files": self.audio_files, "metadata": self.metadata, "error_message": self.error_message, } def update_progress(self, progress: int, stage: str, segment: str = ""): """更新任务进度""" self.progress = progress self.current_stage = stage if segment: self.processing_details.current_segment = segment def mark_failed(self, error_message: str): """标记任务失败""" self.status = TaskStatus.FAILED self.error_message = error_message self.completed_at = datetime.now() def mark_completed(self): """标记任务完成""" self.status = TaskStatus.COMPLETED self.progress = 100 self.current_stage = "转换完成" self.completed_at = datetime.now() def can_retry(self) -> bool: """检查是否可以重试""" return self.retry_count < self.max_retries and self.status == TaskStatus.FAILED def increment_retry(self): """增加重试次数""" self.retry_count += 1 self.status = TaskStatus.PENDING self.error_message = None