74f146bbaf
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 40s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 52s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 56s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m21s
CI/CD Pipeline / Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build API Image (pull_request) Has been skipped
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 2m9s
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Successful in 47s
AI Code Review / AI Code Review (pull_request) Successful in 2m22s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 1m47s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m20s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 1m50s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 4m54s
CI/CD Pipeline / Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Canary Release to Production (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / CI Gate (pull_request) Successful in 6s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 38s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 49s
- API层新增VoiceClonePreviewResponse类型和getVoiceClonePreview函数 - useMyVoices hook播放逻辑改为先调接口获取音频URL再播放 - 新增loadingPreviewId状态,试听合成中显示加载态 - VoiceCard增加isLoading prop,加载时按钮显示"合成中…"并禁用 - 错误处理:接口失败Toast提示,播放失败友好提示
103 lines
3.6 KiB
TypeScript
103 lines
3.6 KiB
TypeScript
/**
|
|
* 音色克隆 API 函数
|
|
*/
|
|
import apiClient from "../client"
|
|
import { toVoiceClone } from "./utils"
|
|
import type {
|
|
VoiceClone,
|
|
VoiceCloneProfile,
|
|
CreateVoiceCloneRequest,
|
|
CreateVoiceCloneRequestFull,
|
|
VoiceCloneListParams,
|
|
ListVoiceCloneResponse,
|
|
VoiceCloneStatusResponse,
|
|
VoiceClonePreviewResponse,
|
|
} from "./types"
|
|
|
|
/** 获取克隆音色列表(返回前端兼容数组) */
|
|
export const getVoiceClones = async (params?: VoiceCloneListParams): Promise<VoiceClone[]> => {
|
|
const searchParams = new URLSearchParams()
|
|
if (params?.status) searchParams.set("status", params.status)
|
|
if (params?.skip !== undefined) searchParams.set("skip", String(params.skip))
|
|
if (params?.limit !== undefined) searchParams.set("limit", String(params.limit))
|
|
const qs = searchParams.toString()
|
|
const response = await apiClient.get<ListVoiceCloneResponse>(`/voice-clones${qs ? `?${qs}` : ""}`)
|
|
return response.data.items.map(toVoiceClone)
|
|
}
|
|
|
|
/** 获取克隆音色列表(返回完整响应含 total) */
|
|
export const getVoiceClonesWithTotal = async (
|
|
params?: VoiceCloneListParams,
|
|
): Promise<ListVoiceCloneResponse> => {
|
|
const searchParams = new URLSearchParams()
|
|
if (params?.status) searchParams.set("status", params.status)
|
|
if (params?.skip !== undefined) searchParams.set("skip", String(params.skip))
|
|
if (params?.limit !== undefined) searchParams.set("limit", String(params.limit))
|
|
const qs = searchParams.toString()
|
|
const response = await apiClient.get<ListVoiceCloneResponse>(`/voice-clones${qs ? `?${qs}` : ""}`)
|
|
return response.data
|
|
}
|
|
|
|
/** 获取单个克隆音色详情 */
|
|
export const getVoiceCloneDetail = async (id: string): Promise<VoiceCloneProfile> => {
|
|
const response = await apiClient.get<VoiceCloneProfile>(`/voice-clones/${id}`)
|
|
return response.data
|
|
}
|
|
|
|
/** 创建克隆音色 */
|
|
export const createVoiceClone = async (
|
|
data: CreateVoiceCloneRequest,
|
|
): Promise<VoiceCloneProfile> => {
|
|
const payload: CreateVoiceCloneRequestFull = {
|
|
name: data.name,
|
|
description: data.description,
|
|
source_audio_url: data.audio_url,
|
|
}
|
|
const response = await apiClient.post<VoiceCloneProfile>("/voice-clones", payload)
|
|
return response.data
|
|
}
|
|
|
|
/** 删除克隆音色 */
|
|
export const deleteVoiceClone = async (id: string): Promise<void> => {
|
|
await apiClient.delete(`/voice-clones/${id}`)
|
|
}
|
|
|
|
/** 更新克隆音色名称(stub — 后端暂无 PATCH 端点) */
|
|
export const updateVoiceClone = async (
|
|
id: string,
|
|
data: Partial<Pick<VoiceClone, "name">>,
|
|
): Promise<VoiceClone> => {
|
|
const response = await apiClient.get<VoiceCloneProfile>(`/voice-clones/${id}`)
|
|
return toVoiceClone({
|
|
...response.data,
|
|
...data,
|
|
updated_at: new Date().toISOString(),
|
|
})
|
|
}
|
|
|
|
/** 获取克隆状态 */
|
|
export const getVoiceCloneStatus = async (id: string): Promise<VoiceCloneStatusResponse> => {
|
|
const response = await apiClient.get<VoiceCloneStatusResponse>(`/voice-clones/${id}/status`)
|
|
return response.data
|
|
}
|
|
|
|
/** 重试克隆 */
|
|
export const retryVoiceClone = async (id: string): Promise<VoiceCloneProfile> => {
|
|
const response = await apiClient.post<VoiceCloneProfile>(`/voice-clones/${id}/retry`)
|
|
return response.data
|
|
}
|
|
|
|
/** 获取克隆音色试听音频(实时 TTS 合成) */
|
|
export const getVoiceClonePreview = async (
|
|
cloneId: string,
|
|
text?: string,
|
|
): Promise<VoiceClonePreviewResponse> => {
|
|
const searchParams = new URLSearchParams()
|
|
if (text) searchParams.set("text", text)
|
|
const qs = searchParams.toString()
|
|
const response = await apiClient.get<VoiceClonePreviewResponse>(
|
|
`/voice-clones/${cloneId}/preview${qs ? `?${qs}` : ""}`,
|
|
)
|
|
return response.data
|
|
}
|