First working POC
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
//! Tauri commands for offline data access
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::sync::Arc;
|
||||
use tauri::State;
|
||||
|
||||
use super::DatabaseWrapper;
|
||||
use crate::storage::db_service::{DatabaseService, Query, QueryParam};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct OfflineItem {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub item_type: String,
|
||||
pub album_id: Option<String>,
|
||||
pub album_name: Option<String>,
|
||||
pub artists: Option<String>,
|
||||
pub runtime_ticks: Option<i64>,
|
||||
pub primary_image_tag: Option<String>,
|
||||
}
|
||||
|
||||
/// Check if an item is available offline
|
||||
#[tauri::command]
|
||||
pub async fn offline_is_available(
|
||||
db: State<'_, DatabaseWrapper>,
|
||||
item_id: String,
|
||||
) -> Result<bool, String> {
|
||||
let db_service = {
|
||||
let database = db.0.lock().map_err(|e| e.to_string())?;
|
||||
Arc::new(database.service())
|
||||
};
|
||||
|
||||
let query = Query::with_params(
|
||||
"SELECT COUNT(*) FROM downloads WHERE item_id = ? AND status = 'completed'",
|
||||
vec![QueryParam::String(item_id)],
|
||||
);
|
||||
|
||||
let count: i64 = db_service
|
||||
.query_one(query, |row| row.get(0))
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
Ok(count > 0)
|
||||
}
|
||||
|
||||
/// Get all offline items for a user
|
||||
#[tauri::command]
|
||||
pub async fn offline_get_items(
|
||||
db: State<'_, DatabaseWrapper>,
|
||||
user_id: String,
|
||||
) -> Result<Vec<OfflineItem>, String> {
|
||||
let db_service = {
|
||||
let database = db.0.lock().map_err(|e| e.to_string())?;
|
||||
Arc::new(database.service())
|
||||
};
|
||||
|
||||
let query = Query::with_params(
|
||||
"SELECT i.id, i.name, i.item_type, i.album_id, i.album_name, i.artists,
|
||||
i.runtime_ticks, i.primary_image_tag
|
||||
FROM items i
|
||||
INNER JOIN downloads d ON i.id = d.item_id
|
||||
WHERE d.user_id = ? AND d.status = 'completed'
|
||||
ORDER BY d.completed_at DESC",
|
||||
vec![QueryParam::String(user_id)],
|
||||
);
|
||||
|
||||
db_service
|
||||
.query_many(query, |row| {
|
||||
Ok(OfflineItem {
|
||||
id: row.get(0)?,
|
||||
name: row.get(1)?,
|
||||
item_type: row.get(2)?,
|
||||
album_id: row.get(3)?,
|
||||
album_name: row.get(4)?,
|
||||
artists: row.get(5)?,
|
||||
runtime_ticks: row.get(6)?,
|
||||
primary_image_tag: row.get(7)?,
|
||||
})
|
||||
})
|
||||
.await
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
/// Search offline items
|
||||
#[tauri::command]
|
||||
pub async fn offline_search(
|
||||
db: State<'_, DatabaseWrapper>,
|
||||
user_id: String,
|
||||
query: String,
|
||||
) -> Result<Vec<OfflineItem>, String> {
|
||||
let db_service = {
|
||||
let database = db.0.lock().map_err(|e| e.to_string())?;
|
||||
Arc::new(database.service())
|
||||
};
|
||||
|
||||
let search_query = format!("%{}%", query.to_lowercase());
|
||||
|
||||
let db_query = Query::with_params(
|
||||
"SELECT i.id, i.name, i.item_type, i.album_id, i.album_name, i.artists,
|
||||
i.runtime_ticks, i.primary_image_tag
|
||||
FROM items i
|
||||
INNER JOIN downloads d ON i.id = d.item_id
|
||||
WHERE d.user_id = ? AND d.status = 'completed'
|
||||
AND (LOWER(i.name) LIKE ? OR LOWER(i.artists) LIKE ? OR LOWER(i.album_name) LIKE ?)
|
||||
ORDER BY i.name
|
||||
LIMIT 50",
|
||||
vec![
|
||||
QueryParam::String(user_id),
|
||||
QueryParam::String(search_query.clone()),
|
||||
QueryParam::String(search_query.clone()),
|
||||
QueryParam::String(search_query),
|
||||
],
|
||||
);
|
||||
|
||||
db_service
|
||||
.query_many(db_query, |row| {
|
||||
Ok(OfflineItem {
|
||||
id: row.get(0)?,
|
||||
name: row.get(1)?,
|
||||
item_type: row.get(2)?,
|
||||
album_id: row.get(3)?,
|
||||
album_name: row.get(4)?,
|
||||
artists: row.get(5)?,
|
||||
runtime_ticks: row.get(6)?,
|
||||
primary_image_tag: row.get(7)?,
|
||||
})
|
||||
})
|
||||
.await
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_offline_item_serialization() {
|
||||
let item = OfflineItem {
|
||||
id: "123".to_string(),
|
||||
name: "Test Song".to_string(),
|
||||
item_type: "Audio".to_string(),
|
||||
album_id: Some("album1".to_string()),
|
||||
album_name: Some("Test Album".to_string()),
|
||||
artists: Some("Artist 1".to_string()),
|
||||
runtime_ticks: Some(180000000),
|
||||
primary_image_tag: Some("tag123".to_string()),
|
||||
};
|
||||
|
||||
let json = serde_json::to_string(&item).unwrap();
|
||||
assert!(json.contains("\"itemType\":\"Audio\""));
|
||||
assert!(json.contains("\"albumName\":\"Test Album\""));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user