fix: 预览请求补传 output_width/output_height,匹配 video_ratio #1488

Merged
xiaoxia merged 1 commits from fix/preview-resolution into develop 2026-08-25 09:10:52 +08:00
4 changed files with 62 additions and 31 deletions
+4
View File
@@ -13,6 +13,10 @@ export interface CreatePreviewRequest {
video_title?: string
duration?: number
video_ratio?: string
/** 输出视频宽度(与 video_ratio 匹配,如 9:16 → 1080 */
output_width?: number
/** 输出视频高度(与 video_ratio 匹配,如 9:16 → 1920 */
output_height?: number
/* 标题烧录配置(可选,传入后 ASS 渲染标题到预览视频中) */
title_config?: {
text?: string
@@ -15,6 +15,7 @@ import { Modal, message } from "antd"
import { useNavigate } from "react-router-dom"
import type { VoiceClone } from "@/api/voice-clone"
import { useCloneProgress } from "@/hooks/useCloneProgress"
import { calculateResolution } from "./utils/calculateResolution"
import CloneModal from "@/components/voice/CloneModal"
import GenerateHeader from "./components/GenerateHeader"
import {
@@ -119,6 +120,9 @@ const GeneratePage: React.FC = () => {
[bgm, currentTemplate],
)
/* ── 输出分辨率(根据视频比例计算) ── */
const resolution = useMemo(() => calculateResolution(videoRatio), [videoRatio])
/* ── 加载素材详情(仅用于配音时长校验,不用于播放) ── */
const previewAssetsEnabled = previewAssetIds.length > 0
const { assets: previewAssets } = usePreviewAssets(previewAssetIds, previewAssetsEnabled)
@@ -143,6 +147,8 @@ const GeneratePage: React.FC = () => {
asset_ids: previewAssetIds,
duration: duration || 30,
video_ratio: videoRatio,
output_width: resolution.width,
output_height: resolution.height,
...(voiceLibraryId ? { voice_library_id: voiceLibraryId } : {}),
bgm_config: {
enabled: bgm !== false,
@@ -168,6 +174,7 @@ const GeneratePage: React.FC = () => {
previewAssetIds,
duration,
videoRatio,
resolution,
voiceLibraryId,
bgm,
bgmConfig,
@@ -11,6 +11,7 @@ import type { UseGenerateVideoProps } from "./generate-video/types"
import { getGenerationPhase } from "./generate-video/phase"
import { useGenerationPolling } from "./generate-video/useGenerationPolling"
import { validateGenerateInputs } from "./generate-video/buildPayload"
import { calculateResolution } from "../utils/calculateResolution"
import { extractBackendError, translateError } from "./generate-video/errorUtils"
export function useGenerateVideo(props: UseGenerateVideoProps) {
@@ -60,37 +61,10 @@ export function useGenerateVideo(props: UseGenerateVideoProps) {
clearTimer()
try {
// 解析分辨率
const ratio = props.videoRatio || "9:16"
let outputWidth: number
let outputHeight: number
if (ratio.includes(":")) {
const [rw, rh] = ratio.split(":").map(Number)
if (rw > 0 && rh > 0) {
const [longSide, shortSide] = rw < rh ? [rh, rw] : [rw, rh]
const baseLong = 1920
const baseShort = Math.round((baseLong * shortSide) / longSide)
const evenShort = baseShort - (baseShort % 2)
if (rw < rh) {
outputWidth = evenShort
outputHeight = baseLong
} else {
outputWidth = baseLong
outputHeight = evenShort
}
} else {
outputWidth = 1080
outputHeight = 1920
}
} else if (ratio.includes("x")) {
const [wStr, hStr] = ratio.split("x")
outputWidth = parseInt(wStr, 10) || 1080
outputHeight = parseInt(hStr, 10) || 1920
} else {
outputWidth = 1080
outputHeight = 1920
}
// 解析分辨率(共享工具函数,与预览 API 一致)
const { width: outputWidth, height: outputHeight } = calculateResolution(
props.videoRatio || "9:16",
)
const assetIds =
props.materialMode === "auto" ? props.smartSelectedIds : props.selectedMaterials
@@ -0,0 +1,46 @@
/**
* 根据视频比例计算输出分辨率
*
* 规则:
* - 长边固定 1920
* - 短边按 1920 × (短/长) 计算,对齐到偶数
* - 9:16 → 1080×1920(竖屏)
* - 16:9 → 1920×1080(横屏)
* - 1:1 → 1920×1920
* - 无法解析时 fallback 到 1080×1920
*/
export interface Resolution {
width: number
height: number
}
export function calculateResolution(ratio: string): Resolution {
if (!ratio) return { width: 1080, height: 1920 }
if (ratio.includes(":")) {
const [rw, rh] = ratio.split(":").map(Number)
if (rw > 0 && rh > 0) {
const [longSide, shortSide] = rw < rh ? [rh, rw] : [rw, rh]
const baseLong = 1920
const baseShort = Math.round((baseLong * shortSide) / longSide)
const evenShort = baseShort - (baseShort % 2)
if (rw < rh) {
// 竖屏:短边是宽,长边是高
return { width: evenShort, height: baseLong }
} else {
// 横屏:长边是宽,短边是高
return { width: baseLong, height: evenShort }
}
}
return { width: 1080, height: 1920 }
}
if (ratio.includes("x")) {
const [wStr, hStr] = ratio.split("x")
const w = parseInt(wStr, 10) || 1080
const h = parseInt(hStr, 10) || 1920
return { width: w, height: h }
}
return { width: 1080, height: 1920 }
}