mixvideo-v2/apps/desktop/src-tauri/src/data/models/template.rs

366 lines
10 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use serde::{Deserialize, Serialize};
use chrono::{DateTime, Utc};
/// 模板实体模型
/// 遵循 Tauri 开发规范的数据模型设计原则
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Template {
pub id: String,
pub name: String,
pub description: Option<String>,
pub canvas_config: CanvasConfig,
pub duration: u64, // 模板总时长(微秒)
pub fps: f64,
pub materials: Vec<TemplateMaterial>,
pub tracks: Vec<Track>,
pub import_status: ImportStatus,
pub source_file_path: Option<String>, // 原始draft_content.json文件路径
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
pub is_active: bool,
}
/// 画布配置
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CanvasConfig {
pub width: u32,
pub height: u32,
pub ratio: String, // "original", "16:9", "9:16", etc.
}
/// 模板素材
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TemplateMaterial {
pub id: String,
pub template_id: String,
pub original_id: String, // 原始剪映素材ID
pub name: String,
pub material_type: TemplateMaterialType,
pub original_path: String, // 原始本地路径
pub remote_url: Option<String>, // 上传后的远程URL
pub file_size: Option<u64>,
pub duration: Option<u64>, // 素材时长(微秒)
pub width: Option<u32>,
pub height: Option<u32>,
pub upload_status: UploadStatus,
pub file_exists: bool, // 本地文件是否存在
pub upload_success: bool, // 是否上传成功
pub metadata: Option<String>, // JSON格式的额外元数据
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
/// 轨道
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Track {
pub id: String,
pub template_id: String,
pub name: String,
pub track_type: TrackType,
pub track_index: u32, // 轨道索引
pub segments: Vec<TrackSegment>,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
/// 轨道片段
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TrackSegment {
pub id: String,
pub track_id: String,
pub template_material_id: Option<String>, // 关联的模板素材ID
pub name: String,
pub start_time: u64, // 开始时间(微秒)
pub end_time: u64, // 结束时间(微秒)
pub duration: u64, // 片段时长(微秒)
pub segment_index: u32, // 片段在轨道中的索引
pub properties: Option<String>, // JSON格式的片段属性如变换、效果等
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
/// 模板素材类型
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum TemplateMaterialType {
Video,
Audio,
Image,
Text,
Effect,
Sticker,
Canvas,
Other(String),
}
/// 轨道类型
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum TrackType {
Video,
Audio,
Text,
Sticker,
Effect,
Other(String),
}
/// 导入状态
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum ImportStatus {
Pending, // 等待导入
Parsing, // 解析中
Uploading, // 上传中
Processing, // 处理中
Completed, // 完成
Failed, // 失败
}
/// 上传状态
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum UploadStatus {
Pending, // 等待上传
Uploading, // 上传中
Completed, // 上传完成
Failed, // 上传失败
Skipped, // 跳过(文件不存在等)
}
/// 创建模板请求
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreateTemplateRequest {
pub name: String,
pub description: Option<String>,
pub project_id: Option<String>,
pub source_file_path: String, // draft_content.json文件路径
}
/// 模板导入请求
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ImportTemplateRequest {
pub file_path: String, // draft_content.json文件路径
pub template_name: Option<String>, // 自定义模板名称
pub project_id: Option<String>,
pub auto_upload: bool, // 是否自动上传素材到云端
}
/// 批量导入请求
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BatchImportRequest {
pub folder_path: String, // 包含多个draft_content.json的文件夹路径
pub project_id: Option<String>,
pub auto_upload: bool,
pub max_concurrent: Option<u32>, // 最大并发数
}
/// 导入进度信息
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ImportProgress {
pub template_id: String,
pub template_name: String,
pub status: ImportStatus,
pub total_materials: u32,
pub uploaded_materials: u32,
pub failed_materials: u32,
pub current_operation: String,
pub error_message: Option<String>,
pub progress_percentage: f64, // 0.0 - 100.0
}
impl Template {
/// 创建新的模板实例
pub fn new(
name: String,
canvas_config: CanvasConfig,
duration: u64,
fps: f64,
) -> Self {
let now = Utc::now();
Self {
id: uuid::Uuid::new_v4().to_string(),
name,
description: None,
canvas_config,
duration,
fps,
materials: Vec::new(),
tracks: Vec::new(),
import_status: ImportStatus::Pending,
source_file_path: None,
created_at: now,
updated_at: now,
is_active: true,
}
}
/// 添加素材
pub fn add_material(&mut self, material: TemplateMaterial) {
self.materials.push(material);
self.updated_at = Utc::now();
}
/// 添加轨道
pub fn add_track(&mut self, track: Track) {
self.tracks.push(track);
self.updated_at = Utc::now();
}
/// 更新导入状态
pub fn update_import_status(&mut self, status: ImportStatus) {
self.import_status = status;
self.updated_at = Utc::now();
}
/// 获取所有需要上传的素材
pub fn get_pending_upload_materials(&self) -> Vec<&TemplateMaterial> {
self.materials
.iter()
.filter(|m| matches!(m.upload_status, UploadStatus::Pending))
.collect()
}
/// 获取上传进度
pub fn get_upload_progress(&self) -> f64 {
if self.materials.is_empty() {
return 100.0;
}
let completed = self.materials
.iter()
.filter(|m| matches!(m.upload_status, UploadStatus::Completed | UploadStatus::Skipped))
.count();
(completed as f64 / self.materials.len() as f64) * 100.0
}
}
impl TemplateMaterial {
/// 创建新的模板素材实例
pub fn new(
id: String, // 素材的真实ID来自剪映草稿
template_id: String,
original_id: String, // local_material_id
name: String,
material_type: TemplateMaterialType,
original_path: String,
) -> Self {
let now = Utc::now();
Self {
id, // 使用传入的真实ID
template_id,
original_id, // original_id 对应 local_material_id可以重复
name,
material_type,
original_path,
remote_url: None,
file_size: None,
duration: None,
width: None,
height: None,
upload_status: UploadStatus::Pending,
file_exists: false,
upload_success: false,
metadata: None,
created_at: now,
updated_at: now,
}
}
/// 更新上传状态
pub fn update_upload_status(&mut self, status: UploadStatus, remote_url: Option<String>) {
self.upload_status = status;
if let Some(url) = remote_url {
self.remote_url = Some(url);
}
self.updated_at = Utc::now();
}
/// 检查本地文件是否存在
pub fn file_exists(&self) -> bool {
if self.original_path.is_empty() {
return false;
}
std::path::Path::new(&self.original_path).exists()
}
/// 更新文件存在状态
pub fn update_file_exists(&mut self) {
self.file_exists = self.file_exists();
self.updated_at = Utc::now();
}
/// 更新上传成功状态
pub fn update_upload_success(&mut self, success: bool) {
self.upload_success = success;
self.updated_at = Utc::now();
}
}
impl Track {
/// 创建新的轨道实例
pub fn new(
id: String, // 轨道的真实ID来自剪映草稿
template_id: String,
name: String,
track_type: TrackType,
track_index: u32,
) -> Self {
let now = Utc::now();
Self {
id, // 使用传入的真实ID
template_id,
name,
track_type,
track_index,
segments: Vec::new(),
created_at: now,
updated_at: now,
}
}
/// 添加片段
pub fn add_segment(&mut self, segment: TrackSegment) {
self.segments.push(segment);
self.updated_at = Utc::now();
}
/// 获取轨道总时长
pub fn get_total_duration(&self) -> u64 {
self.segments
.iter()
.map(|s| s.duration)
.max()
.unwrap_or(0)
}
}
impl TrackSegment {
/// 创建新的轨道片段实例
pub fn new(
id: String, // 片段的真实ID来自剪映草稿
track_id: String,
name: String,
start_time: u64,
end_time: u64,
segment_index: u32,
) -> Self {
let now = Utc::now();
Self {
id, // 使用传入的真实ID
track_id,
template_material_id: None,
name,
start_time,
end_time,
duration: end_time - start_time,
segment_index,
properties: None,
created_at: now,
updated_at: now,
}
}
/// 关联模板素材
pub fn associate_material(&mut self, material_id: String) {
self.template_material_id = Some(material_id);
}
}