Skip to main content

jellytau_lib/commands/storage/
series_prefs.rs

1//! Per-series preferred audio track commands.
2//!
3//! TRACES: UR-021 | DR-024
4
5use serde::{Deserialize, Serialize};
6use std::sync::Arc;
7use tauri::State;
8
9use super::DatabaseWrapper;
10use crate::storage::db_service::{DatabaseService, Query, QueryParam};
11
12/// Audio track preference for a series
13#[derive(specta::Type, Debug, Clone, Serialize, Deserialize)]
14#[serde(rename_all = "camelCase")]
15pub struct SeriesAudioPreference {
16    pub series_id: String,
17    pub audio_track_display_title: Option<String>,
18    pub audio_track_language: Option<String>,
19    pub audio_track_index: Option<i32>,
20}
21
22/// Save user's preferred audio track for a series
23#[tauri::command]
24#[specta::specta]
25pub async fn storage_save_series_audio_preference(
26    db: State<'_, DatabaseWrapper>,
27    user_id: String,
28    series_id: String,
29    server_id: String,
30    audio_track_display_title: Option<String>,
31    audio_track_language: Option<String>,
32    audio_track_index: Option<i32>,
33) -> Result<(), String> {
34    let db_service = {
35        let database = db.0.lock().map_err(|e| e.to_string())?;
36        Arc::new(database.service())
37    };
38
39    let query = Query::with_params(
40        "INSERT INTO series_audio_preferences
41            (user_id, series_id, server_id, audio_track_display_title, audio_track_language, audio_track_index, updated_at)
42         VALUES (?, ?, ?, ?, ?, ?, datetime('now'))
43         ON CONFLICT (user_id, series_id, server_id) DO UPDATE SET
44            audio_track_display_title = excluded.audio_track_display_title,
45            audio_track_language = excluded.audio_track_language,
46            audio_track_index = excluded.audio_track_index,
47            updated_at = datetime('now')",
48        vec![
49            QueryParam::String(user_id),
50            QueryParam::String(series_id),
51            QueryParam::String(server_id),
52            audio_track_display_title.map(QueryParam::String).unwrap_or(QueryParam::Null),
53            audio_track_language.map(QueryParam::String).unwrap_or(QueryParam::Null),
54            audio_track_index.map(|i| QueryParam::Int64(i as i64)).unwrap_or(QueryParam::Null),
55        ],
56    );
57
58    db_service.execute(query).await.map_err(|e| e.to_string())?;
59    Ok(())
60}
61
62/// Get user's preferred audio track for a series
63#[tauri::command]
64#[specta::specta]
65pub async fn storage_get_series_audio_preference(
66    db: State<'_, DatabaseWrapper>,
67    user_id: String,
68    series_id: String,
69) -> Result<Option<SeriesAudioPreference>, String> {
70    let db_service = {
71        let database = db.0.lock().map_err(|e| e.to_string())?;
72        Arc::new(database.service())
73    };
74
75    let query = Query::with_params(
76        "SELECT series_id, audio_track_display_title, audio_track_language, audio_track_index
77         FROM series_audio_preferences
78         WHERE user_id = ? AND series_id = ?",
79        vec![QueryParam::String(user_id), QueryParam::String(series_id)],
80    );
81
82    let preference = db_service
83        .query_optional(query, |row| {
84            Ok(SeriesAudioPreference {
85                series_id: row.get(0)?,
86                audio_track_display_title: row.get(1)?,
87                audio_track_language: row.get(2)?,
88                audio_track_index: row.get(3)?,
89            })
90        })
91        .await
92        .map_err(|e| e.to_string())?;
93
94    Ok(preference)
95}