diff --git a/frontend/app/web-gold/src/api/kling.js b/frontend/app/web-gold/src/api/kling.js index 9758bfc589..ff4eb3abbb 100644 --- a/frontend/app/web-gold/src/api/kling.js +++ b/frontend/app/web-gold/src/api/kling.js @@ -2,51 +2,10 @@ * 可灵数字人 API */ import request from './http' -import { MaterialService } from './material' - -// ========== 辅助函数 ========== /** - * 执行人脸识别并返回结果 + * 人脸识别 */ -async function performFaceIdentification(videoUrl) { - const identifyRes = await identifyFace({ video_url: videoUrl }) - if (identifyRes.code !== 0) { - throw new Error(identifyRes.msg || '识别失败') - } - - const faceData = identifyRes.data.data?.face_data?.[0] - return { - sessionId: identifyRes.data.sessionId, - faceId: faceData?.face_id || null, - startTime: faceData?.start_time || 0, - endTime: faceData?.end_time || 0 - } -} - -/** - * 构建标准响应格式 - */ -function buildIdentifyResponse(fileId, videoUrl, identifyData, isUploadedFile = false) { - return { - success: true, - data: { - fileId, - videoUrl, - sessionId: identifyData.sessionId, - faceId: identifyData.faceId, - startTime: isUploadedFile - ? Math.round(identifyData.startTime * 1000) - : identifyData.startTime, - endTime: isUploadedFile - ? Math.round(identifyData.endTime * 1000) - : identifyData.endTime - } - } -} - -// ========== API 方法 ========== - export function identifyFace(data) { return request({ url: '/webApi/api/tik/kling/identify-face', @@ -55,6 +14,9 @@ export function identifyFace(data) { }) } +/** + * 创建口型同步任务 + */ export function createLipSyncTask(data) { return request({ url: '/webApi/api/tik/kling/task/create', @@ -63,43 +25,12 @@ export function createLipSyncTask(data) { }) } +/** + * 获取口型同步任务状态 + */ export function getLipSyncTask(taskId) { return request({ url: `/webApi/api/tik/kling/lip-sync/${taskId}`, method: 'get' }) } - -/** - * 识别已上传的视频 - */ -export async function identifyUploadedVideo(videoFile) { - const urlRes = await MaterialService.getVideoPlayUrl(videoFile.fileId) - if (urlRes.code !== 0 || !urlRes.data) { - throw new Error(urlRes.msg || '获取播放链接失败') - } - - const identifyData = await performFaceIdentification(urlRes.data) - return buildIdentifyResponse(videoFile.id, urlRes.data, identifyData, false) -} - -/** - * 上传视频并识别 - */ -export async function uploadAndIdentifyVideo(file) { - const uploadRes = await MaterialService.uploadFile(file, 'digital_human', null, null) - if (uploadRes.code !== 0) { - throw new Error(uploadRes.msg || '上传失败') - } - - const fileId = uploadRes.data - - const urlRes = await MaterialService.getVideoPlayUrl(fileId) - if (urlRes.code !== 0) { - throw new Error(urlRes.msg || '获取播放链接失败') - } - - const identifyData = await performFaceIdentification(urlRes.data) - return buildIdentifyResponse(fileId, urlRes.data, identifyData, true) -} - diff --git a/frontend/app/web-gold/src/api/material.js b/frontend/app/web-gold/src/api/material.js index 879a85d9a1..b4c440aceb 100644 --- a/frontend/app/web-gold/src/api/material.js +++ b/frontend/app/web-gold/src/api/material.js @@ -9,38 +9,6 @@ import { API_BASE } from '@gold/config/api' // 使用 webApi 前缀,确保能够被代理 const BASE_URL = `${API_BASE.APP_TIK}/file` -/** - * 获取视频时长(秒) - * @param {File} file - 视频文件对象 - * @returns {Promise} 时长(秒) - */ -function getVideoDuration(file) { - return new Promise((resolve, reject) => { - // 只处理视频文件 - if (!file.type.startsWith('video/')) { - resolve(null); - return; - } - - const video = document.createElement('video'); - video.preload = 'metadata'; - video.muted = true; // 静音,避免浏览器阻止自动播放 - - video.onloadedmetadata = function() { - const duration = Math.round(video.duration); - URL.revokeObjectURL(video.src); - resolve(duration); - }; - - video.onerror = function() { - URL.revokeObjectURL(video.src); - resolve(60); - }; - - video.src = URL.createObjectURL(file); - }); -} - /** * 素材库 API 服务 */ @@ -61,36 +29,6 @@ export const MaterialService = { return http.get(`${BASE_URL}/page`, { params }) }, - /** - * 上传文件 - * @param {File} file - 文件对象 - * @param {string} fileCategory - 文件分类(video/generate/audio/mix/voice) - * @param {number} duration - 视频时长(秒,可选,自动获取) - * @param {number} groupId - 分组编号(可选) - * @returns {Promise} - */ - async uploadFile(file, fileCategory, duration = null, groupId = null) { - if (duration === null && file.type.startsWith('video/')) { - duration = await getVideoDuration(file); - } - - const formData = new FormData() - formData.append('file', file) - formData.append('fileCategory', fileCategory) - - if (duration !== null) { - formData.append('duration', duration.toString()); - } - - if (groupId !== null) { - formData.append('groupId', groupId.toString()) - } - - return http.post(`${BASE_URL}/upload`, formData, { - timeout: 30 * 60 * 1000 - }) - }, - /** * 获取预签名URL(直传模式) * @param {Object} params - 请求参数 diff --git a/frontend/app/web-gold/src/components/VideoSelector.vue b/frontend/app/web-gold/src/components/VideoSelector.vue index f7a9c25ce5..58dbe76f95 100644 --- a/frontend/app/web-gold/src/components/VideoSelector.vue +++ b/frontend/app/web-gold/src/components/VideoSelector.vue @@ -294,7 +294,7 @@ const getVideoPreviewUrl = (video) => { ? video.coverBase64 : `data:image/jpeg;base64,${video.coverBase64}` } - return video.previewUrl || video.coverUrl || defaultCover + return video.imgUrl || video.coverUrl || defaultCover } const handleCancel = () => { diff --git a/frontend/app/web-gold/src/components/VoiceSelector.vue b/frontend/app/web-gold/src/components/VoiceSelector.vue index 4faec8a771..a0de85b083 100644 --- a/frontend/app/web-gold/src/components/VoiceSelector.vue +++ b/frontend/app/web-gold/src/components/VoiceSelector.vue @@ -5,50 +5,78 @@ -
- +
+ +
+
+
+ + + + + +
+ 音色选择 + {{ currentVoiceName }} +
- - - 合成 - +
+
+ + + +
+ + + + 合成试听 + +
+
+ + + +
+
+
+ + + 下载音频 + +
+
+
- - -
- - - - 下载音频 -
- diff --git a/frontend/app/web-gold/src/components/material/MaterialMixModal.vue b/frontend/app/web-gold/src/components/material/MaterialMixModal.vue index deb6db5196..f27486235e 100644 --- a/frontend/app/web-gold/src/components/material/MaterialMixModal.vue +++ b/frontend/app/web-gold/src/components/material/MaterialMixModal.vue @@ -157,7 +157,7 @@ const handleConfirm = async () => { // 提取视频URL const videoUrls = videoGroupFiles.value - .map(file => file?.fileUrl || file?.previewUrl) + .map(file => file?.fileUrl || file?.imgUrl) .filter(Boolean) if (videoUrls.length === 0) { diff --git a/frontend/app/web-gold/src/composables/useUpload.js b/frontend/app/web-gold/src/composables/useUpload.js index 1e05e56572..6cef49d188 100644 --- a/frontend/app/web-gold/src/composables/useUpload.js +++ b/frontend/app/web-gold/src/composables/useUpload.js @@ -198,7 +198,7 @@ export function useUpload() { state.status = 'success' state.progress = 100 - const fileId = completeData.data?.infraFileId || completeData.data?.userFileId + const fileId = completeData.data?.userFileId const fileUrl = presignedData.data.presignedUrl onSuccess?.(fileId, fileUrl) diff --git a/frontend/app/web-gold/src/views/kling/IdentifyFace.vue b/frontend/app/web-gold/src/views/kling/IdentifyFace.vue index 601af22cd1..ad94c0d52c 100644 --- a/frontend/app/web-gold/src/views/kling/IdentifyFace.vue +++ b/frontend/app/web-gold/src/views/kling/IdentifyFace.vue @@ -1,37 +1,146 @@