From a10d8b7ae0fb57265014831f6925d32db2711227 Mon Sep 17 00:00:00 2001 From: xiaoxia Date: Fri, 4 Sep 2026 16:01:21 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E9=85=8D=E9=9F=B3=E6=97=B6=E9=95=BF?= =?UTF-8?q?=E8=BF=87=E6=BB=A4=20+=20=E7=A7=BB=E9=99=A4=E6=97=B6=E9=95=BF?= =?UTF-8?q?=E8=AD=A6=E5=91=8A=20UI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Step5VoiceSelect: 自动过滤时长差异>±10%的配音,AI音色始终保留 - Step5VoiceSelect: 移除'配音时长不足'弹窗及相关状态 - Step5VoiceSelect: 移除'⚠ 时长不足'警告标签 - Step5VoiceSelect: 选中配音直接生效,不再弹窗确认 - Step1TemplateSelect: 模板卡片移除estimated_duration显示 - 过滤后如有被隐藏的素材,底部提示用户 --- .../components/Step1TemplateSelect.tsx | 4 +- .../generate/components/Step5VoiceSelect.tsx | 118 ++++-------------- 2 files changed, 26 insertions(+), 96 deletions(-) diff --git a/apps/web/src/pages/generate/components/Step1TemplateSelect.tsx b/apps/web/src/pages/generate/components/Step1TemplateSelect.tsx index d39a864b3..52cd525ee 100644 --- a/apps/web/src/pages/generate/components/Step1TemplateSelect.tsx +++ b/apps/web/src/pages/generate/components/Step1TemplateSelect.tsx @@ -47,9 +47,7 @@ const Step1TemplateSelect: React.FC = (props) => { 🎬

{tpl.name}

-

- {tpl.estimated_duration}s · {tpl.segments.length}片段 -

+

{tpl.segments.length}片段

{tpl.tags.length > 0 && (
{ return item.duration ?? (item.metadata?.duration as number) ?? 0 @@ -34,13 +33,6 @@ const isAiVoice = (item: AssetItem): boolean => { return (!duration || duration <= 0) && (!size || size <= 0) } -const formatDuration = (seconds?: number): string => { - if (!seconds || seconds <= 0) return "00:00" - const m = Math.floor(seconds / 60) - const s = Math.floor(seconds % 60) - return `${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}` -} - /** 格式化文件大小 */ const formatFileSize = (bytes?: number): string => { if (!bytes || bytes <= 0) return "未知" @@ -58,8 +50,6 @@ const Step5VoiceSelect: React.FC = ({ const navigate = useNavigate() const [playingId, setPlayingId] = useState(null) const audioRef = useRef(null) - const [durationWarningOpen, setDurationWarningOpen] = useState(false) - const [pendingVoiceId, setPendingVoiceId] = useState(null) // 获取用户上传的配音素材 const { data: materials = [], isLoading } = useQuery({ @@ -67,6 +57,19 @@ const Step5VoiceSelect: React.FC = ({ queryFn: () => getAssetsByKind("voice", { limit: 50 }), }) + // 根据视频总时长过滤配音:只保留时长在 ±10% 以内的素材,AI 音色始终展示 + const filteredMaterials = React.useMemo(() => { + if (totalVideoDuration <= 0) return materials + return materials.filter((item) => { + // AI 音色没有固定时长,始终保留 + if (isAiVoice(item)) return true + const duration = getDuration(item) + if (duration <= 0) return true + const ratio = duration / totalVideoDuration + return ratio >= 0.9 && ratio <= 1.1 + }) + }, [materials, totalVideoDuration]) + /** 切换播放/暂停 */ const togglePlay = useCallback( (material: AssetItem) => { @@ -100,38 +103,14 @@ const Step5VoiceSelect: React.FC = ({ [playingId], ) - /** 选中素材(含时长校验) */ + /** 选中素材(直接选中,不再做时长校验弹窗) */ const handleSelect = useCallback( (id: string) => { - // 如果启用了时长校验,且配音时长不足(AI 音色按脚本实时合成,不参与时长校验) - if (totalVideoDuration > 0) { - const material = materials.find((m) => m.id === id) - if (material && !isAiVoice(material) && getDuration(material) < totalVideoDuration) { - setPendingVoiceId(id) - setDurationWarningOpen(true) - return - } - } onSelectedVoiceChange(id) }, - [onSelectedVoiceChange, totalVideoDuration, materials], + [onSelectedVoiceChange], ) - /** 确认使用时长不足的配音 */ - const handleConfirmUseAnyway = useCallback(() => { - if (pendingVoiceId) { - onSelectedVoiceChange(pendingVoiceId) - } - setDurationWarningOpen(false) - setPendingVoiceId(null) - }, [pendingVoiceId, onSelectedVoiceChange]) - - /** 取消选择 */ - const handleCancelSelection = useCallback(() => { - setDurationWarningOpen(false) - setPendingVoiceId(null) - }, []) - /** 跳转到配音库上传 */ const handleGoToUpload = useCallback(() => { navigate("/app/voices?tab=material&upload=1") @@ -196,7 +175,7 @@ const Step5VoiceSelect: React.FC = ({ gap: 12, }} > - {materials.map((item) => { + {filteredMaterials.map((item) => { const isSelected = selectedVoice === item.id const isPlaying = playingId === item.id @@ -277,7 +256,7 @@ const Step5VoiceSelect: React.FC = ({ {item.name}
- {/* 时长 + 大小 */} + {/* 文件大小 */}
= ({ > {isAiVoice(item) ? ( AI 音色 - ) : ( - - {formatDuration(getDuration(item))} - {totalVideoDuration > 0 && getDuration(item) < Number(totalVideoDuration) && ( - - - 时长不足 - - )} - - )} + ) : null} {isAiVoice(item) ? "按文本合成" : formatFileSize(getFileSize(item))}
) })} - - {/* 时长不足警告弹窗 */} - - - 配音时长不足 - - } - open={durationWarningOpen} - onOk={handleConfirmUseAnyway} - onCancel={handleCancelSelection} - okText="仍要使用" - cancelText="重新选择" - okButtonProps={{ danger: true }} - > - {(() => { - const pendingMaterial = pendingVoiceId - ? materials.find((m) => m.id === pendingVoiceId) - : null - return ( -

- 该配音时长( - - {pendingMaterial ? formatDuration(getDuration(pendingMaterial)) : "--"} - - )短于视频总时长( - {formatDuration(totalVideoDuration)} - ),播放时配音可能提前结束,建议选择更长的配音素材。 -

- ) - })()} -
+ {totalVideoDuration > 0 && filteredMaterials.length < materials.length && ( +

+ 已根据视频时长({Math.round(totalVideoDuration)}s)自动过滤时长差异过大的配音 +

+ )} ) } -- 2.54.0