Skip to main content

mvsep_api_tester/db/
tasks_db.rs

1//! 任务数据库(tasks.db)。
2//!
3//! 独立的 SQLite 数据库,追踪任务生命周期和下载进度。
4//!
5//! ## 任务状态流转
6//!
7//! ```text
8//! uploaded → queued → processing → done
9//!                     → failed
10//!                               → expired(文件过期)
11//! ```
12//!
13//! ## 输出文件追踪
14//!
15//! `output_files` 字段存 JSON,记录每个产物的远程 URL、本地路径、下载状态:
16//!
17//! ```json
18//! [
19//!   {"remote_name":"vocals.flac","url":"https://...","downloaded":true,"local_path":"/out/..."},
20//!   {"remote_name":"other.flac","url":"https://...","downloaded":false,"local_path":null}
21//! ]
22//! ```
23
24use rusqlite::Connection;
25use std::sync::Mutex;
26
27pub struct TasksDatabase {
28    pub conn: Mutex<Connection>,
29}
30
31impl TasksDatabase {
32    pub fn new(db_path: Option<&str>) -> anyhow::Result<Self> {
33        let path = db_path.map(|p| p.to_string()).unwrap_or_else(|| {
34            crate::utils::paths::tasks_db_path()
35                .to_string_lossy()
36                .to_string()
37        });
38
39        let conn = Connection::open(&path)?;
40        conn.execute_batch("PRAGMA journal_mode=WAL")?;
41        conn.execute_batch("PRAGMA foreign_keys=ON")?;
42        conn.execute_batch("PRAGMA busy_timeout=5000")?;
43
44        let db = Self {
45            conn: Mutex::new(conn),
46        };
47
48        {
49            let locked = db
50                .conn
51                .lock()
52                .map_err(|e| anyhow::anyhow!("Poison error: {}", e))?;
53            run_migrations(&locked)?;
54        }
55
56        Ok(db)
57    }
58
59    pub fn with_conn<F, T>(&self, f: F) -> anyhow::Result<T>
60    where
61        F: FnOnce(&Connection) -> anyhow::Result<T>,
62    {
63        let conn = self
64            .conn
65            .lock()
66            .map_err(|e| anyhow::anyhow!("TasksDatabase lock poisoned: {}", e))?;
67        f(&conn)
68    }
69}
70
71fn run_migrations(conn: &Connection) -> anyhow::Result<()> {
72    conn.execute_batch(
73        "
74        CREATE TABLE IF NOT EXISTS tasks (
75            hash TEXT PRIMARY KEY,
76            file_name TEXT NOT NULL,
77            algorithm_id INTEGER NOT NULL,
78            algorithm_name TEXT NOT NULL,
79            model_id INTEGER,
80            model_name TEXT,
81            model2_id INTEGER,
82            model2_name TEXT,
83            model3_id INTEGER,
84            model3_name TEXT,
85            format INTEGER NOT NULL DEFAULT 1,
86            status TEXT NOT NULL DEFAULT 'uploaded',
87            progress REAL DEFAULT 0,
88            created_at INTEGER NOT NULL,
89            output_files TEXT DEFAULT '[]',
90            error TEXT,
91            message TEXT,
92            queue_count INTEGER,
93            current_order INTEGER,
94            phase TEXT DEFAULT 'uploaded',
95            download_file_name TEXT,
96            download_bytes INTEGER DEFAULT 0,
97            download_total_bytes INTEGER,
98            download_speed_bps REAL DEFAULT 0,
99            download_percent REAL DEFAULT 0
100        );
101
102        CREATE TABLE IF NOT EXISTS task_history (
103            id TEXT PRIMARY KEY,
104            file_name TEXT NOT NULL,
105            algorithm_id INTEGER NOT NULL,
106            algorithm_name TEXT NOT NULL,
107            model_id INTEGER,
108            model_name TEXT,
109            model2_id INTEGER,
110            model2_name TEXT,
111            model3_id INTEGER,
112            model3_name TEXT,
113            format_id INTEGER NOT NULL,
114            format_name TEXT NOT NULL,
115            status TEXT NOT NULL CHECK(status IN ('done', 'failed')),
116            created_at INTEGER NOT NULL,
117            completed_at INTEGER,
118            output_files TEXT DEFAULT '[]',
119            output_path TEXT,
120            error TEXT
121        );
122
123        CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status);
124        CREATE INDEX IF NOT EXISTS idx_tasks_created_at ON tasks(created_at);
125        CREATE INDEX IF NOT EXISTS idx_task_history_completed_at ON task_history(completed_at);
126        CREATE INDEX IF NOT EXISTS idx_task_history_algorithm_id ON task_history(algorithm_id);
127        ",
128    )?;
129    Ok(())
130}