106 lines
2.8 KiB
TypeScript
106 lines
2.8 KiB
TypeScript
const BASE = "/api";
|
|
|
|
export interface Library {
|
|
id: number;
|
|
name: string;
|
|
path: string;
|
|
}
|
|
|
|
export interface Tag {
|
|
id: number;
|
|
name: string;
|
|
category: string;
|
|
}
|
|
|
|
export interface TagsByCategory {
|
|
category: string;
|
|
tags: Tag[];
|
|
}
|
|
|
|
export interface MediaItem {
|
|
id: number;
|
|
library_id: number;
|
|
rel_path: string;
|
|
filename: string;
|
|
media_type: "image" | "video";
|
|
size_bytes: number | null;
|
|
missing: boolean;
|
|
tags: Tag[];
|
|
created_at: string;
|
|
updated_at: string;
|
|
}
|
|
|
|
export interface BrowseEntry {
|
|
name: string;
|
|
type: "dir" | "image" | "video";
|
|
rel_path: string;
|
|
media_item_id: number | null;
|
|
}
|
|
|
|
export interface BrowseResult {
|
|
path: string;
|
|
entries: BrowseEntry[];
|
|
}
|
|
|
|
async function request<T>(path: string, init?: RequestInit): Promise<T> {
|
|
const res = await fetch(`${BASE}${path}`, {
|
|
headers: { "Content-Type": "application/json" },
|
|
...init,
|
|
});
|
|
if (!res.ok) {
|
|
const text = await res.text().catch(() => "");
|
|
throw new Error(`${res.status}: ${text}`);
|
|
}
|
|
if (res.status === 204) return undefined as T;
|
|
return res.json();
|
|
}
|
|
|
|
export const api = {
|
|
libraries: {
|
|
list: () => request<Library[]>("/libraries"),
|
|
create: (name: string, path: string) =>
|
|
request<Library>("/libraries", {
|
|
method: "POST",
|
|
body: JSON.stringify({ name, path }),
|
|
}),
|
|
delete: (id: number) =>
|
|
request<void>(`/libraries/${id}`, { method: "DELETE" }),
|
|
browse: (id: number, path = "") =>
|
|
request<BrowseResult>(`/libraries/${id}/browse?path=${encodeURIComponent(path)}`),
|
|
scanStatus: (id: number) =>
|
|
request<{ scanning: boolean }>(`/libraries/${id}/scan-status`),
|
|
rescan: (id: number) =>
|
|
request<{ scanning: boolean }>(`/libraries/${id}/rescan`, { method: "POST" }),
|
|
},
|
|
|
|
media: {
|
|
get: (id: number) => request<MediaItem>(`/media/${id}`),
|
|
fileUrl: (id: number) => `${BASE}/media/${id}/file`,
|
|
thumbnailUrl: (id: number) => `${BASE}/media/${id}/thumbnail`,
|
|
setTags: (id: number, tagIds: number[]) =>
|
|
request<MediaItem>(`/media/${id}/tags`, {
|
|
method: "PUT",
|
|
body: JSON.stringify({ tag_ids: tagIds }),
|
|
}),
|
|
},
|
|
|
|
tags: {
|
|
list: () => request<TagsByCategory[]>("/tags"),
|
|
create: (name: string, category: string) =>
|
|
request<Tag>("/tags", {
|
|
method: "POST",
|
|
body: JSON.stringify({ name, category }),
|
|
}),
|
|
delete: (id: number) =>
|
|
request<void>(`/tags/${id}`, { method: "DELETE" }),
|
|
},
|
|
|
|
search: (params: { q?: string; tags?: number[]; library_id?: number }) => {
|
|
const p = new URLSearchParams();
|
|
if (params.q) p.set("q", params.q);
|
|
if (params.tags?.length) p.set("tags", params.tags.join(","));
|
|
if (params.library_id != null) p.set("library_id", String(params.library_id));
|
|
return request<MediaItem[]>(`/search?${p}`);
|
|
},
|
|
};
|