117 lines
3.8 KiB
TypeScript
117 lines
3.8 KiB
TypeScript
import { useCallback, useState } from "react";
|
|
import api from "@/shared/api/axios";
|
|
import { ApiResponse, unwrap } from "@/shared/api/types";
|
|
import { toast } from "sonner";
|
|
|
|
interface RefAudio {
|
|
id: string;
|
|
name: string;
|
|
path: string;
|
|
ref_text: string;
|
|
duration_sec: number;
|
|
created_at: number;
|
|
}
|
|
|
|
interface UseRefAudiosOptions {
|
|
selectedRefAudio: RefAudio | null;
|
|
setSelectedRefAudio: React.Dispatch<React.SetStateAction<RefAudio | null>>;
|
|
setRefText: React.Dispatch<React.SetStateAction<string>>;
|
|
}
|
|
|
|
export const useRefAudios = ({
|
|
selectedRefAudio,
|
|
setSelectedRefAudio,
|
|
setRefText,
|
|
}: UseRefAudiosOptions) => {
|
|
const [refAudios, setRefAudios] = useState<RefAudio[]>([]);
|
|
const [isUploadingRef, setIsUploadingRef] = useState(false);
|
|
const [uploadRefError, setUploadRefError] = useState<string | null>(null);
|
|
const [retranscribingId, setRetranscribingId] = useState<string | null>(null);
|
|
|
|
const fetchRefAudios = useCallback(async () => {
|
|
try {
|
|
const { data: res } = await api.get<ApiResponse<{ items: RefAudio[] }>>('/api/ref-audios');
|
|
const payload = unwrap(res);
|
|
const items: RefAudio[] = payload.items || [];
|
|
items.sort((a, b) => b.created_at - a.created_at);
|
|
setRefAudios(items);
|
|
} catch (error) {
|
|
console.error("获取参考音频失败:", error);
|
|
}
|
|
}, []);
|
|
|
|
const uploadRefAudio = useCallback(async (file: File) => {
|
|
setIsUploadingRef(true);
|
|
setUploadRefError(null);
|
|
|
|
try {
|
|
const formData = new FormData();
|
|
formData.append('file', file);
|
|
|
|
const { data: res } = await api.post<ApiResponse<RefAudio>>('/api/ref-audios', formData, {
|
|
headers: { 'Content-Type': 'multipart/form-data' },
|
|
});
|
|
const payload = unwrap(res);
|
|
|
|
await fetchRefAudios();
|
|
setSelectedRefAudio(payload);
|
|
setRefText(payload.ref_text);
|
|
setIsUploadingRef(false);
|
|
} catch (err: unknown) {
|
|
console.error("Upload ref audio failed:", err);
|
|
setIsUploadingRef(false);
|
|
const axiosErr = err as { response?: { data?: { message?: string } }; message?: string };
|
|
const errorMsg = axiosErr.response?.data?.message || axiosErr.message || String(err);
|
|
setUploadRefError(`上传失败: ${errorMsg}`);
|
|
}
|
|
}, [fetchRefAudios, setRefText, setSelectedRefAudio]);
|
|
|
|
const deleteRefAudio = useCallback(async (audioId: string) => {
|
|
if (!confirm("确定要删除这个参考音频吗?")) return;
|
|
try {
|
|
await api.delete(`/api/ref-audios/${encodeURIComponent(audioId)}`);
|
|
fetchRefAudios();
|
|
if (selectedRefAudio?.id === audioId) {
|
|
setSelectedRefAudio(null);
|
|
setRefText('');
|
|
}
|
|
} catch (error) {
|
|
toast.error("删除失败: " + error);
|
|
}
|
|
}, [fetchRefAudios, selectedRefAudio, setRefText, setSelectedRefAudio]);
|
|
|
|
const retranscribeRefAudio = useCallback(async (audioId: string) => {
|
|
setRetranscribingId(audioId);
|
|
try {
|
|
const { data: res } = await api.post<ApiResponse<{ ref_text: string }>>(
|
|
`/api/ref-audios/${encodeURIComponent(audioId)}/retranscribe`
|
|
);
|
|
const payload = unwrap(res);
|
|
toast.success("识别完成");
|
|
// 更新列表和当前选中
|
|
await fetchRefAudios();
|
|
if (selectedRefAudio?.id === audioId) {
|
|
setRefText(payload.ref_text);
|
|
}
|
|
} catch (err: unknown) {
|
|
const axiosErr = err as { response?: { data?: { message?: string } }; message?: string };
|
|
const errorMsg = axiosErr.response?.data?.message || axiosErr.message || String(err);
|
|
toast.error(`识别失败: ${errorMsg}`);
|
|
} finally {
|
|
setRetranscribingId(null);
|
|
}
|
|
}, [fetchRefAudios, selectedRefAudio, setRefText]);
|
|
|
|
return {
|
|
refAudios,
|
|
isUploadingRef,
|
|
uploadRefError,
|
|
setUploadRefError,
|
|
fetchRefAudios,
|
|
uploadRefAudio,
|
|
deleteRefAudio,
|
|
retranscribeRefAudio,
|
|
retranscribingId,
|
|
};
|
|
};
|