import { invoke } from '@tauri-apps/api/core'; import { useAuthStore } from '../store/authStore'; import { api } from './subsonicClient'; import type { SubsonicPlaylist, SubsonicSong } from './subsonicTypes'; export async function getPlaylists(includeOrbit = false): Promise { const data = await api<{ playlists: { playlist: SubsonicPlaylist[] } }>('getPlaylists.view', { _t: Date.now() }); const all = data.playlists?.playlist ?? []; // Orbit session + outbox playlists are technical internals. They're `public` // so guests can reach them, which means they leak into every UI picker and // even into the Navidrome web client. Filter them out of every UI call; // orbit's own sweep passes `includeOrbit=true`. return includeOrbit ? all : all.filter(p => !p.name.startsWith('__psyorbit_')); } export async function getPlaylist(id: string): Promise<{ playlist: SubsonicPlaylist; songs: SubsonicSong[] }> { const data = await api<{ playlist: SubsonicPlaylist & { entry: SubsonicSong[] } }>('getPlaylist.view', { id }); const { entry, ...playlist } = data.playlist; return { playlist, songs: entry ?? [] }; } export async function createPlaylist(name: string, songIds?: string[]): Promise { const params: Record = { name }; if (songIds && songIds.length > 0) { params.songId = songIds; } const data = await api<{ playlist: SubsonicPlaylist }>('createPlaylist.view', params); return data.playlist; } export async function updatePlaylist(id: string, songIds: string[], prevCount = 0): Promise { if (songIds.length > 0) { // createPlaylist with playlistId replaces the existing playlist's songs (Subsonic API 1.14+) await api('createPlaylist.view', { playlistId: id, songId: songIds }); } else if (prevCount > 0) { // Axios serialises empty arrays as no params — createPlaylist.view would leave songs unchanged. // Use updatePlaylist.view with explicit index removal to clear the list instead. await api('updatePlaylist.view', { playlistId: id, songIndexToRemove: Array.from({ length: prevCount }, (_, i) => i), }); } } export async function updatePlaylistMeta( id: string, name: string, comment: string, isPublic: boolean, ): Promise { await api('updatePlaylist.view', { playlistId: id, name, comment, public: isPublic }); } export async function uploadPlaylistCoverArt(id: string, file: File): Promise { // Navidrome-specific endpoint — handled in Rust to bypass browser CORS restrictions. const { getBaseUrl, getActiveServer } = useAuthStore.getState(); const server = getActiveServer(); const baseUrl = getBaseUrl(); const buffer = await file.arrayBuffer(); const fileBytes = Array.from(new Uint8Array(buffer)); await invoke('upload_playlist_cover', { serverUrl: baseUrl, playlistId: id, username: server?.username ?? '', password: server?.password ?? '', fileBytes, mimeType: file.type || 'image/jpeg', }); } export async function uploadArtistImage(id: string, file: File): Promise { // Navidrome-specific endpoint — handled in Rust to bypass browser CORS restrictions. const { getBaseUrl, getActiveServer } = useAuthStore.getState(); const server = getActiveServer(); const baseUrl = getBaseUrl(); const buffer = await file.arrayBuffer(); const fileBytes = Array.from(new Uint8Array(buffer)); await invoke('upload_artist_image', { serverUrl: baseUrl, artistId: id, username: server?.username ?? '', password: server?.password ?? '', fileBytes, mimeType: file.type || 'image/jpeg', }); } export async function deletePlaylist(id: string): Promise { await api('deletePlaylist.view', { id }); }