feat: 功能

This commit is contained in:
2026-03-05 23:21:00 +08:00
parent 9b132082d2
commit dff90abbb4
11 changed files with 87 additions and 104 deletions

View File

@@ -76,12 +76,3 @@ export function deleteTask(taskId) {
})
}
/**
* 获取任务输出文件的签名URL
*/
export function getSignedUrls(taskId) {
return request({
url: `/webApi/api/tik/digital-human/task/${taskId}/signed-url`,
method: 'get'
})
}

View File

@@ -264,12 +264,12 @@ const initPlayer = (url) => {
player.on('canplay', () => {
isPlayerInitializing.value = false
// 发送音频时长和 base64 数据给父组件
// 发送音频时长和 URL 给父组件
const durationMs = Math.floor(player.audio.duration * 1000)
if (durationMs > 0) {
emit('audioGenerated', {
durationMs,
audioBase64: currentAudioBase64.value
audioUrl: audioUrl.value // 使用 URL性能优化
})
}
})

View File

@@ -326,13 +326,13 @@ function clearVideo() {
store.resetProcess()
}
function handleAudioGenerated(data: { durationMs: number; audioBase64: string }) {
function handleAudioGenerated(data: { durationMs: number; audioUrl: string }) {
if (store.timeline && data.durationMs > 0) {
store.timeline.audioDurationMs = data.durationMs
}
if (data.audioBase64) {
if (data.audioUrl) {
store.audioData = {
audioBase64: data.audioBase64,
audioUrl: data.audioUrl,
format: 'mp3',
durationMs: data.durationMs
}

View File

@@ -21,7 +21,7 @@ import type { VoiceMeta, Video, PipelinePhase, VideoStep, AudioStep, CreateStep,
/** 音频数据 */
interface AudioData {
audioBase64: string
audioUrl: string // 预签名 URL
format: string
durationMs: number
}
@@ -373,14 +373,20 @@ export const useDigitalHumanStore = defineStore('digitalHuman', () => {
providerType: DEFAULT_VOICE_PROVIDER,
} as any)
if (res.code !== 0 || !res.data?.audioBase64) {
if (res.code !== 0 || !res.data?.audioUrl) {
throw new Error(res.msg || '音频生成失败')
}
// 使用 URL性能优化
const audioUrl = res.data.audioUrl
if (!audioUrl) {
throw new Error('音频生成失败未返回音频URL')
}
const durationMs = await parseAudioDuration(res.data.audioBase64)
// 获取音频时长
const durationMs = await getAudioDurationFromUrl(audioUrl)
audioData.value = {
audioBase64: res.data.audioBase64,
audioUrl: audioUrl,
format: 'mp3',
durationMs,
}
@@ -447,10 +453,8 @@ export const useDigitalHumanStore = defineStore('digitalHuman', () => {
kling_face_end_time: identifyData.value.faceEndTime,
ai_provider: 'kling',
voiceConfigId: voiceId,
pre_generated_audio: {
audioBase64: audioData.value.audioBase64,
format: audioData.value.format,
},
// 使用预生成的音频 URL性能优化
audio_url: audioData.value.audioUrl,
sound_end_time: audioData.value.durationMs,
})
@@ -571,32 +575,24 @@ export const useDigitalHumanStore = defineStore('digitalHuman', () => {
// ==================== 工具方法 ====================
/** 解析音频时长 */
async function parseAudioDuration(base64Data: string): Promise<number> {
const base64 = base64Data.includes(',') ? base64Data.split(',')[1] : base64Data
const binaryString = window.atob(base64)
const bytes = new Uint8Array(binaryString.length)
for (let i = 0; i < bytes.length; i++) {
bytes[i] = binaryString.charCodeAt(i)
}
/** 从 URL 获取音频时长 */
async function getAudioDurationFromUrl(url: string): Promise<number> {
return new Promise((resolve, reject) => {
const blob = new Blob([bytes], { type: 'audio/mp3' })
const audio = new Audio()
const objectUrl = URL.createObjectURL(blob)
let resolved = false
let lastDuration = 0
const timeoutId = setTimeout(() => {
if (!resolved) {
cleanup()
reject(new Error('音频解析超时'))
reject(new Error('音频加载超时'))
}
}, 15000)
const cleanup = () => {
clearTimeout(timeoutId)
URL.revokeObjectURL(objectUrl)
audio.pause()
audio.src = ''
}
const tryResolve = (duration: number, source: string) => {
@@ -621,11 +617,11 @@ export const useDigitalHumanStore = defineStore('digitalHuman', () => {
resolve(Math.floor(lastDuration * 1000) - 200)
} else {
cleanup()
reject(new Error('音频解析失败'))
reject(new Error('音频加载失败'))
}
}
audio.src = objectUrl
audio.src = url
audio.load()
})
}

View File

@@ -95,9 +95,9 @@ export interface AudioState {
* 音频数据接口
*/
export interface AudioData {
audioBase64: string
audioUrl?: string
audioUrl: string // 预签名 URL性能优化
format?: string
durationMs?: number // 音频时长(毫秒)
}
/**
@@ -253,9 +253,6 @@ export interface LipSyncTaskData {
ai_provider: string
voiceId?: string // 系统预置音色ID
voiceConfigId?: string // 用户配音IDtik_user_voice.id
pre_generated_audio?: {
audioBase64: string
format: string
}
audio_url?: string // 预生成音频 URL性能优化
sound_end_time?: number
}

View File

@@ -132,13 +132,6 @@
</a-table>
</a-spin>
</div>
<!-- 视频预览弹窗 -->
<VideoPreviewModal
v-model:open="previewVisible"
:video-url="previewUrl"
:title="previewTitle"
/>
</div>
</template>
@@ -146,13 +139,12 @@
import { ref, onMounted } from 'vue'
import { message, Modal } from 'ant-design-vue'
import { SearchOutlined, DownloadOutlined, DeleteOutlined, PlayCircleOutlined } from '@ant-design/icons-vue'
import { getDigitalHumanTaskPage, cancelTask, deleteTask, getSignedUrls } from '@/api/digitalHuman'
import { getDigitalHumanTaskPage, cancelTask, deleteTask } from '@/api/digitalHuman'
import { formatDate } from '@/utils/file'
import { useTaskList } from '@/views/system/task-management/composables/useTaskList'
import { useTaskOperations } from '@/views/system/task-management/composables/useTaskOperations'
import { useTaskPolling } from '@/views/system/task-management/composables/useTaskPolling'
import TaskStatusTag from '@/views/system/task-management/components/TaskStatusTag.vue'
import VideoPreviewModal from '@/components/VideoPreviewModal.vue'
// 进度状态映射
const PROGRESS_STATUS = {
@@ -175,41 +167,16 @@ const rowSelection = {
onChange: (keys) => { selectedRowKeys.value = keys }
}
// 视频预览状态
const previewVisible = ref(false)
const previewUrl = ref('')
const previewTitle = ref('')
// 状态判断
const isStatus = (status, target) => status === target || status === target.toUpperCase()
// 预览视频
const handlePreview = async (record) => {
if (!record.id) {
message.warning('任务信息不完整')
// 预览视频(直接打开链接)
const handlePreview = (record) => {
if (!record.resultVideoUrl) {
message.warning('任务暂无视频结果,请稍后再试')
return
}
// 显示加载提示
const hideLoading = message.loading('正在获取视频地址...', 0)
try {
// 调用后端API获取带签名的视频URL
const res = await getSignedUrls(record.id)
hideLoading()
if (res.code === 0 && res.data && res.data.length > 0) {
previewUrl.value = res.data[0]
previewTitle.value = record.taskName || '视频预览'
previewVisible.value = true
} else {
message.error(res.msg || '获取视频地址失败')
}
} catch (error) {
hideLoading()
console.error('获取视频播放URL失败:', error)
message.error('获取视频地址失败,请重试')
}
window.open(record.resultVideoUrl, '_blank')
}
// 下载视频