From d3469d9799ababda3257b00448c1a1debfdae682 Mon Sep 17 00:00:00 2001 From: CI Bot Date: Thu, 20 Aug 2026 15:54:01 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E7=BB=9F=E4=B8=80=E6=89=80?= =?UTF-8?q?=E6=9C=89=E7=B4=A0=E6=9D=90=E4=B8=8A=E4=BC=A0=E4=B8=BA=E5=8D=95?= =?UTF-8?q?=E4=B8=80=20ingest=20=E8=B7=AF=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 前端修改: 1. useVoiceUpload.ts (voices): 删除 createAsset 调用,改为轮询 ingest_job_id 2. useVoiceUpload.ts (voice-materials): 同上,使用 result_asset_id 打标签 后端修改: 1. POST /assets 返回 410 Gone(已废弃,统一走 ingest-jobs) 2. 上传流程: uploadAssetDirect → completeDirectUpload → _submit_ingest_job → Worker 统一后的上传路径: - 前端上传 OSS(直传) - 调用 completeDirectUpload 创建 ingest job - Worker 根据类型处理(视频转码/图片音频元数据提取) - 创建 Asset 记录 → ready - 前端轮询 job 状态,完成后刷新 --- apps/api/app/api/routes/assets.py | 18 +++---- .../actions/useVoiceUpload.ts | 47 ++++++++++--------- .../src/pages/voices/hooks/useVoiceUpload.ts | 40 ++++++++-------- 3 files changed, 51 insertions(+), 54 deletions(-) diff --git a/apps/api/app/api/routes/assets.py b/apps/api/app/api/routes/assets.py index 161ffa36c..6c40e6af4 100755 --- a/apps/api/app/api/routes/assets.py +++ b/apps/api/app/api/routes/assets.py @@ -680,19 +680,13 @@ def untag_asset( def create_asset( request: CreateAssetRequest, authenticated_user: AuthenticatedUser = Depends(get_current_user), - asset_repository: Any = Depends(get_asset_repository), - asset_library_repository: Any = Depends(get_asset_library_repository), - project_repository: Any = Depends(get_project_repository), ) -> AssetResponse: - # 视频素材必须走 ingest-jobs 接口(统一上传路径,Worker 会自动处理 HEVC 转码) - if request.mime_type and request.mime_type.startswith("video"): - raise HTTPException( - status_code=400, - detail="视频素材请使用 ingest-jobs 接口上传,Worker 会自动处理 HEVC 转码和元数据提取" - ) - - # 先获取素材库,用于推导 project_id(前端可能不传) - library = asset_library_repository.get(request.library_id) + # 统一上传路径:所有素材必须走 ingest-jobs 接口 + # 前端上传 OSS → completeDirectUpload → _submit_ingest_job → Worker 处理 + raise HTTPException( + status_code=410, + detail="此接口已废弃。请使用 uploadAssetDirect 接口上传素材,Worker 会自动处理(视频转码、图片/音频元数据提取)并创建 Asset 记录。" + ) if library is None: raise HTTPException(status_code=404, detail=f"AssetLibrary {request.library_id} not found") diff --git a/apps/web/src/pages/voice-materials/hooks/useVoiceMaterials/actions/useVoiceUpload.ts b/apps/web/src/pages/voice-materials/hooks/useVoiceMaterials/actions/useVoiceUpload.ts index 84ddecc4e..c8c13e69a 100755 --- a/apps/web/src/pages/voice-materials/hooks/useVoiceMaterials/actions/useVoiceUpload.ts +++ b/apps/web/src/pages/voice-materials/hooks/useVoiceMaterials/actions/useVoiceUpload.ts @@ -2,14 +2,13 @@ import { useState, useCallback } from "react" import { useMutation, useQueryClient } from "@tanstack/react-query" import { message } from "antd" import { - createAsset, uploadAssetDirect, getAssetLibraries, + getIngestJob, type AssetLibraryItem, } from "@/api/assets" import { tagAsset } from "@/api/tags" -import { type VoiceGender, type VoiceMaterial, buildMetadata } from "../../../types" -import { getAudioDuration } from "../../../utils/audio" +import { type VoiceGender, type VoiceMaterial } from "../../../types" interface UseVoiceUploadOptions { voiceLibrary?: { id: string; kind: string } @@ -48,32 +47,34 @@ export function useVoiceUpload({ voiceLibrary, createLibMutation }: UseVoiceUplo if (!lib) throw new Error("无法创建配音库") } - // 2. 上传文件(带进度) - const { storage_key } = await uploadAssetDirect({ + // 2. 上传文件(带进度,后端自动创建 ingest job) + const { ingest_job_id } = await uploadAssetDirect({ file: data.file, library_id: lib.id, onProgress: (p) => setUploadProgress(p), }) - // 3. 获取音频时长 - const duration = await getAudioDuration(data.file) + // 3. 轮询 ingest job 状态 + let job: Awaited> | null = null + let retries = 0 + const maxRetries = 60 // 最多等待 5 分钟 + while (retries < maxRetries) { + await new Promise((r) => setTimeout(r, 5000)) + job = await getIngestJob(ingest_job_id) + if (job.status === "completed" || job.status === "failed") break + retries++ + } + + if (!job || job.status === "failed") { + throw new Error("音频处理失败,请重试") + } + if (retries >= maxRetries) { + throw new Error("音频处理超时,请稍后在素材库查看") + } - // 4. 创建素材记录 - const asset = await createAsset({ - library_id: lib.id, - name: data.name, - storage_key, - mime_type: data.file.type || "audio/mpeg", - metadata: buildMetadata({ - gender: data.gender, - description: data.description, - duration, - }), - }) - - // 5. 打标签(标签走独立 API) - if (data.tagIds.length > 0) { - await tagAsset(asset.id, data.tagIds) + // 4. 打标签(标签走独立 API) + if (data.tagIds.length > 0 && job.result_asset_id) { + await tagAsset(job.result_asset_id, data.tagIds) } } finally { setUploadProgress(null) diff --git a/apps/web/src/pages/voices/hooks/useVoiceUpload.ts b/apps/web/src/pages/voices/hooks/useVoiceUpload.ts index 6e1424cec..f0d64912e 100644 --- a/apps/web/src/pages/voices/hooks/useVoiceUpload.ts +++ b/apps/web/src/pages/voices/hooks/useVoiceUpload.ts @@ -1,8 +1,7 @@ import { useState, useCallback } from "react" import { useMutation, useQueryClient } from "@tanstack/react-query" -import { uploadAssetDirect, getAssetLibraries, createAsset } from "@/api/assets" -import { getAudioDuration } from "../utils/audio" -import { buildVoiceMetadata } from "../types" +import { uploadAssetDirect, getAssetLibraries, getIngestJob } from "@/api/assets" +import type { IngestJob } from "@/api/assets/types" /** * 配音上传 Hook @@ -33,27 +32,30 @@ export function useVoiceUpload({ showToast }: UseVoiceUploadProps) { const lib = libs.find((l) => l.kind === "voice") if (!lib) throw new Error("配音库不存在,请先在配音库页面创建") - /* 直传文件 */ - const { storage_key } = await uploadAssetDirect({ + /* 直传文件(后端会自动创建 ingest job) */ + const { ingest_job_id } = await uploadAssetDirect({ file: data.file, library_id: lib.id, onProgress: (p) => setUploadProgress(p), }) - /* 获取音频时长 */ - const duration = await getAudioDuration(data.file) - - /* 创建素材记录 */ - await createAsset({ - library_id: lib.id, - name: data.name, - storage_key, - mime_type: data.file.type || "audio/mpeg", - metadata: buildVoiceMetadata({ - description: data.description, - duration, - }), - }) + /* 轮询 ingest job 状态,等待 Worker 处理完成 */ + let jobStatus = "" + let retries = 0 + const maxRetries = 60 // 最多等待 5 分钟(60 * 5秒) + while (jobStatus !== "ready" && jobStatus !== "failed" && retries < maxRetries) { + await new Promise((r) => setTimeout(r, 5000)) + const job = await getIngestJob(ingest_job_id) + jobStatus = job.status + retries++ + } + + if (jobStatus === "failed") { + throw new Error("音频处理失败,请重试") + } + if (retries >= maxRetries) { + throw new Error("音频处理超时,请稍后在素材库查看") + } } finally { setUploadProgress(null) }