mirror of
https://github.com/jamiepine/voicebox.git
synced 2026-09-17 05:40:42 -07:00
237 lines
6.6 KiB
TypeScript
237 lines
6.6 KiB
TypeScript
import { useServerStore } from '@/stores/serverStore';
|
|
import type {
|
|
VoiceProfileCreate,
|
|
VoiceProfileResponse,
|
|
ProfileSampleResponse,
|
|
GenerationRequest,
|
|
GenerationResponse,
|
|
HistoryQuery,
|
|
HistoryListResponse,
|
|
HistoryResponse,
|
|
TranscriptionResponse,
|
|
HealthResponse,
|
|
ModelStatusListResponse,
|
|
ModelDownloadRequest,
|
|
ActiveTasksResponse,
|
|
} from './types';
|
|
|
|
class ApiClient {
|
|
private getBaseUrl(): string {
|
|
const serverUrl = useServerStore.getState().serverUrl;
|
|
return serverUrl;
|
|
}
|
|
|
|
private async request<T>(endpoint: string, options?: RequestInit): Promise<T> {
|
|
const url = `${this.getBaseUrl()}${endpoint}`;
|
|
const response = await fetch(url, {
|
|
...options,
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
...options?.headers,
|
|
},
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const error = await response.json().catch(() => ({
|
|
detail: response.statusText,
|
|
}));
|
|
throw new Error(error.detail || `HTTP error! status: ${response.status}`);
|
|
}
|
|
|
|
return response.json();
|
|
}
|
|
|
|
// Health
|
|
async getHealth(): Promise<HealthResponse> {
|
|
return this.request<HealthResponse>('/health');
|
|
}
|
|
|
|
// Profiles
|
|
async createProfile(data: VoiceProfileCreate): Promise<VoiceProfileResponse> {
|
|
return this.request<VoiceProfileResponse>('/profiles', {
|
|
method: 'POST',
|
|
body: JSON.stringify(data),
|
|
});
|
|
}
|
|
|
|
async listProfiles(): Promise<VoiceProfileResponse[]> {
|
|
return this.request<VoiceProfileResponse[]>('/profiles');
|
|
}
|
|
|
|
async getProfile(profileId: string): Promise<VoiceProfileResponse> {
|
|
return this.request<VoiceProfileResponse>(`/profiles/${profileId}`);
|
|
}
|
|
|
|
async updateProfile(profileId: string, data: VoiceProfileCreate): Promise<VoiceProfileResponse> {
|
|
return this.request<VoiceProfileResponse>(`/profiles/${profileId}`, {
|
|
method: 'PUT',
|
|
body: JSON.stringify(data),
|
|
});
|
|
}
|
|
|
|
async deleteProfile(profileId: string): Promise<void> {
|
|
await this.request<void>(`/profiles/${profileId}`, {
|
|
method: 'DELETE',
|
|
});
|
|
}
|
|
|
|
async addProfileSample(
|
|
profileId: string,
|
|
file: File,
|
|
referenceText: string,
|
|
): Promise<ProfileSampleResponse> {
|
|
const url = `${this.getBaseUrl()}/profiles/${profileId}/samples`;
|
|
const formData = new FormData();
|
|
formData.append('file', file);
|
|
formData.append('reference_text', referenceText);
|
|
|
|
const response = await fetch(url, {
|
|
method: 'POST',
|
|
body: formData,
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const error = await response.json().catch(() => ({
|
|
detail: response.statusText,
|
|
}));
|
|
throw new Error(error.detail || `HTTP error! status: ${response.status}`);
|
|
}
|
|
|
|
return response.json();
|
|
}
|
|
|
|
async listProfileSamples(profileId: string): Promise<ProfileSampleResponse[]> {
|
|
return this.request<ProfileSampleResponse[]>(`/profiles/${profileId}/samples`);
|
|
}
|
|
|
|
async deleteProfileSample(sampleId: string): Promise<void> {
|
|
await this.request<void>(`/profiles/samples/${sampleId}`, {
|
|
method: 'DELETE',
|
|
});
|
|
}
|
|
|
|
async exportProfile(profileId: string): Promise<Blob> {
|
|
const url = `${this.getBaseUrl()}/profiles/${profileId}/export`;
|
|
const response = await fetch(url);
|
|
|
|
if (!response.ok) {
|
|
const error = await response.json().catch(() => ({
|
|
detail: response.statusText,
|
|
}));
|
|
throw new Error(error.detail || `HTTP error! status: ${response.status}`);
|
|
}
|
|
|
|
return response.blob();
|
|
}
|
|
|
|
async importProfile(file: File): Promise<VoiceProfileResponse> {
|
|
const url = `${this.getBaseUrl()}/profiles/import`;
|
|
const formData = new FormData();
|
|
formData.append('file', file);
|
|
|
|
const response = await fetch(url, {
|
|
method: 'POST',
|
|
body: formData,
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const error = await response.json().catch(() => ({
|
|
detail: response.statusText,
|
|
}));
|
|
throw new Error(error.detail || `HTTP error! status: ${response.status}`);
|
|
}
|
|
|
|
return response.json();
|
|
}
|
|
|
|
// Generation
|
|
async generateSpeech(data: GenerationRequest): Promise<GenerationResponse> {
|
|
return this.request<GenerationResponse>('/generate', {
|
|
method: 'POST',
|
|
body: JSON.stringify(data),
|
|
});
|
|
}
|
|
|
|
// History
|
|
async listHistory(query?: HistoryQuery): Promise<HistoryListResponse> {
|
|
const params = new URLSearchParams();
|
|
if (query?.profile_id) params.append('profile_id', query.profile_id);
|
|
if (query?.search) params.append('search', query.search);
|
|
if (query?.limit) params.append('limit', query.limit.toString());
|
|
if (query?.offset) params.append('offset', query.offset.toString());
|
|
|
|
const queryString = params.toString();
|
|
const endpoint = queryString ? `/history?${queryString}` : '/history';
|
|
|
|
return this.request<HistoryListResponse>(endpoint);
|
|
}
|
|
|
|
async getGeneration(generationId: string): Promise<HistoryResponse> {
|
|
return this.request<HistoryResponse>(`/history/${generationId}`);
|
|
}
|
|
|
|
async deleteGeneration(generationId: string): Promise<void> {
|
|
await this.request<void>(`/history/${generationId}`, {
|
|
method: 'DELETE',
|
|
});
|
|
}
|
|
|
|
// Audio
|
|
getAudioUrl(audioId: string): string {
|
|
return `${this.getBaseUrl()}/audio/${audioId}`;
|
|
}
|
|
|
|
getSampleUrl(sampleId: string): string {
|
|
return `${this.getBaseUrl()}/samples/${sampleId}`;
|
|
}
|
|
|
|
// Transcription
|
|
async transcribeAudio(file: File, language?: 'en' | 'zh'): Promise<TranscriptionResponse> {
|
|
const formData = new FormData();
|
|
formData.append('file', file);
|
|
if (language) {
|
|
formData.append('language', language);
|
|
}
|
|
|
|
const url = `${this.getBaseUrl()}/transcribe`;
|
|
const response = await fetch(url, {
|
|
method: 'POST',
|
|
body: formData,
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const error = await response.json().catch(() => ({
|
|
detail: response.statusText,
|
|
}));
|
|
throw new Error(error.detail || `HTTP error! status: ${response.status}`);
|
|
}
|
|
|
|
return response.json();
|
|
}
|
|
|
|
// Model Management
|
|
async getModelStatus(): Promise<ModelStatusListResponse> {
|
|
return this.request<ModelStatusListResponse>('/models/status');
|
|
}
|
|
|
|
async triggerModelDownload(modelName: string): Promise<{ message: string }> {
|
|
return this.request<{ message: string }>('/models/download', {
|
|
method: 'POST',
|
|
body: JSON.stringify({ model_name: modelName } as ModelDownloadRequest),
|
|
});
|
|
}
|
|
|
|
async deleteModel(modelName: string): Promise<{ message: string }> {
|
|
return this.request<{ message: string }>(`/models/${modelName}`, {
|
|
method: 'DELETE',
|
|
});
|
|
}
|
|
|
|
// Task Management
|
|
async getActiveTasks(): Promise<ActiveTasksResponse> {
|
|
return this.request<ActiveTasksResponse>('/tasks/active');
|
|
}
|
|
}
|
|
|
|
export const apiClient = new ApiClient();
|