Files
mcma-webui/src/api/endpoints/downloads.ts
T
Senko-san 231887c3b7 feat(discover): wire A4 search + A5 downloads to backend
Adds DownloadJob/ExternalSearchResult/SourceInfo contract types + mappers, the downloads + search RTKQ endpoints, and the SearchDownloadPage (search external sources, per-result download states) and DownloadsManagerPage (active/history, progress, retry/cancel, poll-while-active). en/ru i18n. Snapshot also bundles in-progress queue/metadata-editor/storage UI work.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 14:04:43 +03:00

107 lines
3.0 KiB
TypeScript

import { api } from '../index';
import {
toDownloadJob,
toPage,
type RawDownloadJob,
type RawPaged,
} from '../mappers';
import type {
DownloadJob,
DownloadRequestResult,
PaginatedResponse,
} from '../types';
interface ListParams {
status?: DownloadJob['status'];
/** Only the current user's jobs (backend `mine=true`). */
mine?: boolean;
page?: number;
pageSize?: number;
}
interface RawCreateResponse {
already_in_library: boolean;
track_id: string | null;
job: RawDownloadJob | null;
}
export const downloadsApi = api.injectEndpoints({
endpoints: (build) => ({
getDownloads: build.query<
PaginatedResponse<DownloadJob>,
ListParams | void
>({
query: (params) => {
const p = params ?? {};
const size = p.pageSize ?? 50;
return {
url: '/downloads',
params: {
status: p.status,
mine: p.mine ? true : undefined,
limit: size,
offset: ((p.page ?? 1) - 1) * size,
},
};
},
transformResponse: (raw: RawPaged<RawDownloadJob>) =>
toPage(raw, toDownloadJob),
providesTags: (result) =>
result
? [
...result.items.map(({ id }) => ({
type: 'Download' as const,
id,
})),
'Download',
]
: ['Download'],
}),
getDownload: build.query<DownloadJob, string>({
query: (id) => `/downloads/${id}`,
transformResponse: (raw: RawDownloadJob) => toDownloadJob(raw),
providesTags: (_r, _e, id) => [{ type: 'Download', id }],
}),
/** Request a download of a discovered item (§A4 "Download to library"). */
createDownload: build.mutation<
DownloadRequestResult,
{ source: string; sourceId: string; query?: string }
>({
query: (body) => ({
url: '/downloads',
method: 'POST',
body: {
source: body.source,
source_id: body.sourceId,
query: body.query,
},
}),
transformResponse: (raw: RawCreateResponse): DownloadRequestResult => ({
alreadyInLibrary: raw.already_in_library,
trackId: raw.track_id ?? undefined,
job: raw.job ? toDownloadJob(raw.job) : undefined,
}),
// A completed dedup can surface an existing library track; refresh both.
invalidatesTags: ['Download', 'Track'],
}),
cancelDownload: build.mutation<void, string>({
query: (id) => ({ url: `/downloads/${id}`, method: 'DELETE' }),
invalidatesTags: ['Download'],
}),
retryDownload: build.mutation<DownloadJob, string>({
query: (id) => ({ url: `/downloads/${id}/retry`, method: 'POST' }),
transformResponse: (raw: RawDownloadJob) => toDownloadJob(raw),
invalidatesTags: (_r, _e, id) => [{ type: 'Download', id }, 'Download'],
}),
}),
overrideExisting: false,
});
export const {
useGetDownloadsQuery,
useGetDownloadQuery,
useCreateDownloadMutation,
useCancelDownloadMutation,
useRetryDownloadMutation,
} = downloadsApi;