use crate::error::DatabaseError;
use crate::types::DownloadId;
use crate::{Error, Result};
use super::{Database, Download, NewDownload};
impl Database {
pub async fn insert_download(&self, download: &NewDownload) -> Result<DownloadId> {
let now = chrono::Utc::now().timestamp();
let result = sqlx::query(
r#"
INSERT INTO downloads (
name, nzb_path, nzb_meta_name, nzb_hash, job_name,
category, destination, post_process, priority, status,
progress, speed_bps, size_bytes, downloaded_bytes,
created_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
"#,
)
.bind(&download.name)
.bind(&download.nzb_path)
.bind(&download.nzb_meta_name)
.bind(&download.nzb_hash)
.bind(&download.job_name)
.bind(&download.category)
.bind(&download.destination)
.bind(download.post_process)
.bind(download.priority)
.bind(download.status)
.bind(0.0f32) .bind(0i64) .bind(download.size_bytes)
.bind(0i64) .bind(now)
.execute(&self.pool)
.await
.map_err(|e| {
Error::Database(DatabaseError::QueryFailed(format!(
"Failed to insert download: {}",
e
)))
})?;
Ok(DownloadId(result.last_insert_rowid()))
}
pub async fn get_download(&self, id: DownloadId) -> Result<Option<Download>> {
let row = sqlx::query_as::<_, Download>(
r#"
SELECT
id, name, nzb_path, nzb_meta_name, nzb_hash, job_name,
category, destination, post_process, priority, status,
progress, speed_bps, size_bytes, downloaded_bytes,
error_message, created_at, started_at, completed_at,
direct_unpack_state, direct_unpack_extracted_count
FROM downloads
WHERE id = ?
"#,
)
.bind(id)
.fetch_optional(&self.pool)
.await
.map_err(|e| {
Error::Database(DatabaseError::QueryFailed(format!(
"Failed to get download: {}",
e
)))
})?;
Ok(row)
}
pub async fn list_downloads(&self) -> Result<Vec<Download>> {
let rows = sqlx::query_as::<_, Download>(
r#"
SELECT
id, name, nzb_path, nzb_meta_name, nzb_hash, job_name,
category, destination, post_process, priority, status,
progress, speed_bps, size_bytes, downloaded_bytes,
error_message, created_at, started_at, completed_at,
direct_unpack_state, direct_unpack_extracted_count
FROM downloads
ORDER BY priority DESC, created_at ASC
"#,
)
.fetch_all(&self.pool)
.await
.map_err(|e| {
Error::Database(DatabaseError::QueryFailed(format!(
"Failed to list downloads: {}",
e
)))
})?;
Ok(rows)
}
pub async fn list_downloads_by_status(&self, status: i32) -> Result<Vec<Download>> {
let rows = sqlx::query_as::<_, Download>(
r#"
SELECT
id, name, nzb_path, nzb_meta_name, nzb_hash, job_name,
category, destination, post_process, priority, status,
progress, speed_bps, size_bytes, downloaded_bytes,
error_message, created_at, started_at, completed_at,
direct_unpack_state, direct_unpack_extracted_count
FROM downloads
WHERE status = ?
ORDER BY priority DESC, created_at ASC
"#,
)
.bind(status)
.fetch_all(&self.pool)
.await
.map_err(|e| {
Error::Database(DatabaseError::QueryFailed(format!(
"Failed to list downloads by status: {}",
e
)))
})?;
Ok(rows)
}
pub async fn update_status(&self, id: DownloadId, status: i32) -> Result<()> {
sqlx::query("UPDATE downloads SET status = ? WHERE id = ?")
.bind(status)
.bind(id)
.execute(&self.pool)
.await
.map_err(|e| {
Error::Database(DatabaseError::QueryFailed(format!(
"Failed to update status: {}",
e
)))
})?;
Ok(())
}
pub async fn update_progress(
&self,
id: DownloadId,
progress: f32,
speed_bps: u64,
downloaded_bytes: u64,
) -> Result<()> {
sqlx::query(
"UPDATE downloads SET progress = ?, speed_bps = ?, downloaded_bytes = ? WHERE id = ?",
)
.bind(progress)
.bind(speed_bps as i64)
.bind(downloaded_bytes as i64)
.bind(id)
.execute(&self.pool)
.await
.map_err(|e| {
Error::Database(DatabaseError::QueryFailed(format!(
"Failed to update progress: {}",
e
)))
})?;
Ok(())
}
pub async fn update_priority(&self, id: DownloadId, priority: i32) -> Result<()> {
sqlx::query("UPDATE downloads SET priority = ? WHERE id = ?")
.bind(priority)
.bind(id)
.execute(&self.pool)
.await
.map_err(|e| {
Error::Database(DatabaseError::QueryFailed(format!(
"Failed to update priority: {}",
e
)))
})?;
Ok(())
}
pub async fn set_error(&self, id: DownloadId, error: &str) -> Result<()> {
sqlx::query("UPDATE downloads SET error_message = ? WHERE id = ?")
.bind(error)
.bind(id)
.execute(&self.pool)
.await
.map_err(|e| {
Error::Database(DatabaseError::QueryFailed(format!(
"Failed to set error: {}",
e
)))
})?;
Ok(())
}
pub async fn set_started(&self, id: DownloadId) -> Result<()> {
let now = chrono::Utc::now().timestamp();
sqlx::query("UPDATE downloads SET started_at = ? WHERE id = ?")
.bind(now)
.bind(id)
.execute(&self.pool)
.await
.map_err(|e| {
Error::Database(DatabaseError::QueryFailed(format!(
"Failed to set started timestamp: {}",
e
)))
})?;
Ok(())
}
pub async fn set_completed(&self, id: DownloadId) -> Result<()> {
let now = chrono::Utc::now().timestamp();
sqlx::query("UPDATE downloads SET completed_at = ? WHERE id = ?")
.bind(now)
.bind(id)
.execute(&self.pool)
.await
.map_err(|e| {
Error::Database(DatabaseError::QueryFailed(format!(
"Failed to set completed timestamp: {}",
e
)))
})?;
Ok(())
}
pub async fn delete_download(&self, id: DownloadId) -> Result<()> {
sqlx::query("DELETE FROM downloads WHERE id = ?")
.bind(id)
.execute(&self.pool)
.await
.map_err(|e| {
Error::Database(DatabaseError::QueryFailed(format!(
"Failed to delete download: {}",
e
)))
})?;
Ok(())
}
pub async fn get_incomplete_downloads(&self) -> Result<Vec<Download>> {
let rows = sqlx::query_as::<_, Download>(
r#"
SELECT
id, name, nzb_path, nzb_meta_name, nzb_hash, job_name,
category, destination, post_process, priority, status,
progress, speed_bps, size_bytes, downloaded_bytes,
error_message, created_at, started_at, completed_at,
direct_unpack_state, direct_unpack_extracted_count
FROM downloads
WHERE status IN (0, 1, 3)
ORDER BY priority DESC, created_at ASC
"#,
)
.fetch_all(&self.pool)
.await
.map_err(|e| {
Error::Database(DatabaseError::QueryFailed(format!(
"Failed to get incomplete downloads: {}",
e
)))
})?;
Ok(rows)
}
pub async fn get_all_downloads(&self) -> Result<Vec<Download>> {
let rows = sqlx::query_as::<_, Download>(
r#"
SELECT
id, name, nzb_path, nzb_meta_name, nzb_hash, job_name,
category, destination, post_process, priority, status,
progress, speed_bps, size_bytes, downloaded_bytes,
error_message, created_at, started_at, completed_at,
direct_unpack_state, direct_unpack_extracted_count
FROM downloads
ORDER BY created_at ASC
"#,
)
.fetch_all(&self.pool)
.await
.map_err(|e| {
Error::Database(DatabaseError::QueryFailed(format!(
"Failed to get all downloads: {}",
e
)))
})?;
Ok(rows)
}
}