- Add #[specta::specta] to all 201 #[tauri::command] functions. - Derive specta::Type on all IPC DTOs (repository/types, settings, player/storage/ download command DTOs, player enums, jellyfin SessionInfo/NowPlayingItem/PlayState, ThumbnailCacheStats, DownloadInfo, CacheConfig, etc.). - Replace tauri::generate_handler! with a tauri_specta::Builder + collect_commands! in lib.rs (exports bindings.ts in debug builds). Two contract changes required by specta constraints (frontend migration follows): - specta caps command arity at 10 args: download_item_and_start / download_item / download_video now take a single request struct (params bundled, body unchanged via destructuring). - specta can't parse split serde rename_all: SessionInfo/NowPlayingItem/PlayState switched to rename_all = "PascalCase" (Jellyfin deserialization preserved; these now serialize PascalCase to the frontend). cargo check --lib is clean (0 errors). Frontend migration to bindings.ts is the next step.
123 lines
3.8 KiB
Rust
123 lines
3.8 KiB
Rust
//! Tauri commands for playlist management
|
|
//! Uses handle-based system: UUID -> Arc<HybridRepository>
|
|
//!
|
|
//! TRACES: UR-014 | JA-019, JA-020
|
|
|
|
use log::debug;
|
|
use tauri::State;
|
|
|
|
use crate::repository::{MediaRepository, types::*};
|
|
use super::repository::RepositoryManagerWrapper;
|
|
|
|
/// Create a new playlist
|
|
#[tauri::command]
|
|
#[specta::specta]
|
|
pub async fn playlist_create(
|
|
manager: State<'_, RepositoryManagerWrapper>,
|
|
handle: String,
|
|
name: String,
|
|
item_ids: Option<Vec<String>>,
|
|
) -> Result<PlaylistCreatedResult, String> {
|
|
debug!("[PLAYLIST] create called: name={}", name);
|
|
let repo = manager.0.get(&handle).ok_or("Repository not found")?;
|
|
let ids = item_ids.unwrap_or_default();
|
|
repo.as_ref().create_playlist(&name, &ids)
|
|
.await
|
|
.map_err(|e| format!("{:?}", e))
|
|
}
|
|
|
|
/// Delete a playlist
|
|
#[tauri::command]
|
|
#[specta::specta]
|
|
pub async fn playlist_delete(
|
|
manager: State<'_, RepositoryManagerWrapper>,
|
|
handle: String,
|
|
playlist_id: String,
|
|
) -> Result<(), String> {
|
|
debug!("[PLAYLIST] delete called: id={}", playlist_id);
|
|
let repo = manager.0.get(&handle).ok_or("Repository not found")?;
|
|
repo.as_ref().delete_playlist(&playlist_id)
|
|
.await
|
|
.map_err(|e| format!("{:?}", e))
|
|
}
|
|
|
|
/// Rename a playlist
|
|
#[tauri::command]
|
|
#[specta::specta]
|
|
pub async fn playlist_rename(
|
|
manager: State<'_, RepositoryManagerWrapper>,
|
|
handle: String,
|
|
playlist_id: String,
|
|
name: String,
|
|
) -> Result<(), String> {
|
|
debug!("[PLAYLIST] rename called: id={}, name={}", playlist_id, name);
|
|
let repo = manager.0.get(&handle).ok_or("Repository not found")?;
|
|
repo.as_ref().rename_playlist(&playlist_id, &name)
|
|
.await
|
|
.map_err(|e| format!("{:?}", e))
|
|
}
|
|
|
|
/// Get playlist items with PlaylistItemId
|
|
#[tauri::command]
|
|
#[specta::specta]
|
|
pub async fn playlist_get_items(
|
|
manager: State<'_, RepositoryManagerWrapper>,
|
|
handle: String,
|
|
playlist_id: String,
|
|
) -> Result<Vec<PlaylistEntry>, String> {
|
|
debug!("[PLAYLIST] get_items called: id={}", playlist_id);
|
|
let repo = manager.0.get(&handle).ok_or("Repository not found")?;
|
|
repo.as_ref().get_playlist_items(&playlist_id)
|
|
.await
|
|
.map_err(|e| format!("{:?}", e))
|
|
}
|
|
|
|
/// Add items to a playlist
|
|
#[tauri::command]
|
|
#[specta::specta]
|
|
pub async fn playlist_add_items(
|
|
manager: State<'_, RepositoryManagerWrapper>,
|
|
handle: String,
|
|
playlist_id: String,
|
|
item_ids: Vec<String>,
|
|
) -> Result<(), String> {
|
|
debug!("[PLAYLIST] add_items called: id={}, count={}", playlist_id, item_ids.len());
|
|
let repo = manager.0.get(&handle).ok_or("Repository not found")?;
|
|
repo.as_ref().add_to_playlist(&playlist_id, &item_ids)
|
|
.await
|
|
.map_err(|e| format!("{:?}", e))
|
|
}
|
|
|
|
/// Remove items from a playlist (uses PlaylistItemId entry IDs, NOT media item IDs)
|
|
#[tauri::command]
|
|
#[specta::specta]
|
|
pub async fn playlist_remove_items(
|
|
manager: State<'_, RepositoryManagerWrapper>,
|
|
handle: String,
|
|
playlist_id: String,
|
|
entry_ids: Vec<String>,
|
|
) -> Result<(), String> {
|
|
debug!("[PLAYLIST] remove_items called: id={}, count={}", playlist_id, entry_ids.len());
|
|
let repo = manager.0.get(&handle).ok_or("Repository not found")?;
|
|
repo.as_ref().remove_from_playlist(&playlist_id, &entry_ids)
|
|
.await
|
|
.map_err(|e| format!("{:?}", e))
|
|
}
|
|
|
|
/// Move a playlist item to a new position
|
|
#[tauri::command]
|
|
#[specta::specta]
|
|
pub async fn playlist_move_item(
|
|
manager: State<'_, RepositoryManagerWrapper>,
|
|
handle: String,
|
|
playlist_id: String,
|
|
item_id: String,
|
|
new_index: u32,
|
|
) -> Result<(), String> {
|
|
debug!("[PLAYLIST] move_item called: playlist={}, item={}, index={}", playlist_id, item_id, new_index);
|
|
let repo = manager.0.get(&handle).ok_or("Repository not found")?;
|
|
repo.as_ref().move_playlist_item(&playlist_id, &item_id, new_index)
|
|
.await
|
|
.map_err(|e| format!("{:?}", e))
|
|
}
|