Initial commit: Shelfless – alternative Audiobookshelf frontend
React + Vite + TypeScript SPA covering the full ABS feature set (library browsing, item detail, metadata/cover editing, podcasts, player with session sync, admin: users/libraries/scanner/server settings). Dev uses a dynamic CORS proxy; production is served by server/index.mjs (static + reverse proxy to ABS_URL). Includes systemd unit and installer under deploy/. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
59
src/api/items.ts
Normal file
59
src/api/items.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { api } from './client'
|
||||
import type { BookMetadata, LibraryItem, PodcastMetadata } from '@/types/abs'
|
||||
|
||||
export interface GetItemOptions {
|
||||
expanded?: boolean
|
||||
/** e.g. "progress" */
|
||||
include?: string
|
||||
}
|
||||
|
||||
export async function getItem(id: string, opts: GetItemOptions = {}): Promise<LibraryItem> {
|
||||
const res = await api.get<LibraryItem>(`/api/items/${id}`, {
|
||||
params: {
|
||||
expanded: opts.expanded ? 1 : undefined,
|
||||
include: opts.include,
|
||||
},
|
||||
})
|
||||
return res.data
|
||||
}
|
||||
|
||||
export async function deleteItem(id: string, hard = false): Promise<void> {
|
||||
await api.delete(`/api/items/${id}`, { params: hard ? { hard: 1 } : undefined })
|
||||
}
|
||||
|
||||
/** Update book/podcast metadata. ABS: PATCH /api/items/:id/media with { metadata }. */
|
||||
export async function updateMedia(
|
||||
id: string,
|
||||
payload: { metadata?: Partial<BookMetadata> | Partial<PodcastMetadata>; tags?: string[] },
|
||||
): Promise<LibraryItem> {
|
||||
const res = await api.patch<LibraryItem>(`/api/items/${id}/media`, payload)
|
||||
return res.data
|
||||
}
|
||||
|
||||
/** Upload a cover image file (multipart/form-data). */
|
||||
export async function uploadCover(id: string, file: File): Promise<{ success?: boolean; cover?: string }> {
|
||||
const form = new FormData()
|
||||
form.append('cover', file)
|
||||
const res = await api.post(`/api/items/${id}/cover`, form, {
|
||||
headers: { 'Content-Type': 'multipart/form-data' },
|
||||
})
|
||||
return res.data
|
||||
}
|
||||
|
||||
/** Set a cover from a remote URL (JSON body). */
|
||||
export async function setCoverByUrl(id: string, url: string): Promise<{ success?: boolean; cover?: string }> {
|
||||
const res = await api.post(`/api/items/${id}/cover`, { url })
|
||||
return res.data
|
||||
}
|
||||
|
||||
export async function removeCover(id: string): Promise<void> {
|
||||
await api.delete(`/api/items/${id}/cover`)
|
||||
}
|
||||
|
||||
export async function batchDelete(libraryItemIds: string[], hard = false): Promise<void> {
|
||||
await api.post('/api/items/batch/delete', { libraryItemIds }, { params: hard ? { hard: 1 } : undefined })
|
||||
}
|
||||
|
||||
export async function batchUpdate(updates: Array<{ id: string } & Record<string, unknown>>): Promise<void> {
|
||||
await api.post('/api/items/batch/update', updates)
|
||||
}
|
||||
Reference in New Issue
Block a user