Fix download + casting breakages from the specta migration
Casting: restore camelCase serialization on SessionInfo/NowPlayingItem/PlayState (container rename_all = "camelCase" + per-field PascalCase serde aliases so Jellyfin PascalCase still deserializes). Bindings and the existing frontend are camelCase again — casting works with no frontend session changes. Downloads: migrate DownloadButton.svelte and downloads.ts to the typed commands.downloadItemAndStart / downloadItem / downloadVideo wrappers (request objects), matching the bundled backend signatures. Tooling: - Enable tauri-specta ErrorHandlingMode::Throw so commands.* return Promise<T> and throw (drop-in for invoke()). - Disambiguate specta name collisions: player MediaItem/MediaSource -> PlayerMediaItem/PlayerMediaSource, auth ServerInfo -> AuthServerInfo. - Regenerate bindings.ts; svelte-check passes (0 errors).
This commit is contained in:
parent
7d07b38d50
commit
ea342d76e3
@ -12,6 +12,7 @@ pub use session_verifier::SessionVerifier;
|
||||
/// Server information returned from Jellyfin
|
||||
#[derive(specta::Type, Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[specta(rename = "AuthServerInfo")]
|
||||
pub struct ServerInfo {
|
||||
pub name: String,
|
||||
pub version: String,
|
||||
|
||||
@ -382,71 +382,102 @@ fn default_true() -> bool {
|
||||
|
||||
/// Session information from Jellyfin
|
||||
#[derive(specta::Type, Debug, Clone, Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SessionInfo {
|
||||
#[serde(default)]
|
||||
#[serde(alias = "Id")]
|
||||
pub id: Option<String>,
|
||||
#[serde(default)]
|
||||
#[serde(alias = "UserId")]
|
||||
pub user_id: Option<String>,
|
||||
#[serde(default)]
|
||||
#[serde(alias = "UserName")]
|
||||
pub user_name: Option<String>,
|
||||
#[serde(default)]
|
||||
#[serde(alias = "Client")]
|
||||
pub client: Option<String>,
|
||||
#[serde(default)]
|
||||
#[serde(alias = "DeviceName")]
|
||||
pub device_name: Option<String>,
|
||||
#[serde(default)]
|
||||
#[serde(alias = "DeviceId")]
|
||||
pub device_id: Option<String>,
|
||||
#[serde(default)]
|
||||
#[serde(alias = "ApplicationVersion")]
|
||||
pub application_version: Option<String>,
|
||||
#[serde(default)]
|
||||
#[serde(alias = "IsActive")]
|
||||
pub is_active: Option<bool>,
|
||||
#[serde(default)]
|
||||
#[serde(alias = "SupportsMediaControl")]
|
||||
pub supports_media_control: Option<bool>,
|
||||
#[serde(default = "default_true")]
|
||||
#[serde(alias = "SupportsRemoteControl")]
|
||||
pub supports_remote_control: bool,
|
||||
#[serde(default)]
|
||||
#[serde(alias = "NowPlayingItem")]
|
||||
pub now_playing_item: Option<NowPlayingItem>,
|
||||
#[serde(default)]
|
||||
#[serde(alias = "PlayState")]
|
||||
pub play_state: Option<PlayState>,
|
||||
#[serde(default)]
|
||||
#[serde(alias = "PlayableMediaTypes")]
|
||||
pub playable_media_types: Option<Vec<String>>,
|
||||
#[serde(default)]
|
||||
#[serde(alias = "SupportedCommands")]
|
||||
pub supported_commands: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(specta::Type, Debug, Clone, Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct NowPlayingItem {
|
||||
#[serde(alias = "Id")]
|
||||
pub id: Option<String>,
|
||||
#[serde(alias = "Name")]
|
||||
pub name: Option<String>,
|
||||
#[serde(alias = "RunTimeTicks")]
|
||||
pub run_time_ticks: Option<i64>,
|
||||
#[serde(alias = "Album")]
|
||||
pub album: Option<String>,
|
||||
#[serde(alias = "AlbumId")]
|
||||
pub album_id: Option<String>,
|
||||
#[serde(alias = "AlbumArtist")]
|
||||
pub album_artist: Option<String>,
|
||||
#[serde(alias = "Artists")]
|
||||
pub artists: Option<Vec<String>>,
|
||||
#[serde(alias = "ImageTags")]
|
||||
pub image_tags: Option<std::collections::HashMap<String, String>>,
|
||||
#[serde(alias = "PrimaryImageTag")]
|
||||
pub primary_image_tag: Option<String>,
|
||||
#[serde(alias = "AlbumPrimaryImageTag")]
|
||||
pub album_primary_image_tag: Option<String>,
|
||||
#[serde(rename = "Type")]
|
||||
pub item_type: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(specta::Type, Debug, Clone, Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PlayState {
|
||||
#[serde(default)]
|
||||
#[serde(alias = "PositionTicks")]
|
||||
pub position_ticks: Option<i64>,
|
||||
#[serde(default)]
|
||||
#[serde(alias = "CanSeek")]
|
||||
pub can_seek: Option<bool>,
|
||||
#[serde(default)]
|
||||
#[serde(alias = "IsPaused")]
|
||||
pub is_paused: Option<bool>,
|
||||
#[serde(default)]
|
||||
#[serde(alias = "IsMuted")]
|
||||
pub is_muted: Option<bool>,
|
||||
#[serde(default)]
|
||||
#[serde(alias = "VolumeLevel")]
|
||||
pub volume_level: Option<i32>,
|
||||
#[serde(default)]
|
||||
#[serde(alias = "RepeatMode")]
|
||||
pub repeat_mode: Option<String>,
|
||||
#[serde(default)]
|
||||
#[serde(alias = "ShuffleMode")]
|
||||
pub shuffle_mode: Option<String>,
|
||||
}
|
||||
|
||||
|
||||
@ -351,6 +351,9 @@ fn create_player_backend(
|
||||
/// bindings-export test so the TypeScript bindings always match the handler.
|
||||
fn specta_builder() -> Builder<tauri::Wry> {
|
||||
Builder::<tauri::Wry>::new()
|
||||
// Throw on error so generated `commands.*` return Promise<T> and throw,
|
||||
// matching the existing frontend's invoke() try/catch convention.
|
||||
.error_handling(tauri_specta::ErrorHandlingMode::Throw)
|
||||
.commands(tauri_specta::collect_commands![
|
||||
// Player commands
|
||||
player_play_item,
|
||||
|
||||
@ -42,6 +42,7 @@ pub struct SubtitleTrack {
|
||||
/// TRACES: UR-003, UR-004 | DR-002
|
||||
#[derive(specta::Type, Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[specta(rename = "PlayerMediaItem")]
|
||||
pub struct MediaItem {
|
||||
/// Unique identifier
|
||||
pub id: String,
|
||||
@ -116,6 +117,7 @@ pub enum MediaType {
|
||||
/// TRACES: UR-002, UR-003, UR-004, UR-011 | DR-003
|
||||
#[derive(specta::Type, Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(tag = "type", rename_all = "lowercase")]
|
||||
#[specta(rename = "PlayerMediaSource")]
|
||||
pub enum MediaSource {
|
||||
/// Streaming from Jellyfin server
|
||||
Remote {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -2,6 +2,7 @@
|
||||
import { downloads } from "$lib/stores/downloads";
|
||||
import { auth } from "$lib/stores/auth";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { commands } from "$lib/api/bindings";
|
||||
import DownloadButtonCore from "./DownloadButtonCore.svelte";
|
||||
import type { DownloadState } from "./DownloadButtonCore.svelte";
|
||||
|
||||
@ -84,14 +85,14 @@
|
||||
console.log(" Target directory:", targetDir);
|
||||
|
||||
// Queue and start download in single atomic operation
|
||||
const downloadId = await invoke<number>("download_item_and_start", {
|
||||
const downloadId = await commands.downloadItemAndStart({
|
||||
itemId,
|
||||
userId,
|
||||
streamUrl,
|
||||
targetDir,
|
||||
itemName: itemName || undefined,
|
||||
artistName: artistName || undefined,
|
||||
albumName: albumName || undefined,
|
||||
itemName: itemName || null,
|
||||
artistName: artistName || null,
|
||||
albumName: albumName || null,
|
||||
});
|
||||
console.log(" Download queued and started with ID:", downloadId);
|
||||
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
// TRACES: UR-011, UR-013, UR-018 | DR-015, DR-017
|
||||
import { writable, derived, get } from 'svelte/store';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { commands } from '$lib/api/bindings';
|
||||
import { listen, type UnlistenFn } from '@tauri-apps/api/event';
|
||||
|
||||
// Event listener state
|
||||
@ -150,15 +151,16 @@ function createDownloadsStore() {
|
||||
): Promise<number> {
|
||||
try {
|
||||
console.log('📥 downloadItem called:', { itemId, userId, filePath, itemName, artistName, albumName });
|
||||
const downloadId = await invoke<number>('download_item', {
|
||||
const downloadId = await commands.downloadItem({
|
||||
itemId,
|
||||
userId,
|
||||
filePath,
|
||||
mimeType,
|
||||
priority,
|
||||
itemName,
|
||||
artistName,
|
||||
albumName
|
||||
mimeType: mimeType ?? null,
|
||||
priority: priority ?? null,
|
||||
itemName: itemName ?? null,
|
||||
artistName: artistName ?? null,
|
||||
albumName: albumName ?? null,
|
||||
expectedSize: null
|
||||
});
|
||||
console.log(' Got download ID from backend:', downloadId);
|
||||
|
||||
@ -222,18 +224,18 @@ function createDownloadsStore() {
|
||||
qualityPreset,
|
||||
seriesName
|
||||
});
|
||||
const downloadId = await invoke<number>('download_video', {
|
||||
const downloadId = await commands.downloadVideo({
|
||||
itemId,
|
||||
userId,
|
||||
filePath,
|
||||
mimeType,
|
||||
priority,
|
||||
itemName,
|
||||
qualityPreset,
|
||||
seriesName,
|
||||
seasonName,
|
||||
episodeNumber,
|
||||
seasonNumber
|
||||
mimeType: mimeType ?? null,
|
||||
priority: priority ?? null,
|
||||
itemName: itemName ?? null,
|
||||
qualityPreset: qualityPreset ?? null,
|
||||
seriesName: seriesName ?? null,
|
||||
seasonName: seasonName ?? null,
|
||||
episodeNumber: episodeNumber ?? null,
|
||||
seasonNumber: seasonNumber ?? null
|
||||
});
|
||||
console.log(' Got download ID from backend:', downloadId);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user