Three new SPA list+editor surfaces, closing the Blazor-only gap found in the #91 phase-(b) recon: - FFmpeg Profiles (System group): list with resolution/video/audio summary + add/copy/delete; full General/Video/Audio editor. Per-hwaccel video-format and profile rules ported from FFmpegProfileEditViewModelValidator; video preset list ported from FFmpegLibraryHelper.PresetsForFFmpegProfile / AvailablePresets.ForAccelAndFormat (cited in code). Resolutions from /api/settings/resolutions, hwaccel kinds from the new lookup endpoint. Copy = client-side clone into the create form (no server copy endpoint). - Filler Presets (Media group): list + editor with the Blazor VM interlocks (Fallback/Tail force mode None; non-MidRoll clears expression; Fallback disables chapters; Playlist collection type forces Count mode; duration/ count/pad fields gated by mode). Collection pickers use the existing /api/library/browse client for all seven collection types incl. Playlist. - Watermarks (System group): list + editor with mode-driven field enabling, ChannelLogo vs Custom image source, upload via /api/artwork/uploads (target=watermark) and preview via /artwork/watermarks/{path}. - App.tsx: ScreenIds/routes (sub-paths /add + /{id}) and nav registration; api modules ffmpegProfiles/fillerPresets/watermarks with URL-assert tests; SettingsScreen Classic-UI help text updated (these are no longer Blazor-only) and the FFmpeg-profiles card now links to the new screen. - Regenerated web/src/api/generated/v1.d.ts from the #159 OpenAPI changes. web verification: typecheck clean; vitest 188/188 (14 files); vite build OK; eslint clean on touched files. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
48 lines
1.9 KiB
TypeScript
48 lines
1.9 KiB
TypeScript
import { ApiError, request } from './client';
|
|
import type { components } from './generated/v1';
|
|
|
|
// The list endpoint (GET /api/watermarks) is picker-grade (id + name) and is already
|
|
// provided by pickers.ts as getWatermarks(). This module adds the by-id read (full model)
|
|
// and the mutations. Image upload is handled by artwork.ts (uploadArtwork(file,'watermark')).
|
|
|
|
export type WatermarkDetail = components['schemas']['WatermarkFullResponseModel'];
|
|
export type CreateWatermarkRequest = components['schemas']['CreateWatermarkRequest'];
|
|
export type UpdateWatermarkRequest = components['schemas']['UpdateWatermarkRequest'];
|
|
|
|
export function getWatermark(id: number): Promise<WatermarkDetail> {
|
|
return request<WatermarkDetail>(`/api/watermarks/${id}`);
|
|
}
|
|
|
|
export function createWatermark(body: CreateWatermarkRequest): Promise<WatermarkDetail> {
|
|
return request<WatermarkDetail>('/api/watermarks', { body, method: 'POST' });
|
|
}
|
|
|
|
export function updateWatermark(id: number, body: UpdateWatermarkRequest): Promise<WatermarkDetail> {
|
|
return request<WatermarkDetail>(`/api/watermarks/${id}`, { body, method: 'PUT' });
|
|
}
|
|
|
|
export function deleteWatermark(id: number): Promise<void> {
|
|
return request<void>(`/api/watermarks/${id}`, { method: 'DELETE' });
|
|
}
|
|
|
|
// Preview URL for a custom watermark image. Mirrors the Blazor editor
|
|
// (WatermarkEditor.razor) which renders `artwork/watermarks/{path}?contentType=...` via
|
|
// ArtworkContentTypeModel.UrlWithContentType.
|
|
export function watermarkImageUrl(path: string, contentType?: null | string): string {
|
|
return contentType
|
|
? `/artwork/watermarks/${path}?contentType=${encodeURIComponent(contentType)}`
|
|
: `/artwork/watermarks/${path}`;
|
|
}
|
|
|
|
export function messageFromWatermarkError(error: unknown, fallback = 'Unable to load watermarks'): string {
|
|
if (error instanceof ApiError) {
|
|
return error.detail ?? error.message;
|
|
}
|
|
|
|
if (error instanceof Error) {
|
|
return error.message;
|
|
}
|
|
|
|
return fallback;
|
|
}
|