diff --git a/src-tauri/src/auth/mod.rs b/src-tauri/src/auth/mod.rs index 99fe09b..9a7a601 100644 --- a/src-tauri/src/auth/mod.rs +++ b/src-tauri/src/auth/mod.rs @@ -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, diff --git a/src-tauri/src/jellyfin/client.rs b/src-tauri/src/jellyfin/client.rs index 3ae42e2..b4bec70 100644 --- a/src-tauri/src/jellyfin/client.rs +++ b/src-tauri/src/jellyfin/client.rs @@ -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, #[serde(default)] + #[serde(alias = "UserId")] pub user_id: Option, #[serde(default)] + #[serde(alias = "UserName")] pub user_name: Option, #[serde(default)] + #[serde(alias = "Client")] pub client: Option, #[serde(default)] + #[serde(alias = "DeviceName")] pub device_name: Option, #[serde(default)] + #[serde(alias = "DeviceId")] pub device_id: Option, #[serde(default)] + #[serde(alias = "ApplicationVersion")] pub application_version: Option, #[serde(default)] + #[serde(alias = "IsActive")] pub is_active: Option, #[serde(default)] + #[serde(alias = "SupportsMediaControl")] pub supports_media_control: Option, #[serde(default = "default_true")] + #[serde(alias = "SupportsRemoteControl")] pub supports_remote_control: bool, #[serde(default)] + #[serde(alias = "NowPlayingItem")] pub now_playing_item: Option, #[serde(default)] + #[serde(alias = "PlayState")] pub play_state: Option, #[serde(default)] + #[serde(alias = "PlayableMediaTypes")] pub playable_media_types: Option>, #[serde(default)] + #[serde(alias = "SupportedCommands")] pub supported_commands: Option>, } #[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, + #[serde(alias = "Name")] pub name: Option, + #[serde(alias = "RunTimeTicks")] pub run_time_ticks: Option, + #[serde(alias = "Album")] pub album: Option, + #[serde(alias = "AlbumId")] pub album_id: Option, + #[serde(alias = "AlbumArtist")] pub album_artist: Option, + #[serde(alias = "Artists")] pub artists: Option>, + #[serde(alias = "ImageTags")] pub image_tags: Option>, + #[serde(alias = "PrimaryImageTag")] pub primary_image_tag: Option, + #[serde(alias = "AlbumPrimaryImageTag")] pub album_primary_image_tag: Option, #[serde(rename = "Type")] pub item_type: Option, } #[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, #[serde(default)] + #[serde(alias = "CanSeek")] pub can_seek: Option, #[serde(default)] + #[serde(alias = "IsPaused")] pub is_paused: Option, #[serde(default)] + #[serde(alias = "IsMuted")] pub is_muted: Option, #[serde(default)] + #[serde(alias = "VolumeLevel")] pub volume_level: Option, #[serde(default)] + #[serde(alias = "RepeatMode")] pub repeat_mode: Option, #[serde(default)] + #[serde(alias = "ShuffleMode")] pub shuffle_mode: Option, } diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 0999fb3..9ff9950 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -351,6 +351,9 @@ fn create_player_backend( /// bindings-export test so the TypeScript bindings always match the handler. fn specta_builder() -> Builder { Builder::::new() + // Throw on error so generated `commands.*` return Promise 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, diff --git a/src-tauri/src/player/media.rs b/src-tauri/src/player/media.rs index d2a5b72..a3420e8 100644 --- a/src-tauri/src/player/media.rs +++ b/src-tauri/src/player/media.rs @@ -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 { diff --git a/src/lib/api/bindings.ts b/src/lib/api/bindings.ts index 46d214c..809eba0 100644 --- a/src/lib/api/bindings.ts +++ b/src/lib/api/bindings.ts @@ -16,13 +16,8 @@ export const commands = { * @req: UR-005 - Control media playback (play operation) * @req: DR-009 - Audio player UI */ -async playerPlayItem(item: PlayItemRequest) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("player_play_item", { item }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playerPlayItem(item: PlayItemRequest) : Promise { + return await TAURI_INVOKE("player_play_item", { item }); }, /** * Play a queue of media items @@ -32,91 +27,41 @@ async playerPlayItem(item: PlayItemRequest) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("player_play_queue", { request }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playerPlayQueue(request: PlayQueueRequest) : Promise { + return await TAURI_INVOKE("player_play_queue", { request }); }, /** * Play a track from an album - backend fetches all album tracks and builds queue */ -async playerPlayAlbumTrack(repositoryHandle: string, request: PlayAlbumTrackRequest) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("player_play_album_track", { repositoryHandle, request }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playerPlayAlbumTrack(repositoryHandle: string, request: PlayAlbumTrackRequest) : Promise { + return await TAURI_INVOKE("player_play_album_track", { repositoryHandle, request }); }, /** * Play tracks by ID - backend fetches all metadata */ -async playerPlayTracks(repositoryHandle: string, request: PlayTracksRequest) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("player_play_tracks", { repositoryHandle, request }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playerPlayTracks(repositoryHandle: string, request: PlayTracksRequest) : Promise { + return await TAURI_INVOKE("player_play_tracks", { repositoryHandle, request }); }, -async playerPlay() : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("player_play") }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playerPlay() : Promise { + return await TAURI_INVOKE("player_play"); }, -async playerPause() : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("player_pause") }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playerPause() : Promise { + return await TAURI_INVOKE("player_pause"); }, -async playerToggle() : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("player_toggle") }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playerToggle() : Promise { + return await TAURI_INVOKE("player_toggle"); }, -async playerStop() : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("player_stop") }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playerStop() : Promise { + return await TAURI_INVOKE("player_stop"); }, -async playerNext() : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("player_next") }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playerNext() : Promise { + return await TAURI_INVOKE("player_next"); }, -async playerPrevious() : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("player_previous") }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playerPrevious() : Promise { + return await TAURI_INVOKE("player_previous"); }, -async playerSeek(position: number) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("player_seek", { position }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playerSeek(position: number) : Promise { + return await TAURI_INVOKE("player_seek", { position }); }, /** * Smart video seeking that decides between native and server-side seeking @@ -130,252 +75,117 @@ async playerSeek(position: number) : Promise> { * For native (non-HTML5) backends, this command handles the entire stream reload * internally. For HTML5 backends, it returns the new URL for the frontend to handle. */ -async playerSeekVideo(repositoryHandle: string, position: number, mediaSourceId: string | null, audioStreamIndex: number | null, useHtml5: boolean) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("player_seek_video", { repositoryHandle, position, mediaSourceId, audioStreamIndex, useHtml5 }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playerSeekVideo(repositoryHandle: string, position: number, mediaSourceId: string | null, audioStreamIndex: number | null, useHtml5: boolean) : Promise { + return await TAURI_INVOKE("player_seek_video", { repositoryHandle, position, mediaSourceId, audioStreamIndex, useHtml5 }); }, -async playerSetVolume(volume: number) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("player_set_volume", { volume }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playerSetVolume(volume: number) : Promise { + return await TAURI_INVOKE("player_set_volume", { volume }); }, -async playerToggleMute() : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("player_toggle_mute") }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playerToggleMute() : Promise { + return await TAURI_INVOKE("player_toggle_mute"); }, -async playerSetAudioTrack(streamIndex: number) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("player_set_audio_track", { streamIndex }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playerSetAudioTrack(streamIndex: number) : Promise { + return await TAURI_INVOKE("player_set_audio_track", { streamIndex }); }, /** * Switch audio track - handles both HTML5 (stream reload) and native (direct switch) * Note: Frontend should handle saving series preferences after this command succeeds */ -async playerSwitchAudioTrack(repositoryHandle: string, streamIndex: number, arrayIndex: number, useHtml5: boolean, currentPosition: number | null, mediaSourceId: string | null) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("player_switch_audio_track", { repositoryHandle, streamIndex, arrayIndex, useHtml5, currentPosition, mediaSourceId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playerSwitchAudioTrack(repositoryHandle: string, streamIndex: number, arrayIndex: number, useHtml5: boolean, currentPosition: number | null, mediaSourceId: string | null) : Promise { + return await TAURI_INVOKE("player_switch_audio_track", { repositoryHandle, streamIndex, arrayIndex, useHtml5, currentPosition, mediaSourceId }); }, -async playerSetSubtitleTrack(streamIndex: number | null) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("player_set_subtitle_track", { streamIndex }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playerSetSubtitleTrack(streamIndex: number | null) : Promise { + return await TAURI_INVOKE("player_set_subtitle_track", { streamIndex }); }, -async playerToggleShuffle() : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("player_toggle_shuffle") }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playerToggleShuffle() : Promise { + return await TAURI_INVOKE("player_toggle_shuffle"); }, -async playerCycleRepeat() : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("player_cycle_repeat") }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playerCycleRepeat() : Promise { + return await TAURI_INVOKE("player_cycle_repeat"); }, -async playerGetStatus() : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("player_get_status") }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playerGetStatus() : Promise { + return await TAURI_INVOKE("player_get_status"); }, -async playerGetQueue() : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("player_get_queue") }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playerGetQueue() : Promise { + return await TAURI_INVOKE("player_get_queue"); }, -async playerAddToQueue(request: AddToQueueRequest) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("player_add_to_queue", { request }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playerAddToQueue(request: AddToQueueRequest) : Promise { + return await TAURI_INVOKE("player_add_to_queue", { request }); }, /** * Add a track to queue by ID - backend fetches metadata and constructs URLs */ -async playerAddTrackById(repositoryHandle: string, request: AddTrackByIdRequest) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("player_add_track_by_id", { repositoryHandle, request }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playerAddTrackById(repositoryHandle: string, request: AddTrackByIdRequest) : Promise { + return await TAURI_INVOKE("player_add_track_by_id", { repositoryHandle, request }); }, /** * Add multiple tracks to queue by IDs - backend fetches metadata and constructs URLs */ -async playerAddTracksByIds(repositoryHandle: string, request: AddTracksByIdsRequest) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("player_add_tracks_by_ids", { repositoryHandle, request }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playerAddTracksByIds(repositoryHandle: string, request: AddTracksByIdsRequest) : Promise { + return await TAURI_INVOKE("player_add_tracks_by_ids", { repositoryHandle, request }); }, -async playerRemoveFromQueue(index: number) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("player_remove_from_queue", { index }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playerRemoveFromQueue(index: number) : Promise { + return await TAURI_INVOKE("player_remove_from_queue", { index }); }, -async playerMoveInQueue(fromIndex: number, toIndex: number) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("player_move_in_queue", { fromIndex, toIndex }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playerMoveInQueue(fromIndex: number, toIndex: number) : Promise { + return await TAURI_INVOKE("player_move_in_queue", { fromIndex, toIndex }); }, -async playerSkipTo(index: number) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("player_skip_to", { index }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playerSkipTo(index: number) : Promise { + return await TAURI_INVOKE("player_skip_to", { index }); }, -async playerSetAudioSettings(settings: AudioSettings) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("player_set_audio_settings", { settings }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playerSetAudioSettings(settings: AudioSettings) : Promise { + return await TAURI_INVOKE("player_set_audio_settings", { settings }); }, -async playerGetAudioSettings() : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("player_get_audio_settings") }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playerGetAudioSettings() : Promise { + return await TAURI_INVOKE("player_get_audio_settings"); }, -async playerSetVideoSettings(settings: VideoSettings) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("player_set_video_settings", { settings }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playerSetVideoSettings(settings: VideoSettings) : Promise { + return await TAURI_INVOKE("player_set_video_settings", { settings }); }, -async playerGetVideoSettings() : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("player_get_video_settings") }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playerGetVideoSettings() : Promise { + return await TAURI_INVOKE("player_get_video_settings"); }, /** * Set sleep timer mode */ -async playerSetSleepTimer(mode: SleepTimerMode) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("player_set_sleep_timer", { mode }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playerSetSleepTimer(mode: SleepTimerMode) : Promise { + return await TAURI_INVOKE("player_set_sleep_timer", { mode }); }, /** * Cancel sleep timer */ -async playerCancelSleepTimer() : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("player_cancel_sleep_timer") }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playerCancelSleepTimer() : Promise { + return await TAURI_INVOKE("player_cancel_sleep_timer"); }, /** * Get current sleep timer state */ -async playerGetSleepTimer() : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("player_get_sleep_timer") }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playerGetSleepTimer() : Promise { + return await TAURI_INVOKE("player_get_sleep_timer"); }, /** * Get autoplay settings */ -async playerGetAutoplaySettings() : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("player_get_autoplay_settings") }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playerGetAutoplaySettings() : Promise { + return await TAURI_INVOKE("player_get_autoplay_settings"); }, /** * Set autoplay settings and persist to database */ -async playerSetAutoplaySettings(userId: string, settings: AutoplaySettings) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("player_set_autoplay_settings", { userId, settings }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playerSetAutoplaySettings(userId: string, settings: AutoplaySettings) : Promise { + return await TAURI_INVOKE("player_set_autoplay_settings", { userId, settings }); }, /** * Cancel active autoplay countdown */ -async playerCancelAutoplayCountdown() : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("player_cancel_autoplay_countdown") }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playerCancelAutoplayCountdown() : Promise { + return await TAURI_INVOKE("player_cancel_autoplay_countdown"); }, /** * Play next episode (user confirmed from popup) */ -async playerPlayNextEpisode(item: PlayItemRequest) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("player_play_next_episode", { item }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playerPlayNextEpisode(item: PlayItemRequest) : Promise { + return await TAURI_INVOKE("player_play_next_episode", { item }); }, /** * Handle playback ended event - triggers autoplay decision logic @@ -384,223 +194,123 @@ async playerPlayNextEpisode(item: PlayItemRequest) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("player_on_playback_ended", { itemId, repositoryHandle }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playerOnPlaybackEnded(itemId: string | null, repositoryHandle: string | null) : Promise { + return await TAURI_INVOKE("player_on_playback_ended", { itemId, repositoryHandle }); }, /** * Preload upcoming tracks from the queue * This queues background downloads for the next N tracks that aren't already downloaded */ -async playerPreloadUpcoming(userId: string, downloadBasePath: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("player_preload_upcoming", { userId, downloadBasePath }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playerPreloadUpcoming(userId: string, downloadBasePath: string) : Promise { + return await TAURI_INVOKE("player_preload_upcoming", { userId, downloadBasePath }); }, /** * Update SmartCache configuration */ -async playerSetCacheConfig(config: CacheConfig) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("player_set_cache_config", { config }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playerSetCacheConfig(config: CacheConfig) : Promise { + return await TAURI_INVOKE("player_set_cache_config", { config }); }, /** * Get current SmartCache configuration */ -async playerGetCacheConfig() : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("player_get_cache_config") }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playerGetCacheConfig() : Promise { + return await TAURI_INVOKE("player_get_cache_config"); }, /** * Configure Jellyfin API client for automatic playback reporting */ -async playerConfigureJellyfin(serverUrl: string, accessToken: string, userId: string, deviceId: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("player_configure_jellyfin", { serverUrl, accessToken, userId, deviceId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playerConfigureJellyfin(serverUrl: string, accessToken: string, userId: string, deviceId: string) : Promise { + return await TAURI_INVOKE("player_configure_jellyfin", { serverUrl, accessToken, userId, deviceId }); }, /** * Disable Jellyfin automatic playback reporting */ -async playerDisableJellyfin() : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("player_disable_jellyfin") }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playerDisableJellyfin() : Promise { + return await TAURI_INVOKE("player_disable_jellyfin"); }, /** * Get the current media session state */ -async playerGetSession() : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("player_get_session") }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playerGetSession() : Promise { + return await TAURI_INVOKE("player_get_session"); }, /** * Dismiss the current media session (returns to Idle) */ -async playerDismissSession() : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("player_dismiss_session") }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playerDismissSession() : Promise { + return await TAURI_INVOKE("player_dismiss_session"); }, /** * Play items on a remote Jellyfin session (casting) */ -async remotePlayOnSession(sessionId: string, itemIds: string[], startIndex: number) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("remote_play_on_session", { sessionId, itemIds, startIndex }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async remotePlayOnSession(sessionId: string, itemIds: string[], startIndex: number) : Promise { + return await TAURI_INVOKE("remote_play_on_session", { sessionId, itemIds, startIndex }); }, /** * Send a playback command to a remote session */ -async remoteSendCommand(sessionId: string, command: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("remote_send_command", { sessionId, command }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async remoteSendCommand(sessionId: string, command: string) : Promise { + return await TAURI_INVOKE("remote_send_command", { sessionId, command }); }, /** * Seek on a remote session */ -async remoteSessionSeek(sessionId: string, positionTicks: number) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("remote_session_seek", { sessionId, positionTicks }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async remoteSessionSeek(sessionId: string, positionTicks: number) : Promise { + return await TAURI_INVOKE("remote_session_seek", { sessionId, positionTicks }); }, /** * Set volume on a remote session */ -async remoteSessionSetVolume(sessionId: string, volume: number) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("remote_session_set_volume", { sessionId, volume }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async remoteSessionSetVolume(sessionId: string, volume: number) : Promise { + return await TAURI_INVOKE("remote_session_set_volume", { sessionId, volume }); }, /** * Toggle mute on a remote session */ -async remoteSessionToggleMute(sessionId: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("remote_session_toggle_mute", { sessionId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async remoteSessionToggleMute(sessionId: string) : Promise { + return await TAURI_INVOKE("remote_session_toggle_mute", { sessionId }); }, /** * Set polling frequency hint based on UI state */ -async sessionsSetPollingHint(hint: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("sessions_set_polling_hint", { hint }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async sessionsSetPollingHint(hint: string) : Promise { + return await TAURI_INVOKE("sessions_set_polling_hint", { hint }); }, /** * Manually trigger a session poll (for refresh button) */ -async sessionsPollNow() : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("sessions_poll_now") }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async sessionsPollNow() : Promise { + return await TAURI_INVOKE("sessions_poll_now"); }, /** * Get the current playback mode */ -async playbackModeGetCurrent() : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("playback_mode_get_current") }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playbackModeGetCurrent() : Promise { + return await TAURI_INVOKE("playback_mode_get_current"); }, /** * Set the playback mode (internal/testing use) */ -async playbackModeSet(mode: PlaybackMode) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("playback_mode_set", { mode }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playbackModeSet(mode: PlaybackMode) : Promise { + return await TAURI_INVOKE("playback_mode_set", { mode }); }, /** * Check if currently transferring between playback modes */ -async playbackModeIsTransferring() : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("playback_mode_is_transferring") }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playbackModeIsTransferring() : Promise { + return await TAURI_INVOKE("playback_mode_is_transferring"); }, /** * Transfer playback from local device to a remote Jellyfin session */ -async playbackModeTransferToRemote(sessionId: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("playback_mode_transfer_to_remote", { sessionId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playbackModeTransferToRemote(sessionId: string) : Promise { + return await TAURI_INVOKE("playback_mode_transfer_to_remote", { sessionId }); }, /** * Get remote session status (for polling position/duration) */ -async playbackModeGetRemoteStatus() : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("playback_mode_get_remote_status") }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playbackModeGetRemoteStatus() : Promise { + return await TAURI_INVOKE("playback_mode_get_remote_status"); }, /** * Transfer playback from remote session back to local device @@ -609,190 +319,105 @@ async playbackModeGetRemoteStatus() : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("playback_mode_transfer_to_local", { currentItemId, positionTicks }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playbackModeTransferToLocal(currentItemId: string, positionTicks: number) : Promise { + return await TAURI_INVOKE("playback_mode_transfer_to_local", { currentItemId, positionTicks }); }, /** * Initialize playback reporter (called after login) */ -async playbackReporterInit(serverUrl: string, userId: string, accessToken: string, deviceId: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("playback_reporter_init", { serverUrl, userId, accessToken, deviceId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playbackReporterInit(serverUrl: string, userId: string, accessToken: string, deviceId: string) : Promise { + return await TAURI_INVOKE("playback_reporter_init", { serverUrl, userId, accessToken, deviceId }); }, /** * Destroy playback reporter (called on logout) */ -async playbackReporterDestroy() : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("playback_reporter_destroy") }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playbackReporterDestroy() : Promise { + return await TAURI_INVOKE("playback_reporter_destroy"); }, /** * Report playback start */ -async playbackReportStart(itemId: string, positionSeconds: number, contextType: string | null, contextId: string | null) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("playback_report_start", { itemId, positionSeconds, contextType, contextId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playbackReportStart(itemId: string, positionSeconds: number, contextType: string | null, contextId: string | null) : Promise { + return await TAURI_INVOKE("playback_report_start", { itemId, positionSeconds, contextType, contextId }); }, /** * Report playback progress */ -async playbackReportProgress(itemId: string, positionSeconds: number, isPaused: boolean) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("playback_report_progress", { itemId, positionSeconds, isPaused }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playbackReportProgress(itemId: string, positionSeconds: number, isPaused: boolean) : Promise { + return await TAURI_INVOKE("playback_report_progress", { itemId, positionSeconds, isPaused }); }, /** * Report playback stopped */ -async playbackReportStopped(itemId: string, positionSeconds: number) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("playback_report_stopped", { itemId, positionSeconds }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playbackReportStopped(itemId: string, positionSeconds: number) : Promise { + return await TAURI_INVOKE("playback_report_stopped", { itemId, positionSeconds }); }, /** * Mark item as played */ -async playbackMarkPlayed(itemId: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("playback_mark_played", { itemId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playbackMarkPlayed(itemId: string) : Promise { + return await TAURI_INVOKE("playback_mark_played", { itemId }); }, /** * Initialize the auth manager (call on app startup) * Restores session from storage if available */ -async authInitialize() : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("auth_initialize") }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async authInitialize() : Promise { + return await TAURI_INVOKE("auth_initialize"); }, /** * Connect to a Jellyfin server and get server info */ -async authConnectToServer(serverUrl: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("auth_connect_to_server", { serverUrl }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async authConnectToServer(serverUrl: string) : Promise { + return await TAURI_INVOKE("auth_connect_to_server", { serverUrl }); }, /** * Login with username and password */ -async authLogin(serverUrl: string, username: string, password: string, deviceId: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("auth_login", { serverUrl, username, password, deviceId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async authLogin(serverUrl: string, username: string, password: string, deviceId: string) : Promise { + return await TAURI_INVOKE("auth_login", { serverUrl, username, password, deviceId }); }, /** * Verify current session */ -async authVerifySession(serverUrl: string, userId: string, accessToken: string, deviceId: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("auth_verify_session", { serverUrl, userId, accessToken, deviceId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async authVerifySession(serverUrl: string, userId: string, accessToken: string, deviceId: string) : Promise { + return await TAURI_INVOKE("auth_verify_session", { serverUrl, userId, accessToken, deviceId }); }, /** * Logout (clear session and call Jellyfin logout endpoint) */ -async authLogout(serverUrl: string, accessToken: string, deviceId: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("auth_logout", { serverUrl, accessToken, deviceId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async authLogout(serverUrl: string, accessToken: string, deviceId: string) : Promise { + return await TAURI_INVOKE("auth_logout", { serverUrl, accessToken, deviceId }); }, /** * Get current session */ -async authGetSession() : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("auth_get_session") }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async authGetSession() : Promise { + return await TAURI_INVOKE("auth_get_session"); }, /** * Set current session (for restoration from storage) */ -async authSetSession(session: Session | null) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("auth_set_session", { session }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async authSetSession(session: Session | null) : Promise { + return await TAURI_INVOKE("auth_set_session", { session }); }, /** * Start background session verification */ -async authStartVerification(deviceId: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("auth_start_verification", { deviceId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async authStartVerification(deviceId: string) : Promise { + return await TAURI_INVOKE("auth_start_verification", { deviceId }); }, /** * Stop background session verification */ -async authStopVerification() : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("auth_stop_verification") }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async authStopVerification() : Promise { + return await TAURI_INVOKE("auth_stop_verification"); }, /** * Re-authenticate with password (when session expired) */ -async authReauthenticate(password: string, deviceId: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("auth_reauthenticate", { password, deviceId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async authReauthenticate(password: string, deviceId: string) : Promise { + return await TAURI_INVOKE("auth_reauthenticate", { password, deviceId }); }, /** * Get or create the device ID. @@ -806,13 +431,8 @@ async authReauthenticate(password: string, deviceId: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("device_get_id") }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async deviceGetId() : Promise { + return await TAURI_INVOKE("device_get_id"); }, /** * Set the device ID (primarily for testing or recovery). @@ -827,1311 +447,721 @@ async deviceGetId() : Promise> { * * TRACES: UR-009 | DR-011 */ -async deviceSetId(deviceId: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("device_set_id", { deviceId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async deviceSetId(deviceId: string) : Promise { + return await TAURI_INVOKE("device_set_id", { deviceId }); }, /** * Check if the server is currently reachable */ -async connectivityCheckServer() : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("connectivity_check_server") }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async connectivityCheckServer() : Promise { + return await TAURI_INVOKE("connectivity_check_server"); }, /** * Set the server URL and trigger an immediate check */ -async connectivitySetServerUrl(url: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("connectivity_set_server_url", { url }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async connectivitySetServerUrl(url: string) : Promise { + return await TAURI_INVOKE("connectivity_set_server_url", { url }); }, /** * Get the current connectivity status */ -async connectivityGetStatus() : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("connectivity_get_status") }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async connectivityGetStatus() : Promise { + return await TAURI_INVOKE("connectivity_get_status"); }, /** * Start monitoring connectivity with adaptive polling */ -async connectivityStartMonitoring() : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("connectivity_start_monitoring") }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async connectivityStartMonitoring() : Promise { + return await TAURI_INVOKE("connectivity_start_monitoring"); }, /** * Stop monitoring connectivity */ -async connectivityStopMonitoring() : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("connectivity_stop_monitoring") }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async connectivityStopMonitoring() : Promise { + return await TAURI_INVOKE("connectivity_stop_monitoring"); }, /** * Mark the server as reachable (called after successful API calls) */ -async connectivityMarkReachable() : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("connectivity_mark_reachable") }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async connectivityMarkReachable() : Promise { + return await TAURI_INVOKE("connectivity_mark_reachable"); }, /** * Mark the server as unreachable (called after failed API calls) */ -async connectivityMarkUnreachable(error: string | null) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("connectivity_mark_unreachable", { error }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async connectivityMarkUnreachable(error: string | null) : Promise { + return await TAURI_INVOKE("connectivity_mark_unreachable", { error }); }, /** * Initialize the database and run migrations */ -async storageInit() : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("storage_init") }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async storageInit() : Promise { + return await TAURI_INVOKE("storage_init"); }, /** * Get storage directory path (parent directory of the database file) */ -async storageGetPath() : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("storage_get_path") }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async storageGetPath() : Promise { + return await TAURI_INVOKE("storage_get_path"); }, /** * Get database file size in bytes */ -async storageGetSize() : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("storage_get_size") }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async storageGetSize() : Promise { + return await TAURI_INVOKE("storage_get_size"); }, /** * Get security status (keyring vs encrypted file fallback) */ -async storageGetSecurityStatus() : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("storage_get_security_status") }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async storageGetSecurityStatus() : Promise { + return await TAURI_INVOKE("storage_get_security_status"); }, /** * Save a server connection * Uses INSERT ... ON CONFLICT to avoid triggering CASCADE DELETE on users */ -async storageSaveServer(id: string, name: string, url: string, version: string | null) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("storage_save_server", { id, name, url, version }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async storageSaveServer(id: string, name: string, url: string, version: string | null) : Promise { + return await TAURI_INVOKE("storage_save_server", { id, name, url, version }); }, /** * Get all saved servers */ -async storageGetServers() : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("storage_get_servers") }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async storageGetServers() : Promise { + return await TAURI_INVOKE("storage_get_servers"); }, /** * Delete a server and all associated data */ -async storageDeleteServer(serverId: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("storage_delete_server", { serverId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async storageDeleteServer(serverId: string) : Promise { + return await TAURI_INVOKE("storage_delete_server", { serverId }); }, /** * Save a user account (token stored in secure storage, not database) */ -async storageSaveUser(id: string, serverId: string, username: string, accessToken: string | null) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("storage_save_user", { id, serverId, username, accessToken }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async storageSaveUser(id: string, serverId: string, username: string, accessToken: string | null) : Promise { + return await TAURI_INVOKE("storage_save_user", { id, serverId, username, accessToken }); }, /** * Get users for a server */ -async storageGetUsers(serverId: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("storage_get_users", { serverId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async storageGetUsers(serverId: string) : Promise { + return await TAURI_INVOKE("storage_get_users", { serverId }); }, /** * Set a user as active (and deactivate all other users globally) */ -async storageSetActiveUser(userId: string, serverId: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("storage_set_active_user", { userId, serverId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async storageSetActiveUser(userId: string, serverId: string) : Promise { + return await TAURI_INVOKE("storage_set_active_user", { userId, serverId }); }, /** * Get the active user for a server */ -async storageGetActiveUser(serverId: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("storage_get_active_user", { serverId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async storageGetActiveUser(serverId: string) : Promise { + return await TAURI_INVOKE("storage_get_active_user", { serverId }); }, /** * Get the active session (user + server + token) for session restoration */ -async storageGetActiveSession() : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("storage_get_active_session") }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async storageGetActiveSession() : Promise { + return await TAURI_INVOKE("storage_get_active_session"); }, /** * Get user's access token from secure storage */ -async storageGetAccessToken(userId: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("storage_get_access_token", { userId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async storageGetAccessToken(userId: string) : Promise { + return await TAURI_INVOKE("storage_get_access_token", { userId }); }, /** * Delete a user account and their token from secure storage */ -async storageDeleteUser(userId: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("storage_delete_user", { userId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async storageDeleteUser(userId: string) : Promise { + return await TAURI_INVOKE("storage_delete_user", { userId }); }, /** * Update playback progress in local database * This stores the progress locally for offline access and "continue watching" */ -async storageUpdatePlaybackProgress(userId: string, itemId: string, positionTicks: number) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("storage_update_playback_progress", { userId, itemId, positionTicks }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async storageUpdatePlaybackProgress(userId: string, itemId: string, positionTicks: number) : Promise { + return await TAURI_INVOKE("storage_update_playback_progress", { userId, itemId, positionTicks }); }, /** * Update playback progress with context in local database * This stores the progress along with playback context (container vs single) */ -async storageUpdatePlaybackContext(userId: string, itemId: string, positionTicks: number, contextType: string | null, contextId: string | null) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("storage_update_playback_context", { userId, itemId, positionTicks, contextType, contextId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async storageUpdatePlaybackContext(userId: string, itemId: string, positionTicks: number, contextType: string | null, contextId: string | null) : Promise { + return await TAURI_INVOKE("storage_update_playback_context", { userId, itemId, positionTicks, contextType, contextId }); }, /** * Mark item as played in local database */ -async storageMarkPlayed(userId: string, itemId: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("storage_mark_played", { userId, itemId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async storageMarkPlayed(userId: string, itemId: string) : Promise { + return await TAURI_INVOKE("storage_mark_played", { userId, itemId }); }, /** * Get playback progress for an item */ -async storageGetPlaybackProgress(userId: string, itemId: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("storage_get_playback_progress", { userId, itemId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async storageGetPlaybackProgress(userId: string, itemId: string) : Promise { + return await TAURI_INVOKE("storage_get_playback_progress", { userId, itemId }); }, /** * Mark pending sync as completed for an item */ -async storageMarkSynced(userId: string, itemId: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("storage_mark_synced", { userId, itemId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async storageMarkSynced(userId: string, itemId: string) : Promise { + return await TAURI_INVOKE("storage_mark_synced", { userId, itemId }); }, /** * Toggle favorite status for an item in local database * This updates the is_favorite field and marks it for sync to Jellyfin */ -async storageToggleFavorite(userId: string, itemId: string, isFavorite: boolean) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("storage_toggle_favorite", { userId, itemId, isFavorite }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async storageToggleFavorite(userId: string, itemId: string, isFavorite: boolean) : Promise { + return await TAURI_INVOKE("storage_toggle_favorite", { userId, itemId, isFavorite }); }, /** * Queue a media item for download */ -async downloadItem(request: DownloadItemRequest) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("download_item", { request }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async downloadItem(request: DownloadItemRequest) : Promise { + return await TAURI_INVOKE("download_item", { request }); }, /** * Queue and start a download in a single atomic operation * This simplifies the frontend flow by combining multiple steps */ -async downloadItemAndStart(request: DownloadItemAndStartRequest) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("download_item_and_start", { request }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async downloadItemAndStart(request: DownloadItemAndStartRequest) : Promise { + return await TAURI_INVOKE("download_item_and_start", { request }); }, /** * Queue an entire album for download */ -async downloadAlbum(albumId: string, userId: string, basePath: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("download_album", { albumId, userId, basePath }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async downloadAlbum(albumId: string, userId: string, basePath: string) : Promise { + return await TAURI_INVOKE("download_album", { albumId, userId, basePath }); }, /** * Queue a video item (movie or episode) for download with quality preset */ -async downloadVideo(request: DownloadVideoRequest) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("download_video", { request }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async downloadVideo(request: DownloadVideoRequest) : Promise { + return await TAURI_INVOKE("download_video", { request }); }, /** * Queue all episodes of a series for download */ -async downloadSeries(seriesId: string, seriesName: string, userId: string, basePath: string, qualityPreset: string | null) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("download_series", { seriesId, seriesName, userId, basePath, qualityPreset }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async downloadSeries(seriesId: string, seriesName: string, userId: string, basePath: string, qualityPreset: string | null) : Promise { + return await TAURI_INVOKE("download_series", { seriesId, seriesName, userId, basePath, qualityPreset }); }, /** * Queue all episodes of a specific season for download */ -async downloadSeason(seasonId: string, seriesName: string, seasonName: string, seasonNumber: number, userId: string, basePath: string, qualityPreset: string | null) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("download_season", { seasonId, seriesName, seasonName, seasonNumber, userId, basePath, qualityPreset }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async downloadSeason(seasonId: string, seriesName: string, seasonName: string, seasonNumber: number, userId: string, basePath: string, qualityPreset: string | null) : Promise { + return await TAURI_INVOKE("download_season", { seasonId, seriesName, seasonName, seasonNumber, userId, basePath, qualityPreset }); }, /** * Get all downloads for a user, optionally filtered by status */ -async getDownloads(userId: string, statusFilter: string[] | null) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("get_downloads", { userId, statusFilter }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async getDownloads(userId: string, statusFilter: string[] | null) : Promise { + return await TAURI_INVOKE("get_downloads", { userId, statusFilter }); }, /** * Pause a download */ -async pauseDownload(downloadId: number) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("pause_download", { downloadId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async pauseDownload(downloadId: number) : Promise { + return await TAURI_INVOKE("pause_download", { downloadId }); }, /** * Resume a paused download */ -async resumeDownload(downloadId: number) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("resume_download", { downloadId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async resumeDownload(downloadId: number) : Promise { + return await TAURI_INVOKE("resume_download", { downloadId }); }, /** * Cancel a download */ -async cancelDownload(downloadId: number) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("cancel_download", { downloadId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async cancelDownload(downloadId: number) : Promise { + return await TAURI_INVOKE("cancel_download", { downloadId }); }, /** * Delete a completed download */ -async deleteDownload(downloadId: number) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("delete_download", { downloadId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async deleteDownload(downloadId: number) : Promise { + return await TAURI_INVOKE("delete_download", { downloadId }); }, /** * Delete all downloads for a user */ -async deleteAllDownloads(userId: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("delete_all_downloads", { userId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async deleteAllDownloads(userId: string) : Promise { + return await TAURI_INVOKE("delete_all_downloads", { userId }); }, /** * Delete all downloads for a specific album */ -async deleteAlbumDownloads(albumId: string, userId: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("delete_album_downloads", { albumId, userId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async deleteAlbumDownloads(albumId: string, userId: string) : Promise { + return await TAURI_INVOKE("delete_album_downloads", { albumId, userId }); }, /** * Clear all stale pending/failed/paused downloads */ -async clearStaleDownloads(userId: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("clear_stale_downloads", { userId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async clearStaleDownloads(userId: string) : Promise { + return await TAURI_INVOKE("clear_stale_downloads", { userId }); }, /** * Get storage statistics for downloads */ -async getDownloadStorageStats(userId: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("get_download_storage_stats", { userId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async getDownloadStorageStats(userId: string) : Promise { + return await TAURI_INVOKE("get_download_storage_stats", { userId }); }, /** * Mark a download as completed */ -async markDownloadCompleted(downloadId: number, bytesDownloaded: number, filePath: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("mark_download_completed", { downloadId, bytesDownloaded, filePath }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async markDownloadCompleted(downloadId: number, bytesDownloaded: number, filePath: string) : Promise { + return await TAURI_INVOKE("mark_download_completed", { downloadId, bytesDownloaded, filePath }); }, /** * Mark a download as failed */ -async markDownloadFailed(downloadId: number, errorMessage: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("mark_download_failed", { downloadId, errorMessage }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async markDownloadFailed(downloadId: number, errorMessage: string) : Promise { + return await TAURI_INVOKE("mark_download_failed", { downloadId, errorMessage }); }, /** * Start downloading a file immediately * This command actually downloads the file using the worker */ -async startDownload(downloadId: number, streamUrl: string, targetDir: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("start_download", { downloadId, streamUrl, targetDir }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async startDownload(downloadId: number, streamUrl: string, targetDir: string) : Promise { + return await TAURI_INVOKE("start_download", { downloadId, streamUrl, targetDir }); }, /** * Get download manager statistics */ -async getDownloadManagerStats() : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("get_download_manager_stats") }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async getDownloadManagerStats() : Promise { + return await TAURI_INVOKE("get_download_manager_stats"); }, /** * Set the maximum concurrent downloads */ -async setMaxConcurrentDownloads(max: number) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("set_max_concurrent_downloads", { max }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async setMaxConcurrentDownloads(max: number) : Promise { + return await TAURI_INVOKE("set_max_concurrent_downloads", { max }); }, /** * Get SmartCache statistics */ -async getSmartCacheStats(userId: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("get_smart_cache_stats", { userId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async getSmartCacheStats(userId: string) : Promise { + return await TAURI_INVOKE("get_smart_cache_stats", { userId }); }, /** * Update SmartCache configuration */ -async updateSmartCacheConfig(config: CacheConfig) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("update_smart_cache_config", { config }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async updateSmartCacheConfig(config: CacheConfig) : Promise { + return await TAURI_INVOKE("update_smart_cache_config", { config }); }, /** * Get SmartCache configuration */ -async getSmartCacheConfig() : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("get_smart_cache_config") }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async getSmartCacheConfig() : Promise { + return await TAURI_INVOKE("get_smart_cache_config"); }, /** * Get album recommendations based on play history */ -async getAlbumRecommendations(userId: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("get_album_recommendations", { userId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async getAlbumRecommendations(userId: string) : Promise { + return await TAURI_INVOKE("get_album_recommendations", { userId }); }, /** * Get album affinity status for all tracked albums * This shows the SmartCache's internal play history and threshold status */ -async getAlbumAffinityStatus() : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("get_album_affinity_status") }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async getAlbumAffinityStatus() : Promise { + return await TAURI_INVOKE("get_album_affinity_status"); }, /** * Pin an item's metadata (protects from cache clear) */ -async pinItem(itemId: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("pin_item", { itemId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async pinItem(itemId: string) : Promise { + return await TAURI_INVOKE("pin_item", { itemId }); }, /** * Unpin an item's metadata */ -async unpinItem(itemId: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("unpin_item", { itemId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async unpinItem(itemId: string) : Promise { + return await TAURI_INVOKE("unpin_item", { itemId }); }, /** * Check if an item is pinned */ -async isItemPinned(itemId: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("is_item_pinned", { itemId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async isItemPinned(itemId: string) : Promise { + return await TAURI_INVOKE("is_item_pinned", { itemId }); }, /** * Check if an item is available offline */ -async offlineIsAvailable(itemId: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("offline_is_available", { itemId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async offlineIsAvailable(itemId: string) : Promise { + return await TAURI_INVOKE("offline_is_available", { itemId }); }, /** * Get all offline items for a user */ -async offlineGetItems(userId: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("offline_get_items", { userId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async offlineGetItems(userId: string) : Promise { + return await TAURI_INVOKE("offline_get_items", { userId }); }, /** * Search offline items */ -async offlineSearch(userId: string, query: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("offline_search", { userId, query }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async offlineSearch(userId: string, query: string) : Promise { + return await TAURI_INVOKE("offline_search", { userId, query }); }, /** * Get cached libraries for a server */ -async storageGetLibraries(serverId: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("storage_get_libraries", { serverId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async storageGetLibraries(serverId: string) : Promise { + return await TAURI_INVOKE("storage_get_libraries", { serverId }); }, /** * Get cached items with optional filtering */ -async storageGetItems(serverId: string, parentId: string | null, libraryId: string | null, itemType: string | null, limit: number | null, offset: number | null) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("storage_get_items", { serverId, parentId, libraryId, itemType, limit, offset }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async storageGetItems(serverId: string, parentId: string | null, libraryId: string | null, itemType: string | null, limit: number | null, offset: number | null) : Promise { + return await TAURI_INVOKE("storage_get_items", { serverId, parentId, libraryId, itemType, limit, offset }); }, /** * Get a single cached item by ID */ -async storageGetItem(itemId: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("storage_get_item", { itemId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async storageGetItem(itemId: string) : Promise { + return await TAURI_INVOKE("storage_get_item", { itemId }); }, /** * Search cached items using FTS */ -async storageSearchItems(serverId: string, query: string, limit: number | null) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("storage_search_items", { serverId, query, limit }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async storageSearchItems(serverId: string, query: string, limit: number | null) : Promise { + return await TAURI_INVOKE("storage_search_items", { serverId, query, limit }); }, /** * Save a library to the cache */ -async storageSaveLibrary(id: string, serverId: string, name: string, collectionType: string | null, imageTag: string | null, sortOrder: number | null) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("storage_save_library", { id, serverId, name, collectionType, imageTag, sortOrder }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async storageSaveLibrary(id: string, serverId: string, name: string, collectionType: string | null, imageTag: string | null, sortOrder: number | null) : Promise { + return await TAURI_INVOKE("storage_save_library", { id, serverId, name, collectionType, imageTag, sortOrder }); }, /** * Save an item to the cache */ -async storageSaveItem(item: CachedItem, serverId: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("storage_save_item", { item, serverId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async storageSaveItem(item: CachedItem, serverId: string) : Promise { + return await TAURI_INVOKE("storage_save_item", { item, serverId }); }, /** * Get count of pending sync operations for a user */ -async storageGetPendingSyncCount(userId: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("storage_get_pending_sync_count", { userId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async storageGetPendingSyncCount(userId: string) : Promise { + return await TAURI_INVOKE("storage_get_pending_sync_count", { userId }); }, /** * Queue a mutation for sync to server */ -async syncQueueMutation(userId: string, operation: string, itemId: string | null, payload: string | null) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("sync_queue_mutation", { userId, operation, itemId, payload }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async syncQueueMutation(userId: string, operation: string, itemId: string | null, payload: string | null) : Promise { + return await TAURI_INVOKE("sync_queue_mutation", { userId, operation, itemId, payload }); }, /** * Get all pending sync operations for a user */ -async syncGetPending(userId: string, limit: number | null) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("sync_get_pending", { userId, limit }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async syncGetPending(userId: string, limit: number | null) : Promise { + return await TAURI_INVOKE("sync_get_pending", { userId, limit }); }, /** * Mark a sync operation as in progress */ -async syncMarkProcessing(id: number) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("sync_mark_processing", { id }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async syncMarkProcessing(id: number) : Promise { + return await TAURI_INVOKE("sync_mark_processing", { id }); }, /** * Mark a sync operation as completed */ -async syncMarkCompleted(id: number) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("sync_mark_completed", { id }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async syncMarkCompleted(id: number) : Promise { + return await TAURI_INVOKE("sync_mark_completed", { id }); }, /** * Mark a sync operation as failed with error message */ -async syncMarkFailed(id: number, error: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("sync_mark_failed", { id, error }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async syncMarkFailed(id: number, error: string) : Promise { + return await TAURI_INVOKE("sync_mark_failed", { id, error }); }, /** * Get count of pending sync operations for a user */ -async syncGetPendingCount(userId: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("sync_get_pending_count", { userId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async syncGetPendingCount(userId: string) : Promise { + return await TAURI_INVOKE("sync_get_pending_count", { userId }); }, /** * Delete completed sync operations older than specified days */ -async syncCleanupCompleted(daysOld: number) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("sync_cleanup_completed", { daysOld }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async syncCleanupCompleted(daysOld: number) : Promise { + return await TAURI_INVOKE("sync_cleanup_completed", { daysOld }); }, /** * Delete all sync operations for a user (used during logout) */ -async syncClearUser(userId: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("sync_clear_user", { userId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async syncClearUser(userId: string) : Promise { + return await TAURI_INVOKE("sync_clear_user", { userId }); }, /** * Get cached thumbnail path, returns None if not cached * Also updates last_accessed timestamp for LRU tracking */ -async thumbnailGetCached(itemId: string, imageType: string, tag: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("thumbnail_get_cached", { itemId, imageType, tag }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async thumbnailGetCached(itemId: string, imageType: string, tag: string) : Promise { + return await TAURI_INVOKE("thumbnail_get_cached", { itemId, imageType, tag }); }, /** * Download and save a thumbnail to cache * Returns the local file path on success */ -async thumbnailSave(itemId: string, imageType: string, tag: string, url: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("thumbnail_save", { itemId, imageType, tag, url }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async thumbnailSave(itemId: string, imageType: string, tag: string, url: string) : Promise { + return await TAURI_INVOKE("thumbnail_save", { itemId, imageType, tag, url }); }, /** * Get thumbnail cache statistics */ -async thumbnailGetStats() : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("thumbnail_get_stats") }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async thumbnailGetStats() : Promise { + return await TAURI_INVOKE("thumbnail_get_stats"); }, /** * Set thumbnail cache storage limit in bytes */ -async thumbnailSetLimit(limitBytes: number) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("thumbnail_set_limit", { limitBytes }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async thumbnailSetLimit(limitBytes: number) : Promise { + return await TAURI_INVOKE("thumbnail_set_limit", { limitBytes }); }, /** * Clear all cached thumbnails */ -async thumbnailClearCache() : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("thumbnail_clear_cache") }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async thumbnailClearCache() : Promise { + return await TAURI_INVOKE("thumbnail_clear_cache"); }, /** * Delete cached thumbnails for a specific item */ -async thumbnailDeleteItem(itemId: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("thumbnail_delete_item", { itemId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async thumbnailDeleteItem(itemId: string) : Promise { + return await TAURI_INVOKE("thumbnail_delete_item", { itemId }); }, /** * Get image as base64 data URL, caching if not already cached * This extends the thumbnail system to serve all images through Rust with automatic caching */ -async imageGetUrl(repositoryHandle: string, request: GetImageRequest) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("image_get_url", { repositoryHandle, request }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async imageGetUrl(repositoryHandle: string, request: GetImageRequest) : Promise { + return await TAURI_INVOKE("image_get_url", { repositoryHandle, request }); }, /** * Save a person to the cache */ -async storageSavePerson(person: CachedPerson) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("storage_save_person", { person }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async storageSavePerson(person: CachedPerson) : Promise { + return await TAURI_INVOKE("storage_save_person", { person }); }, /** * Get a cached person by ID */ -async storageGetPerson(personId: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("storage_get_person", { personId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async storageGetPerson(personId: string) : Promise { + return await TAURI_INVOKE("storage_get_person", { personId }); }, /** * Save item-person associations (batch) */ -async storageSaveItemPeople(associations: CachedItemPerson[]) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("storage_save_item_people", { associations }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async storageSaveItemPeople(associations: CachedItemPerson[]) : Promise { + return await TAURI_INVOKE("storage_save_item_people", { associations }); }, /** * Get people for an item (with person details joined) */ -async storageGetItemPeople(itemId: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("storage_get_item_people", { itemId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async storageGetItemPeople(itemId: string) : Promise { + return await TAURI_INVOKE("storage_get_item_people", { itemId }); }, /** * Save user's preferred audio track for a series */ -async storageSaveSeriesAudioPreference(userId: string, seriesId: string, serverId: string, audioTrackDisplayTitle: string | null, audioTrackLanguage: string | null, audioTrackIndex: number | null) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("storage_save_series_audio_preference", { userId, seriesId, serverId, audioTrackDisplayTitle, audioTrackLanguage, audioTrackIndex }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async storageSaveSeriesAudioPreference(userId: string, seriesId: string, serverId: string, audioTrackDisplayTitle: string | null, audioTrackLanguage: string | null, audioTrackIndex: number | null) : Promise { + return await TAURI_INVOKE("storage_save_series_audio_preference", { userId, seriesId, serverId, audioTrackDisplayTitle, audioTrackLanguage, audioTrackIndex }); }, /** * Get user's preferred audio track for a series */ -async storageGetSeriesAudioPreference(userId: string, seriesId: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("storage_get_series_audio_preference", { userId, seriesId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async storageGetSeriesAudioPreference(userId: string, seriesId: string) : Promise { + return await TAURI_INVOKE("storage_get_series_audio_preference", { userId, seriesId }); }, /** * Create a new repository instance * Returns a handle (UUID) for accessing the repository */ -async repositoryCreate(serverUrl: string, userId: string, accessToken: string, serverId: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("repository_create", { serverUrl, userId, accessToken, serverId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async repositoryCreate(serverUrl: string, userId: string, accessToken: string, serverId: string) : Promise { + return await TAURI_INVOKE("repository_create", { serverUrl, userId, accessToken, serverId }); }, /** * Destroy a repository instance */ -async repositoryDestroy(handle: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("repository_destroy", { handle }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async repositoryDestroy(handle: string) : Promise { + return await TAURI_INVOKE("repository_destroy", { handle }); }, /** * Get libraries */ -async repositoryGetLibraries(handle: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("repository_get_libraries", { handle }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async repositoryGetLibraries(handle: string) : Promise { + return await TAURI_INVOKE("repository_get_libraries", { handle }); }, /** * Get items in a container (library, folder, album, etc.) */ -async repositoryGetItems(handle: string, parentId: string, options: GetItemsOptions | null) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("repository_get_items", { handle, parentId, options }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async repositoryGetItems(handle: string, parentId: string, options: GetItemsOptions | null) : Promise { + return await TAURI_INVOKE("repository_get_items", { handle, parentId, options }); }, /** * Get a single item by ID */ -async repositoryGetItem(handle: string, itemId: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("repository_get_item", { handle, itemId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async repositoryGetItem(handle: string, itemId: string) : Promise { + return await TAURI_INVOKE("repository_get_item", { handle, itemId }); }, /** * Get latest items in a library */ -async repositoryGetLatestItems(handle: string, parentId: string, limit: number | null) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("repository_get_latest_items", { handle, parentId, limit }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async repositoryGetLatestItems(handle: string, parentId: string, limit: number | null) : Promise { + return await TAURI_INVOKE("repository_get_latest_items", { handle, parentId, limit }); }, /** * Get resume items (continue watching/listening) */ -async repositoryGetResumeItems(handle: string, parentId: string | null, limit: number | null) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("repository_get_resume_items", { handle, parentId, limit }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async repositoryGetResumeItems(handle: string, parentId: string | null, limit: number | null) : Promise { + return await TAURI_INVOKE("repository_get_resume_items", { handle, parentId, limit }); }, /** * Get next up episodes */ -async repositoryGetNextUpEpisodes(handle: string, seriesId: string | null, limit: number | null) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("repository_get_next_up_episodes", { handle, seriesId, limit }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async repositoryGetNextUpEpisodes(handle: string, seriesId: string | null, limit: number | null) : Promise { + return await TAURI_INVOKE("repository_get_next_up_episodes", { handle, seriesId, limit }); }, /** * Get recently played audio */ -async repositoryGetRecentlyPlayedAudio(handle: string, limit: number | null) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("repository_get_recently_played_audio", { handle, limit }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async repositoryGetRecentlyPlayedAudio(handle: string, limit: number | null) : Promise { + return await TAURI_INVOKE("repository_get_recently_played_audio", { handle, limit }); }, /** * Get resume movies */ -async repositoryGetResumeMovies(handle: string, limit: number | null) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("repository_get_resume_movies", { handle, limit }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async repositoryGetResumeMovies(handle: string, limit: number | null) : Promise { + return await TAURI_INVOKE("repository_get_resume_movies", { handle, limit }); }, /** * Get genres for a library */ -async repositoryGetGenres(handle: string, parentId: string | null) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("repository_get_genres", { handle, parentId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async repositoryGetGenres(handle: string, parentId: string | null) : Promise { + return await TAURI_INVOKE("repository_get_genres", { handle, parentId }); }, /** * Search for items */ -async repositorySearch(handle: string, query: string, options: SearchOptions | null) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("repository_search", { handle, query, options }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async repositorySearch(handle: string, query: string, options: SearchOptions | null) : Promise { + return await TAURI_INVOKE("repository_search", { handle, query, options }); }, /** * Get playback info for an item */ -async repositoryGetPlaybackInfo(handle: string, itemId: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("repository_get_playback_info", { handle, itemId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async repositoryGetPlaybackInfo(handle: string, itemId: string) : Promise { + return await TAURI_INVOKE("repository_get_playback_info", { handle, itemId }); }, /** * Get video stream URL with optional seeking support */ -async repositoryGetVideoStreamUrl(handle: string, itemId: string, mediaSourceId: string | null, startTimeSeconds: number | null, audioStreamIndex: number | null) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("repository_get_video_stream_url", { handle, itemId, mediaSourceId, startTimeSeconds, audioStreamIndex }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async repositoryGetVideoStreamUrl(handle: string, itemId: string, mediaSourceId: string | null, startTimeSeconds: number | null, audioStreamIndex: number | null) : Promise { + return await TAURI_INVOKE("repository_get_video_stream_url", { handle, itemId, mediaSourceId, startTimeSeconds, audioStreamIndex }); }, /** * Get audio stream URL for a track */ -async repositoryGetAudioStreamUrl(handle: string, itemId: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("repository_get_audio_stream_url", { handle, itemId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async repositoryGetAudioStreamUrl(handle: string, itemId: string) : Promise { + return await TAURI_INVOKE("repository_get_audio_stream_url", { handle, itemId }); }, /** * Report playback start */ -async repositoryReportPlaybackStart(handle: string, itemId: string, positionTicks: number) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("repository_report_playback_start", { handle, itemId, positionTicks }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async repositoryReportPlaybackStart(handle: string, itemId: string, positionTicks: number) : Promise { + return await TAURI_INVOKE("repository_report_playback_start", { handle, itemId, positionTicks }); }, /** * Report playback progress */ -async repositoryReportPlaybackProgress(handle: string, itemId: string, positionTicks: number) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("repository_report_playback_progress", { handle, itemId, positionTicks }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async repositoryReportPlaybackProgress(handle: string, itemId: string, positionTicks: number) : Promise { + return await TAURI_INVOKE("repository_report_playback_progress", { handle, itemId, positionTicks }); }, /** * Report playback stopped */ -async repositoryReportPlaybackStopped(handle: string, itemId: string, positionTicks: number) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("repository_report_playback_stopped", { handle, itemId, positionTicks }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async repositoryReportPlaybackStopped(handle: string, itemId: string, positionTicks: number) : Promise { + return await TAURI_INVOKE("repository_report_playback_stopped", { handle, itemId, positionTicks }); }, /** * Get image URL for an item */ -async repositoryGetImageUrl(handle: string, itemId: string, imageType: ImageType, options: ImageOptions | null) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("repository_get_image_url", { handle, itemId, imageType, options }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async repositoryGetImageUrl(handle: string, itemId: string, imageType: ImageType, options: ImageOptions | null) : Promise { + return await TAURI_INVOKE("repository_get_image_url", { handle, itemId, imageType, options }); }, /** * Mark an item as favorite */ -async repositoryMarkFavorite(handle: string, itemId: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("repository_mark_favorite", { handle, itemId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async repositoryMarkFavorite(handle: string, itemId: string) : Promise { + return await TAURI_INVOKE("repository_mark_favorite", { handle, itemId }); }, /** * Unmark an item as favorite */ -async repositoryUnmarkFavorite(handle: string, itemId: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("repository_unmark_favorite", { handle, itemId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async repositoryUnmarkFavorite(handle: string, itemId: string) : Promise { + return await TAURI_INVOKE("repository_unmark_favorite", { handle, itemId }); }, /** * Get person details */ -async repositoryGetPerson(handle: string, personId: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("repository_get_person", { handle, personId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async repositoryGetPerson(handle: string, personId: string) : Promise { + return await TAURI_INVOKE("repository_get_person", { handle, personId }); }, /** * Get items by person (actor, director, etc.) */ -async repositoryGetItemsByPerson(handle: string, personId: string, options: GetItemsOptions | null) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("repository_get_items_by_person", { handle, personId, options }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async repositoryGetItemsByPerson(handle: string, personId: string, options: GetItemsOptions | null) : Promise { + return await TAURI_INVOKE("repository_get_items_by_person", { handle, personId, options }); }, /** * Get similar/related items for a media item */ -async repositoryGetSimilarItems(handle: string, itemId: string, limit: number | null) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("repository_get_similar_items", { handle, itemId, limit }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async repositoryGetSimilarItems(handle: string, itemId: string, limit: number | null) : Promise { + return await TAURI_INVOKE("repository_get_similar_items", { handle, itemId, limit }); }, /** * Create a new playlist */ -async playlistCreate(handle: string, name: string, itemIds: string[] | null) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("playlist_create", { handle, name, itemIds }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playlistCreate(handle: string, name: string, itemIds: string[] | null) : Promise { + return await TAURI_INVOKE("playlist_create", { handle, name, itemIds }); }, /** * Delete a playlist */ -async playlistDelete(handle: string, playlistId: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("playlist_delete", { handle, playlistId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playlistDelete(handle: string, playlistId: string) : Promise { + return await TAURI_INVOKE("playlist_delete", { handle, playlistId }); }, /** * Rename a playlist */ -async playlistRename(handle: string, playlistId: string, name: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("playlist_rename", { handle, playlistId, name }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playlistRename(handle: string, playlistId: string, name: string) : Promise { + return await TAURI_INVOKE("playlist_rename", { handle, playlistId, name }); }, /** * Get playlist items with PlaylistItemId */ -async playlistGetItems(handle: string, playlistId: string) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("playlist_get_items", { handle, playlistId }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playlistGetItems(handle: string, playlistId: string) : Promise { + return await TAURI_INVOKE("playlist_get_items", { handle, playlistId }); }, /** * Add items to a playlist */ -async playlistAddItems(handle: string, playlistId: string, itemIds: string[]) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("playlist_add_items", { handle, playlistId, itemIds }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playlistAddItems(handle: string, playlistId: string, itemIds: string[]) : Promise { + return await TAURI_INVOKE("playlist_add_items", { handle, playlistId, itemIds }); }, /** * Remove items from a playlist (uses PlaylistItemId entry IDs, NOT media item IDs) */ -async playlistRemoveItems(handle: string, playlistId: string, entryIds: string[]) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("playlist_remove_items", { handle, playlistId, entryIds }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playlistRemoveItems(handle: string, playlistId: string, entryIds: string[]) : Promise { + return await TAURI_INVOKE("playlist_remove_items", { handle, playlistId, entryIds }); }, /** * Move a playlist item to a new position */ -async playlistMoveItem(handle: string, playlistId: string, itemId: string, newIndex: number) : Promise> { - try { - return { status: "ok", data: await TAURI_INVOKE("playlist_move_item", { handle, playlistId, itemId, newIndex }) }; -} catch (e) { - if(e instanceof Error) throw e; - else return { status: "error", error: e as any }; -} +async playlistMoveItem(handle: string, playlistId: string, itemId: string, newIndex: number) : Promise { + return await TAURI_INVOKE("playlist_move_item", { handle, playlistId, itemId, newIndex }); }, /** * Format time in seconds to MM:SS display string @@ -2278,6 +1308,14 @@ export type AudioTrackSwitchResponse = * Authentication result */ export type AuthResult = { user: User; accessToken: string; serverId: string } +/** + * Server information returned from Jellyfin + */ +export type AuthServerInfo = { name: string; version: string; id: string; +/** + * Normalized server URL with protocol and no trailing slash + */ +normalizedUrl: string } /** * Autoplay settings (controls next episode behavior) */ @@ -2415,104 +1453,6 @@ export type Library = { id: string; name: string; collectionType: string; imageT * Media item */ export type MediaItem = { id: string; name: string; type: string; serverId: string; parentId?: string | null; libraryId?: string | null; overview?: string | null; genres?: string[] | null; productionYear?: number | null; communityRating?: number | null; officialRating?: string | null; runtimeTicks?: number | null; primaryImageTag?: string | null; backdropImageTags?: string[] | null; parentBackdropImageTags?: string[] | null; albumId?: string | null; albumName?: string | null; albumArtist?: string | null; artists?: string[] | null; artistItems?: ArtistItem[] | null; indexNumber?: number | null; parentIndexNumber?: number | null; seriesId?: string | null; seriesName?: string | null; seasonId?: string | null; seasonName?: string | null; userData?: UserData | null; mediaStreams?: MediaStream[] | null; mediaSources?: MediaSource[] | null; people?: Person[] | null } -/** - * Represents a media item that can be played - * - * TRACES: UR-003, UR-004 | DR-002 - */ -export type MediaItem = { -/** - * Unique identifier - */ -id: string; -/** - * Display title - */ -title: string; -/** - * Name (alias for title - for frontend compatibility) - */ -name?: string | null; -/** - * Artist name(s) for audio - */ -artist: string | null; -/** - * Album name for audio - */ -album: string | null; -/** - * Album name (alias - for frontend compatibility) - */ -albumName?: string | null; -/** - * Album ID (Jellyfin ID) for remote transfer context - */ -albumId?: string | null; -/** - * Artist items with IDs for clickable links - */ -artistItems?: ArtistItem[] | null; -/** - * Artists as array of strings (fallback when artist_items not available) - */ -artists?: string[] | null; -/** - * Primary image tag for artwork - */ -primaryImageTag?: string | null; -/** - * Item type (Audio, Movie, Episode, etc.) - */ -type?: string | null; -/** - * Playlist ID (Jellyfin ID) for remote transfer context - */ -playlistId?: string | null; -/** - * Duration in seconds - */ -duration: number | null; -/** - * URL or path to artwork image - */ -artworkUrl: string | null; -/** - * Type of media - */ -mediaType: MediaType; -/** - * Source of the media - */ -source: MediaSource; -/** - * Video codec (e.g., "h264", "hevc") for video media - */ -videoCodec?: string | null; -/** - * Whether the video requires server-side transcoding - */ -needsTranscoding?: boolean; -/** - * Video width in pixels - */ -videoWidth?: number | null; -/** - * Video height in pixels - */ -videoHeight?: number | null; -/** - * Available subtitle tracks - */ -subtitles?: SubtitleTrack[]; -/** - * Series ID (for TV show episodes) - used for series audio preferences - */ -seriesId?: string | null; -/** - * Server ID - used for series audio preferences - */ -serverId?: string | null } /** * Media session type tracking the high-level playback context */ @@ -2525,35 +1465,19 @@ export type MediaSessionType = * Audio playback session (music, audiobooks, podcasts) * Persists until explicitly dismissed */ -{ type: "audio"; last_item: MediaItem | null; is_active: boolean } | +{ type: "audio"; last_item: PlayerMediaItem | null; is_active: boolean } | /** * Movie playback (single video, auto-dismiss on end) */ -{ type: "movie"; item: MediaItem; is_active: boolean } | +{ type: "movie"; item: PlayerMediaItem; is_active: boolean } | /** * TV show playback (supports next episode auto-advance) */ -{ type: "tv_show"; item: MediaItem; series_id: string; is_active: boolean } +{ type: "tv_show"; item: PlayerMediaItem; series_id: string; is_active: boolean } /** * Media source information */ export type MediaSource = { id: string; name: string; container?: string | null; size?: number | null; bitrate?: number | null; supportsDirectPlay: boolean; supportsDirectStream: boolean; supportsTranscoding: boolean; directStreamUrl?: string | null } -/** - * TRACES: UR-002, UR-003, UR-004, UR-011 | DR-003 - */ -export type MediaSource = -/** - * Streaming from Jellyfin server - */ -{ type: "remote"; stream_url: string; jellyfin_item_id: string } | -/** - * Downloaded/cached locally - */ -{ type: "local"; file_path: string; jellyfin_item_id: string | null } | -/** - * Direct URL (e.g., channel plugins) - */ -{ type: "directurl"; url: string } /** * Media stream information (audio, video, subtitle tracks) */ @@ -2564,7 +1488,7 @@ export type MediaType = "audio" | "video" * Converts from both local MediaItem and remote NowPlayingItem */ export type MergedMediaItem = { id: string; title: string; artist: string | null; album: string | null; albumId: string | null; duration: number | null; primaryImageTag: string | null; mediaType: string } -export type NowPlayingItem = { Id: string | null; Name: string | null; RunTimeTicks: number | null; Album: string | null; AlbumId: string | null; AlbumArtist: string | null; Artists: string[] | null; ImageTags: Partial<{ [key in string]: string }> | null; PrimaryImageTag: string | null; AlbumPrimaryImageTag: string | null; Type: string | null } +export type NowPlayingItem = { id: string | null; name: string | null; runTimeTicks: number | null; album: string | null; albumId: string | null; albumArtist: string | null; artists: string[] | null; imageTags: Partial<{ [key in string]: string }> | null; primaryImageTag: string | null; albumPrimaryImageTag: string | null; Type: string | null } export type OfflineItem = { id: string; name: string; itemType: string; albumId: string | null; albumName: string | null; artists: string | null; runtimeTicks: number | null; primaryImageTag: string | null } /** * Person (cast/crew member) - for movies, series, and episodes @@ -2635,7 +1559,7 @@ export type PlayQueueRequest = { items: PlayItemRequest[]; startIndex: number; s * Used for remote playback transfer */ context?: PlayQueueContext | null } -export type PlayState = { PositionTicks?: number | null; CanSeek?: boolean | null; IsPaused?: boolean | null; IsMuted?: boolean | null; VolumeLevel?: number | null; RepeatMode?: string | null; ShuffleMode?: string | null } +export type PlayState = { positionTicks?: number | null; canSeek?: boolean | null; isPaused?: boolean | null; isMuted?: boolean | null; volumeLevel?: number | null; repeatMode?: string | null; shuffleMode?: string | null } /** * Context information for track playback */ @@ -2656,6 +1580,120 @@ export type PlaybackMode = { type: "local" } | { type: "remote"; session_id: str * Playback progress info */ export type PlaybackProgress = { itemId: string; positionTicks: number; isPlayed: boolean; isFavorite: boolean; playCount: number } +/** + * Represents a media item that can be played + * + * TRACES: UR-003, UR-004 | DR-002 + */ +export type PlayerMediaItem = { +/** + * Unique identifier + */ +id: string; +/** + * Display title + */ +title: string; +/** + * Name (alias for title - for frontend compatibility) + */ +name?: string | null; +/** + * Artist name(s) for audio + */ +artist: string | null; +/** + * Album name for audio + */ +album: string | null; +/** + * Album name (alias - for frontend compatibility) + */ +albumName?: string | null; +/** + * Album ID (Jellyfin ID) for remote transfer context + */ +albumId?: string | null; +/** + * Artist items with IDs for clickable links + */ +artistItems?: ArtistItem[] | null; +/** + * Artists as array of strings (fallback when artist_items not available) + */ +artists?: string[] | null; +/** + * Primary image tag for artwork + */ +primaryImageTag?: string | null; +/** + * Item type (Audio, Movie, Episode, etc.) + */ +type?: string | null; +/** + * Playlist ID (Jellyfin ID) for remote transfer context + */ +playlistId?: string | null; +/** + * Duration in seconds + */ +duration: number | null; +/** + * URL or path to artwork image + */ +artworkUrl: string | null; +/** + * Type of media + */ +mediaType: MediaType; +/** + * Source of the media + */ +source: PlayerMediaSource; +/** + * Video codec (e.g., "h264", "hevc") for video media + */ +videoCodec?: string | null; +/** + * Whether the video requires server-side transcoding + */ +needsTranscoding?: boolean; +/** + * Video width in pixels + */ +videoWidth?: number | null; +/** + * Video height in pixels + */ +videoHeight?: number | null; +/** + * Available subtitle tracks + */ +subtitles?: SubtitleTrack[]; +/** + * Series ID (for TV show episodes) - used for series audio preferences + */ +seriesId?: string | null; +/** + * Server ID - used for series audio preferences + */ +serverId?: string | null } +/** + * TRACES: UR-002, UR-003, UR-004, UR-011 | DR-003 + */ +export type PlayerMediaSource = +/** + * Streaming from Jellyfin server + */ +{ type: "remote"; stream_url: string; jellyfin_item_id: string } | +/** + * Downloaded/cached locally + */ +{ type: "local"; file_path: string; jellyfin_item_id: string | null } | +/** + * Direct URL (e.g., channel plugins) + */ +{ type: "directurl"; url: string } /** * Player state machine (6 states: Idle, Loading, Playing, Paused, Seeking, Error) * @@ -2669,23 +1707,23 @@ export type PlayerState = /** * Media is being loaded/buffered */ -{ kind: "loading"; media: MediaItem } | +{ kind: "loading"; media: PlayerMediaItem } | /** * Media is playing */ -{ kind: "playing"; media: MediaItem; position: number; duration: number } | +{ kind: "playing"; media: PlayerMediaItem; position: number; duration: number } | /** * Media is paused */ -{ kind: "paused"; media: MediaItem; position: number; duration: number } | +{ kind: "paused"; media: PlayerMediaItem; position: number; duration: number } | /** * Seeking to a new position */ -{ kind: "seeking"; media: MediaItem; target: number } | +{ kind: "seeking"; media: PlayerMediaItem; target: number } | /** * An error occurred */ -{ kind: "error"; media: MediaItem | null; error: string } +{ kind: "error"; media: PlayerMediaItem | null; error: string } /** * Response for player state queries */ @@ -2750,7 +1788,7 @@ skipped: number } /** * Response for queue queries */ -export type QueueStatus = { items: MediaItem[]; currentIndex: number | null; shuffle: boolean; repeat: RepeatMode; hasNext: boolean; hasPrevious: boolean } +export type QueueStatus = { items: PlayerMediaItem[]; currentIndex: number | null; shuffle: boolean; repeat: RepeatMode; hasNext: boolean; hasPrevious: boolean } /** * Remote session status for UI updates */ @@ -2781,14 +1819,6 @@ export type SeriesAudioPreference = { seriesId: string; audioTrackDisplayTitle: * Server info returned to frontend */ export type ServerInfo = { id: string; name: string; url: string; version: string | null } -/** - * Server information returned from Jellyfin - */ -export type ServerInfo = { name: string; version: string; id: string; -/** - * Normalized server URL with protocol and no trailing slash - */ -normalizedUrl: string } /** * Active session for restoration */ @@ -2796,7 +1826,7 @@ export type Session = { userId: string; username: string; serverId: string; serv /** * Session information from Jellyfin */ -export type SessionInfo = { Id?: string | null; UserId?: string | null; UserName?: string | null; Client?: string | null; DeviceName?: string | null; DeviceId?: string | null; ApplicationVersion?: string | null; IsActive?: boolean | null; SupportsMediaControl?: boolean | null; SupportsRemoteControl?: boolean; NowPlayingItem?: NowPlayingItem | null; PlayState?: PlayState | null; PlayableMediaTypes?: string[] | null; SupportedCommands?: string[] | null } +export type SessionInfo = { id?: string | null; userId?: string | null; userName?: string | null; client?: string | null; deviceName?: string | null; deviceId?: string | null; applicationVersion?: string | null; isActive?: boolean | null; supportsMediaControl?: boolean | null; supportsRemoteControl?: boolean; nowPlayingItem?: NowPlayingItem | null; playState?: PlayState | null; playableMediaTypes?: string[] | null; supportedCommands?: string[] | null } /** * Sleep timer mode - determines when playback should stop * TRACES: UR-026 | DR-029 diff --git a/src/lib/components/library/DownloadButton.svelte b/src/lib/components/library/DownloadButton.svelte index 024536d..ff38408 100644 --- a/src/lib/components/library/DownloadButton.svelte +++ b/src/lib/components/library/DownloadButton.svelte @@ -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("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); diff --git a/src/lib/stores/downloads.ts b/src/lib/stores/downloads.ts index 2d810aa..ee8d314 100644 --- a/src/lib/stores/downloads.ts +++ b/src/lib/stores/downloads.ts @@ -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 { try { console.log('📥 downloadItem called:', { itemId, userId, filePath, itemName, artistName, albumName }); - const downloadId = await invoke('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('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);