First working POC

This commit is contained in:
2026-01-26 22:21:54 +01:00
commit cfddc1edea
255 changed files with 77606 additions and 0 deletions
+154
View File
@@ -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\""));
}
}