Spaces:
Runtime error
Runtime error
Download frontend/src/api/endpoints.ts from firebird-technologies/NoteStack-Test: direct link, hf CLI and curl.
- Browser
- Download file 24.8 kB
-
https://huggingface.co/spaces/firebird-technologies/NoteStack-Test/resolve/1aa4a1663ff037b16e33cea96515f0268a23545d/frontend/src/api/endpoints.ts
- Command line
-
hf download hf://spaces/firebird-technologies/NoteStack-Test@1aa4a1663ff037b16e33cea96515f0268a23545d/frontend/src/api/endpoints.ts
-
curl -L -o endpoints.ts https://huggingface.co/spaces/firebird-technologies/NoteStack-Test/resolve/1aa4a1663ff037b16e33cea96515f0268a23545d/frontend/src/api/endpoints.ts
24.8 kB
| import { api, apiBlob, del, patch, post, postForm, put, qs, uploadFile } from "./client"; | |
| import type { | |
| Artifact, | |
| ArtifactType, | |
| BillingStatus, | |
| CalendarItem, | |
| ChatMessage, | |
| ChatSummary, | |
| Doc, | |
| DocDetail, | |
| Job, | |
| LibraryVoice, | |
| Notebook, | |
| NotebookSummary, | |
| WorkspaceChat, | |
| Page, | |
| Platform, | |
| Quota, | |
| ResurfaceItem, | |
| Settings, | |
| SocialAccounts, | |
| Source, | |
| TopicDetail, | |
| TopicMap, | |
| Usage, | |
| Voice, | |
| Delivery, | |
| VoiceDesignInput, | |
| VoiceDesignPreview, | |
| VoiceProfileData, | |
| VoiceState, | |
| ExtractedTheme, | |
| LegacyVideo, | |
| MyTemplate, | |
| ScriptPreview, | |
| ScriptScene, | |
| TemplateGeneration, | |
| TemplatesResponse, | |
| VideoCatalog, | |
| VideoConfig, | |
| VideoCreateBody, | |
| VideoFocusSource, | |
| VideoFocusTopic, | |
| VideoCustomVoice, | |
| VideoDesignedVoice, | |
| VideoJobState, | |
| VideoListItem, | |
| VideoOptions, | |
| VideoProject, | |
| VideoProjectData, | |
| VideoQuota, | |
| VideoSavedVoice, | |
| VideoScene, | |
| VideoStatus, | |
| VideoStyleItem, | |
| VideoVoicesResponse, | |
| } from "./types"; | |
| type SourceJob = { source: Source; job: Job }; | |
| export const sourcesApi = { | |
| list: () => api<Source[]>("/api/sources"), | |
| connect: (url: string) => post<SourceJob>("/api/sources", { url }), | |
| importUrl: (url: string) => post<SourceJob>("/api/sources/url", { url }), | |
| importFile: async (file: File) => { | |
| const { upload_id } = await uploadFile(file); | |
| return post<SourceJob>("/api/sources/upload", { upload_id }); | |
| }, | |
| sync: (id: string) => post<SourceJob>(`/api/sources/${id}/sync`), | |
| remove: (id: string) => del<{ ok: true; removed_posts: number }>(`/api/sources/${id}`), | |
| }; | |
| export const docsApi = { | |
| list: (p: { q?: string; source_id?: string; limit?: number; offset?: number } = {}) => | |
| api<Page<Doc>>(`/api/documents${qs(p)}`), | |
| get: (id: string) => api<DocDetail>(`/api/documents/${id}`), | |
| remove: (id: string) => del(`/api/documents/${id}`), | |
| }; | |
| export const notebooksApi = { | |
| list: () => api<NotebookSummary[]>("/api/notebooks"), | |
| get: (id: string) => api<Notebook>(`/api/notebooks/${id}`), | |
| create: (title: string, document_ids: string[] = [], description?: string) => | |
| post<{ id: string; title: string; added: number }>("/api/notebooks", { title, document_ids, description }), | |
| /** The "All posts" notebook, created on first use. */ | |
| archive: () => post<{ id: string; title: string }>("/api/notebooks/archive"), | |
| fromTopic: (topicId: string) => post<{ id: string; title: string }>(`/api/notebooks/from-topic/${topicId}`), | |
| update: (id: string, body: { title?: string; description?: string }) => patch(`/api/notebooks/${id}`, body), | |
| remove: (id: string) => del(`/api/notebooks/${id}`), | |
| addDocs: (id: string, document_ids: string[]) => | |
| post<{ added: number }>(`/api/notebooks/${id}/documents`, { document_ids }), | |
| removeDoc: (id: string, docId: string) => del(`/api/notebooks/${id}/documents/${docId}`), | |
| artifacts: (id: string) => api<Artifact[]>(`/api/notebooks/${id}/artifacts`), | |
| chats: (id: string) => api<ChatSummary[]>(`/api/notebooks/${id}/chats`), | |
| /** Every answered chat in the workspace, newest first. */ | |
| allChats: () => api<WorkspaceChat[]>("/api/notebooks/chats"), | |
| messages: (chatId: string) => api<ChatMessage[]>(`/api/notebooks/chats/${chatId}/messages`), | |
| removeChat: (chatId: string) => del(`/api/notebooks/chats/${chatId}`), | |
| }; | |
| export type GenerateBody = { | |
| type: ArtifactType; | |
| notebook_id?: string; | |
| document_id?: string; | |
| /** No notebook or post: use the "All posts" notebook. */ | |
| archive?: boolean; | |
| format?: "deep_dive" | "brief" | "debate"; | |
| minutes?: number; | |
| style?: "audiogram"; | |
| audio_artifact_id?: string; | |
| slides?: { heading: string; body: string }[]; | |
| parent_id?: string; | |
| }; | |
| export const artifactsApi = { | |
| list: (p: { type?: string; notebook_id?: string; document_id?: string; status?: string; q?: string; | |
| limit?: number; offset?: number } = {}) => api<Page<Artifact>>(`/api/artifacts${qs(p)}`), | |
| get: (id: string) => api<Artifact>(`/api/artifacts/${id}`), | |
| generate: (body: GenerateBody) => post<Artifact>("/api/artifacts/generate", body), | |
| update: (id: string, body: { title?: string; content?: Record<string, unknown> }) => | |
| patch<Artifact>(`/api/artifacts/${id}`, body), | |
| remove: (id: string) => del(`/api/artifacts/${id}`), | |
| retry: (id: string) => post<Artifact>(`/api/artifacts/${id}/retry`), | |
| }; | |
| export const jobsApi = { | |
| active: () => api<Job[]>("/api/jobs?active=1"), | |
| recent: (kind?: string) => api<Job[]>(`/api/jobs${qs({ kind, limit: 10 })}`), | |
| get: (id: string) => api<Job>(`/api/jobs/${id}`), | |
| }; | |
| export const topicsApi = { | |
| map: () => api<TopicMap>("/api/topics"), | |
| get: (id: string) => api<TopicDetail>(`/api/topics/${id}`), | |
| rebuild: (full = false) => post<Job>(`/api/topics/rebuild${qs({ full })}`), | |
| }; | |
| export const voiceApi = { | |
| get: () => api<VoiceState>("/api/voice"), | |
| update: (body: { | |
| profile?: VoiceProfileData; | |
| host_voices?: Partial<{ host_a: string; host_b: string }>; | |
| delivery?: Partial<{ host_a: Delivery; host_b: Delivery }>; | |
| }) => put<VoiceState>("/api/voice", body), | |
| build: (document_ids: string[]) => post<Job>("/api/voice/build", { document_ids }), | |
| voices: () => api<Voice[]>("/api/voice/voices"), | |
| consent: async (files: File[], consent_text: string, remove_background_noise: boolean) => { | |
| const upload_ids = []; | |
| for (const f of files) upload_ids.push((await uploadFile(f)).upload_id); | |
| return post<VoiceState & { job: Job }>("/api/voice/consent", { upload_ids, agreed: true, consent_text, remove_background_noise }); | |
| }, | |
| library: (p: { search?: string; gender?: string; age?: string; accent?: string; language?: string; use_case?: string; page?: number }) => | |
| api<{ voices: LibraryVoice[]; has_more: boolean }>(`/api/voice/library${qs(p)}`), | |
| addFromLibrary: (v: LibraryVoice, use_as?: "host_a" | "host_b") => | |
| post<VoiceState & { voice_id: string }>("/api/voice/library/add", { | |
| public_owner_id: v.public_owner_id, | |
| voice_id: v.voice_id, | |
| name: v.name, | |
| use_as, | |
| }), | |
| preview: (voice_id: string, opts: { text?: string; host?: "host_a" | "host_b"; delivery?: Delivery } = {}) => | |
| post<{ url: string; seconds: number }>("/api/voice/preview", { voice_id, ...opts }), | |
| readingScript: () => api<{ title: string; text: string; words: number }>("/api/voice/reading-script"), | |
| quota: () => api<Quota>("/api/voice/quota"), | |
| revoke: () => del<VoiceState>("/api/voice/consent"), | |
| design: (input: VoiceDesignInput) => post<{ description: string; previews: VoiceDesignPreview[] }>("/api/voice/design", input), | |
| saveDesign: (body: { generated_voice_id: string; name: string; description: string; use_as?: "host_a" | "host_b" }) => | |
| post<VoiceState & { voice_id: string }>("/api/voice/design/save", body), | |
| }; | |
| export type ItemBody = { | |
| platform: Platform; | |
| scheduled_at: string; | |
| content: string; | |
| thread?: string[]; | |
| artifact_id?: string | null; | |
| document_id?: string | null; | |
| social_account_id?: string | null; | |
| remind_by_email?: boolean; | |
| draft?: boolean; | |
| }; | |
| export const launchpadApi = { | |
| items: (p: { start?: string; end?: string; status?: string } = {}) => api<CalendarItem[]>(`/api/calendar${qs(p)}`), | |
| create: (body: ItemBody) => post<CalendarItem>("/api/calendar", body), | |
| update: (id: string, body: Partial<ItemBody> & { status?: "scheduled" | "draft" }) => | |
| patch<CalendarItem>(`/api/calendar/${id}`, body), | |
| remove: (id: string) => del(`/api/calendar/${id}`), | |
| publishNow: (id: string) => post<CalendarItem>(`/api/calendar/${id}/publish`), | |
| accounts: () => api<SocialAccounts>("/api/social/accounts"), | |
| connectBluesky: (handle: string, app_password: string) => | |
| post<SocialAccounts>("/api/social/bluesky", { handle, app_password }), | |
| startOAuth: (platform: "x" | "linkedin") => post<{ url: string }>(`/api/social/${platform}/start`), | |
| completeOAuth: (ticket: string) => post<SocialAccounts>("/api/social/complete", { ticket }), | |
| disconnect: (id: string) => del(`/api/social/accounts/${id}`), | |
| }; | |
| export const resurfaceApi = { | |
| get: () => api<{ evergreen: ResurfaceItem[]; on_this_day: ResurfaceItem[]; unscored: number; total_posts: number }>( | |
| "/api/resurface", | |
| ), | |
| scan: () => post<Job>("/api/resurface/scan"), | |
| }; | |
| export type SettingsPatch = { | |
| name?: string; | |
| workspace_name?: string; | |
| brand_name?: string; | |
| brand_accent?: string; | |
| logo_upload_id?: string; | |
| training_opt_in?: boolean; | |
| email_unsubscribed?: boolean; | |
| }; | |
| export const settingsApi = { | |
| get: () => api<Settings>("/api/settings"), | |
| update: (body: SettingsPatch) => patch<Settings>("/api/settings", body), | |
| usage: () => api<Usage>("/api/settings/usage"), | |
| }; | |
| export type BillingCycle = "monthly" | "annual"; | |
| export const billingApi = { | |
| status: () => api<BillingStatus>("/api/billing/status"), | |
| checkout: (plan: string, cycle: BillingCycle) => post<{ url: string }>("/api/billing/checkout", { plan, cycle }), | |
| portal: () => post<{ url: string }>("/api/billing/portal"), | |
| confirm: (sessionId: string) => post<BillingStatus>("/api/billing/confirm", { session_id: sessionId }), | |
| }; | |
| function form(fields: Record<string, string | number | boolean | Blob | null | undefined>): FormData { | |
| const f = new FormData(); | |
| for (const [k, v] of Object.entries(fields)) { | |
| if (v === undefined || v === null) continue; | |
| f.append(k, v instanceof Blob ? v : String(v)); | |
| } | |
| return f; | |
| } | |
| export type ScriptDraft = { title: string; display_text: string; narration_text: string; revision: number; | |
| draft_scenes: { id: number; title: string; display_text: string | null; narration_text: string }[] }; | |
| /** Videos are made by blog2video; the backend proxies every call. Ids here are our artifact ids. */ | |
| export const videosApi = { | |
| config: () => api<VideoConfig>("/api/videos/config"), | |
| quota: () => api<VideoQuota>("/api/videos/quota"), | |
| catalog: () => api<VideoCatalog>("/api/videos/catalog"), | |
| list: () => api<VideoListItem[]>("/api/videos"), | |
| create: (body: VideoCreateBody) => post<Artifact>("/api/videos", body), | |
| /** Three topics the AI suggests a video from this source could focus on. */ | |
| focusTopics: (body: VideoFocusSource) => post<{ topics: VideoFocusTopic[] }>("/api/videos/focus-topics", body), | |
| /** Multi-link: one video per link. Items with `error` say where it stopped (usually the allowance ran out). */ | |
| createBatch: (urls: string[], options: VideoOptions) => | |
| post<(Artifact | { error: string; url: string })[]>("/api/videos/batch", { ...options, urls }), | |
| /** Up to 5 documents; blog2video reads them itself. */ | |
| createFromFiles: (files: File[], options: VideoOptions) => { | |
| const f = new FormData(); | |
| files.forEach((file) => f.append("files", file)); | |
| f.append("options", JSON.stringify(options)); | |
| return postForm<Artifact>("/api/videos/upload", f); | |
| }, | |
| get: (id: string) => api<VideoProject | LegacyVideo>(`/api/videos/${id}`), | |
| status: (id: string) => api<VideoStatus>(`/api/videos/${id}/status`), | |
| remove: (id: string) => del(`/api/videos/${id}`), | |
| // Script review | |
| script: (id: string) => api<{ status: string; scenes: ScriptScene[] }>(`/api/videos/${id}/script`), | |
| narrationPreview: (id: string, sceneId: number, body: ScriptDraft) => | |
| post<ScriptPreview>(`/api/videos/${id}/script/scenes/${sceneId}/narration-preview`, body), | |
| aiPreview: (id: string, sceneId: number, body: ScriptDraft & { instruction: string }) => | |
| post<ScriptPreview>(`/api/videos/${id}/script/scenes/${sceneId}/ai-preview`, body), | |
| approveScript: (id: string, scenes: (ScriptScene & { source_fingerprint?: string; accepted_ai_instructions?: string[] })[]) => | |
| post<unknown>(`/api/videos/${id}/script/approve`, { scenes }), | |
| }; | |
| const P = (id: string, path: string) => `/api/videos/${id}/p/${path}`; | |
| export type LayoutSchema = Record<string, { defaults?: Record<string, unknown>; fields?: Record<string, unknown>[] }>; | |
| /** The template's layouts, from blog2video's GET .../layouts. Variants ("news_headline__v2") are visual styles of a base | |
| * layout: layout_variants maps a base to its variant ids, layout_variant_labels names each ("Broadsheet"). */ | |
| export type LayoutInfo = { | |
| layouts: string[]; | |
| layout_names?: Record<string, string>; | |
| layout_prop_schema?: LayoutSchema; | |
| layout_variants?: Record<string, string[]>; | |
| layout_variant_labels?: Record<string, string>; | |
| }; | |
| export type StockClip = { provider: string; id: string; preview_url: string; thumbnail_url: string; download_url: string; | |
| width: number; height: number; duration: number; author: string; page_url: string }; | |
| /** The editor: blog2video's project endpoints, through our allowlisted proxy. */ | |
| export const videoEditApi = { | |
| project: (id: string) => api<VideoProjectData>(P(id, "")), | |
| updateProject: (id: string, body: Partial<VideoProjectData>) => patch<VideoProjectData>(P(id, "update-project"), body), | |
| // Scenes | |
| updateScene: (id: string, sceneId: number, body: Partial<Pick<VideoScene, "title" | "display_text" | "narration_text" | | |
| "visual_description" | "duration_seconds" | "extra_hold_seconds" | "remotion_code">>) => | |
| put<VideoScene>(P(id, `scenes/${sceneId}`), body), | |
| deleteScene: (id: string, sceneId: number) => del<unknown>(P(id, `scenes/${sceneId}`)), | |
| reorderScenes: (id: string, scene_orders: { scene_id: number; order: number }[]) => | |
| post<VideoScene[]>(P(id, "scenes/reorder"), { scene_orders }), | |
| regenerateScene: (id: string, sceneId: number, body: { description?: string; narration_text?: string; | |
| regenerate_voiceover?: boolean; voiceover_verbatim?: boolean; layout?: string; image?: File }) => | |
| postForm<VideoScene>(P(id, `scenes/${sceneId}/regenerate`), form(body)), | |
| addScene: (id: string, prompt: string, position?: number) => post<VideoJobState>(P(id, "scenes/add"), { prompt, position }), | |
| addSceneStatus: (id: string) => api<VideoJobState | null>(P(id, "scenes/add-status")), | |
| // Images | |
| uploadImage: (id: string, sceneId: number, image: File) => postForm<VideoScene>(P(id, `scenes/${sceneId}/image`), form({ image })), | |
| generateImage: (id: string, sceneId: number, image_description: string) => | |
| // Not saved on the scene: the image comes back for the user to keep (uploadImage) or discard. | |
| post<{ image_base64: string; refined_prompt: string }>(P(id, `scenes/${sceneId}/generate-image`), { image_description }), | |
| imageFocus: (id: string, sceneId: number, body: { image_focus_x: number; image_focus_y: number; image_zoom?: number }) => | |
| patch<VideoScene>(P(id, `scenes/${sceneId}/image-focus`), body), | |
| moveImage: (id: string, from_scene_id: number, to_scene_id: number) => | |
| post<unknown>(P(id, "images/move"), { from_scene_id, to_scene_id }), | |
| swapImages: (id: string, first_scene_id: number, second_scene_id: number) => | |
| post<unknown>(P(id, "images/swap"), { first_scene_id, second_scene_id }), | |
| duplicateImage: (id: string, source_scene_id: number, target_scene_id: number) => | |
| post<unknown>(P(id, "images/duplicate"), { source_scene_id, target_scene_id }), | |
| /** Deletes the image or clip from the video for good (files too) and takes it off any scene. */ | |
| /** The template's layouts; layout_prop_schema[layout].defaults holds its default font sizes (meta.json). */ | |
| layouts: (id: string) => api<LayoutInfo>(P(id, "layouts")), | |
| deleteAsset: (id: string, assetId: number) => del<unknown>(P(id, `assets/${assetId}`)), | |
| assignImage: (id: string, scene_id: number, asset_id: number) => | |
| post<unknown>(P(id, "images/assign-existing"), { scene_id, asset_id }), | |
| // Stock footage | |
| searchStock: (id: string, q: string) => api<{ clips: StockClip[] }>(P(id, `stock-footage/search${qs({ q })}`)), | |
| /** Downloads the clip into the project (a new asset). It is not on the scene until linkStock. */ | |
| useStock: (id: string, sceneId: number, c: StockClip) => | |
| post<{ asset_id: number; filename: string }>(P(id, `scenes/${sceneId}/stock-footage`), { provider: c.provider, clip_id: c.id, | |
| download_url: c.download_url, width: c.width, height: c.height, duration: c.duration, author: c.author, | |
| page_url: c.page_url }), | |
| /** Put a clip that is already in the project on a scene (assignedVideo). */ | |
| linkStock: (id: string, scene_id: number, filename: string) => | |
| post<unknown>(P(id, "stock-footage/link"), { scene_id, filename }), | |
| pendingStock: (id: string) => api<{ scenes?: unknown[]; [k: string]: unknown }>(P(id, "stock-footage/pending")), | |
| approveStock: (id: string) => post<unknown>(P(id, "stock-footage/approve")), | |
| rejectStock: (id: string) => post<unknown>(P(id, "stock-footage/reject")), | |
| // Voice, voiceovers, language, template | |
| uploadVoiceover: (id: string, sceneId: number, audio: File) => | |
| postForm<VideoScene>(P(id, `scenes/${sceneId}/voiceover`), form({ audio })), | |
| deleteVoiceover: (id: string) => post<unknown>(P(id, "delete-voiceover")), | |
| changeVoice: (id: string, body: { voice_gender?: string; voice_accent?: string; custom_voice_id?: string | null; | |
| voice_emotion?: string | null }) => post<VideoJobState>(P(id, "change-voice"), body), | |
| voiceStatus: (id: string) => api<VideoJobState>(P(id, "voice-change-status")), | |
| changeLanguage: (id: string, content_language: string) => post<VideoJobState>(P(id, "change-language"), { content_language }), | |
| languageStatus: (id: string) => api<VideoJobState>(P(id, "language-change-status")), | |
| changeTemplate: (id: string, template: string) => | |
| post<VideoJobState>(P(id, "change-template-regenerate-layouts"), { template }), | |
| templateStatus: (id: string) => api<VideoJobState>(P(id, "template-change-status")), | |
| // Refresh the script (keeps narration and voiceovers, rewrites titles and layouts) | |
| refreshScript: (id: string, user_instruction?: string) => post<VideoJobState>(P(id, "regenerate-script"), { user_instruction }), | |
| refreshStatus: (id: string) => api<VideoJobState>(P(id, "regenerate-script-status")), | |
| refreshPreview: (id: string) => api<Record<string, unknown>>(P(id, "regenerate-script/preview")), | |
| refreshVerify: (id: string) => post<unknown>(P(id, "regenerate-script/verify")), | |
| refreshRetry: (id: string, user_instruction?: string) => | |
| post<VideoJobState>(P(id, "regenerate-script/regenerate"), { user_instruction }), | |
| // AI chat editing ★ | |
| chat: (id: string, message: string, conversation_id?: number) => | |
| post<{ reply?: string; message?: string; conversation_id?: number; [k: string]: unknown }>(P(id, "chat"), { message, conversation_id }), | |
| chatHistory: (id: string) => api<unknown>(P(id, "chat/history")), | |
| // Avatars ★ | |
| authorizeAvatars: (id: string, scene_ids: number[], avatar_preset?: string, avatar_motion_style?: string) => | |
| post<unknown>(P(id, "avatar-batch/authorize"), { scene_ids, avatar_preset, avatar_motion_style }), | |
| makeAvatar: (id: string, sceneId: number, avatar_preset?: string) => | |
| postForm<unknown>(P(id, `scenes/${sceneId}/avatar`), form({ avatar_preset })), | |
| avatarProgress: (id: string) => api<Record<string, unknown>>(P(id, "avatar-progress")), | |
| deleteAvatar: (id: string, sceneId: number) => del<unknown>(P(id, `scenes/${sceneId}/avatar`)), | |
| uploadPortrait: (id: string, file: File) => postForm<unknown>(P(id, "avatar-portrait"), form({ file })), | |
| deletePortrait: (id: string) => del<unknown>(P(id, "avatar-portrait")), | |
| avatarAppearance: (id: string, sceneId: number, body: { avatar_shape?: string; avatar_size?: number; | |
| avatar_position?: string; avatar_opacity?: number }) => patch<unknown>(P(id, `scenes/${sceneId}/avatar-appearance`), body), | |
| // Render, download, frames | |
| /** force renders again even when an MP4 is already on R2 (blog2video otherwise just returns the old one). */ | |
| render: (id: string, { force = false, resolution = "1080p" }: { force?: boolean; resolution?: string } = {}) => | |
| post<VideoJobState>(P(id, `render${qs({ resolution, force_render: force || undefined })}`)), | |
| renderStatus: (id: string) => api<VideoJobState>(P(id, "render-status")), | |
| cancelRender: (id: string) => post<unknown>(P(id, "cancel-render")), | |
| downloadUrl: (id: string) => api<{ url: string }>(P(id, "download-url")), | |
| downloadStudio: (id: string) => apiBlob(P(id, "download-studio")), | |
| still: (id: string, frame: number) => apiBlob(P(id, `render-still${qs({ frame })}`)), | |
| }; | |
| export const videoVoicesApi = { | |
| list: () => api<VideoVoicesResponse>("/api/video-voices"), | |
| save: (voice_id: string) => post<VideoSavedVoice>("/api/video-voices/saved", { voice_id }), | |
| unsave: (voice_id: string) => del(`/api/video-voices/saved/${encodeURIComponent(voice_id)}`), | |
| designFromPrompt: (prompt: string) => post<{ previews: VideoDesignedVoice[] }>("/api/video-voices/design/prompt", { prompt }), | |
| designFromPreset: (body: { gender: string; age: string; persona: string; speed: string; accent: string }) => | |
| post<{ previews: VideoDesignedVoice[] }>("/api/video-voices/design/preset", body), | |
| keep: (body: { generated_voice_id: string; source: "prompt" | "preset"; name: string; prompt_text?: string }) => | |
| post<VideoCustomVoice>("/api/video-voices/custom", body), | |
| clone: (name: string, file: File, remove_background_noise = true) => | |
| postForm<VideoCustomVoice>("/api/video-voices/clone", form({ name, file, remove_background_noise })), | |
| customPreview: (customId: number) => | |
| api<{ preview_url: string | null; ready: boolean }>(`/api/video-voices/custom/${customId}/preview`), | |
| removeCustom: (customId: number) => del(`/api/video-voices/custom/${customId}`), | |
| /** ★ A few seconds of the voice with the chosen tuning (audio/mpeg). */ | |
| sample: (body: { voice_gender: string; voice_accent: string; custom_voice_id?: string; voice_emotion?: string; | |
| video_style?: string }) => apiBlob("/api/video-voices/sample", { method: "POST", body: JSON.stringify(body) }), | |
| }; | |
| export const videoStylesApi = { | |
| list: () => api<VideoStyleItem[]>("/api/video-styles"), | |
| aiDraft: (prompt: string) => post<{ name: string; guidance: string }>("/api/video-styles/ai-draft", { prompt }), | |
| create: (body: { name: string; guidance: string; creation_method: "manual" | "ai"; source_prompt?: string }) => | |
| post<VideoStyleItem>("/api/video-styles", body), | |
| update: (styleId: number, body: { name: string; guidance: string; version: number }) => | |
| patch<VideoStyleItem>(`/api/video-styles/${styleId}`, body), | |
| remove: (styleId: number) => del(`/api/video-styles/${styleId}`), | |
| }; | |
| const T = (id: number, path: string) => `/api/video-templates/${id}/p/${path}`; | |
| export const videoTemplatesApi = { | |
| list: () => api<TemplatesResponse>("/api/video-templates"), | |
| extractFromUrl: (url: string) => post<ExtractedTheme>("/api/video-templates/extract/url", { url }), | |
| extractFromPrompt: (prompt: string, name?: string) => | |
| post<ExtractedTheme>("/api/video-templates/extract/prompt", { prompt, name }), | |
| extractFromDoc: (file: File, name: string) => postForm<ExtractedTheme>("/api/video-templates/extract/doc", form({ file, name })), | |
| create: (body: { name: string; theme: Record<string, unknown>; source_url?: string; logo_urls?: string[]; | |
| og_image?: string; screenshot_url?: string }) => post<MyTemplate>("/api/video-templates", body), | |
| generate: (id: number) => post<unknown>(`/api/video-templates/${id}/generate`), | |
| generationStatus: (id: number) => api<TemplateGeneration>(`/api/video-templates/${id}/generation-status`), | |
| remove: (id: number) => del(`/api/video-templates/${id}`), | |
| // Editing | |
| get: (id: number) => api<Record<string, unknown>>(T(id, "")), | |
| update: (id: number, body: { name?: string; theme?: Record<string, unknown> }) => put<Record<string, unknown>>(T(id, ""), body), | |
| uploadLogo: (id: number, file: File) => postForm<unknown>(T(id, "upload-logo"), form({ file })), | |
| aiEdit: (id: number, sceneKey: string, prompt: string) => post<unknown>(T(id, `scenes/${sceneKey}/ai-edit`), { prompt }), | |
| aiEditStatus: (id: number, sceneKey: string) => api<VideoJobState>(T(id, `scenes/${sceneKey}/ai-edit/status`)), | |
| drafts: (id: number) => api<unknown>(T(id, "scene-drafts")), | |
| applyDraft: (id: number, sceneKey: string) => post<unknown>(T(id, `scenes/${sceneKey}/draft/apply`)), | |
| discardDraft: (id: number, sceneKey: string) => post<unknown>(T(id, `scenes/${sceneKey}/draft/discard`)), | |
| fontDefaults: (id: number, body: Record<string, unknown>, sceneKey?: string) => | |
| patch<unknown>(T(id, sceneKey ? `scenes/${sceneKey}/font-defaults` : "scenes/font-defaults"), body), | |
| chart: (id: number, sceneKey: string, body: Record<string, unknown>) => patch<unknown>(T(id, `scenes/${sceneKey}/chart`), body), | |
| versions: (id: number) => api<unknown>(T(id, "versions")), | |
| rollback: (id: number, versionId: number) => post<unknown>(T(id, `versions/${versionId}/rollback`)), | |
| regenerate: (id: number) => post<unknown>(T(id, "regenerate-code")), | |
| resume: (id: number) => post<unknown>(T(id, "resume-generation")), | |
| rate: (id: number, body: Record<string, unknown>) => post<unknown>(T(id, "rating"), body), | |
| }; | |