164 lines
4.0 KiB
TypeScript
164 lines
4.0 KiB
TypeScript
export interface PlaylistSummary {
|
|
id: string;
|
|
title: string;
|
|
role: string;
|
|
persistent: boolean;
|
|
cover_pk?: string | null;
|
|
cover_url?: string | null;
|
|
track_count: number;
|
|
max_size?: number | null;
|
|
default_ttl_secs?: number | null;
|
|
last_change: string;
|
|
}
|
|
|
|
import type { AudioCacheMetadata } from "./audioCache";
|
|
|
|
export interface PlaylistTrack {
|
|
cache_pk: string;
|
|
added_at: string;
|
|
ttl_secs?: number | null;
|
|
lazy_pk?: string | null;
|
|
metadata?: AudioCacheMetadata | null;
|
|
cover_url?: string | null;
|
|
cover_source?: string | null;
|
|
}
|
|
|
|
export interface PlaylistDetail {
|
|
summary: PlaylistSummary;
|
|
tracks: PlaylistTrack[];
|
|
}
|
|
|
|
export interface ApiErrorPayload {
|
|
error?: string;
|
|
message?: string;
|
|
}
|
|
|
|
export interface CreatePlaylistPayload {
|
|
id: string;
|
|
title?: string;
|
|
role?: string;
|
|
cover_pk?: string;
|
|
persistent?: boolean;
|
|
max_size?: number;
|
|
default_ttl_secs?: number;
|
|
}
|
|
|
|
export interface UpdatePlaylistPayload {
|
|
title?: string;
|
|
role?: string;
|
|
max_size?: number | null;
|
|
default_ttl_secs?: number | null;
|
|
cover_pk?: string | null;
|
|
}
|
|
|
|
export interface AddTracksPayload {
|
|
cache_pks: string[];
|
|
ttl_secs?: number;
|
|
lazy?: boolean;
|
|
}
|
|
|
|
async function parseJsonOrThrow<T>(response: Response): Promise<T> {
|
|
if (!response.ok) {
|
|
let message = `HTTP ${response.status}`;
|
|
try {
|
|
const error: ApiErrorPayload = await response.json();
|
|
if (error?.message) {
|
|
message = error.message;
|
|
}
|
|
} catch {
|
|
// Ignore JSON parsing errors and keep default message
|
|
}
|
|
throw new Error(message);
|
|
}
|
|
|
|
return response.json() as Promise<T>;
|
|
}
|
|
|
|
async function ensureSuccess(response: Response): Promise<void> {
|
|
if (!response.ok) {
|
|
let message = `HTTP ${response.status}`;
|
|
try {
|
|
const error: ApiErrorPayload = await response.json();
|
|
if (error?.message) {
|
|
message = error.message;
|
|
}
|
|
} catch {
|
|
// ignore
|
|
}
|
|
throw new Error(message);
|
|
}
|
|
}
|
|
|
|
export async function listPlaylists(): Promise<PlaylistSummary[]> {
|
|
const response = await fetch("/api/playlists");
|
|
return parseJsonOrThrow(response);
|
|
}
|
|
|
|
export async function getPlaylistDetail(id: string): Promise<PlaylistDetail> {
|
|
const response = await fetch(`/api/playlists/${encodeURIComponent(id)}`);
|
|
return parseJsonOrThrow(response);
|
|
}
|
|
|
|
export async function createPlaylist(body: CreatePlaylistPayload): Promise<PlaylistDetail> {
|
|
const response = await fetch("/api/playlists", {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify(body),
|
|
});
|
|
return parseJsonOrThrow(response);
|
|
}
|
|
|
|
export async function updatePlaylist(
|
|
id: string,
|
|
body: UpdatePlaylistPayload
|
|
): Promise<PlaylistDetail> {
|
|
const response = await fetch(`/api/playlists/${encodeURIComponent(id)}`, {
|
|
method: "PATCH",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify(body),
|
|
});
|
|
return parseJsonOrThrow(response);
|
|
}
|
|
|
|
export async function deletePlaylist(id: string): Promise<void> {
|
|
const response = await fetch(`/api/playlists/${encodeURIComponent(id)}`, {
|
|
method: "DELETE",
|
|
});
|
|
await ensureSuccess(response);
|
|
}
|
|
|
|
export async function addTracksToPlaylist(
|
|
id: string,
|
|
payload: AddTracksPayload
|
|
): Promise<PlaylistDetail> {
|
|
const response = await fetch(`/api/playlists/${encodeURIComponent(id)}/tracks`, {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify(payload),
|
|
});
|
|
return parseJsonOrThrow(response);
|
|
}
|
|
|
|
export async function flushPlaylist(id: string): Promise<PlaylistDetail> {
|
|
const response = await fetch(`/api/playlists/${encodeURIComponent(id)}/tracks`, {
|
|
method: "DELETE",
|
|
});
|
|
return parseJsonOrThrow(response);
|
|
}
|
|
|
|
export async function removeTrackFromPlaylist(id: string, cachePk: string): Promise<PlaylistDetail> {
|
|
const response = await fetch(
|
|
`/api/playlists/${encodeURIComponent(id)}/tracks/${encodeURIComponent(cachePk)}`,
|
|
{
|
|
method: "DELETE",
|
|
}
|
|
);
|
|
return parseJsonOrThrow(response);
|
|
}
|