Compare commits

...

2 Commits

Author SHA1 Message Date
xiaoxia ebb664668b fix: 预览请求补传 output_width/output_height,匹配 video_ratio
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 1m4s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 1m5s
CI/CD Pipeline / Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build API Image (pull_request) Has been skipped
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 1m30s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m50s
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been skipped
AI Code Review / AI Code Review (pull_request) Successful in 1m57s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Successful in 1m19s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 3m5s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 2m30s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m44s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 4m9s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 5m37s
CI/CD Pipeline / Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Canary Release to Production (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / CI Gate (pull_request) Successful in 15s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 1m3s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 1m31s
- 新增 calculateResolution 共享工具函数,预览和生成共用同一份分辨率计算
- CreatePreviewRequest 类型新增 output_width/output_height 字段
- buildPreviewRequest 传入计算后的分辨率(9:16 → 1080×1920)
- useGenerateVideo 改用共享函数,去除重复的内联计算
2026-08-25 09:03:34 +08:00
xiaoxia 055f6c0475 feat: 预览改为服务器渲染视频,实现所见即所得 (#1487)
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 53s
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 1m11s
CI/CD Pipeline / PR Build Web Image (push) Has been skipped
CI/CD Pipeline / PR Build Worker Image (push) Has been skipped
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 3m7s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been skipped
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 3m36s
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 3m45s
CI/CD Pipeline / Build Staging API Image (push) Successful in 3m47s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 4m16s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 49s
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 39s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
PR Automation / Auto Approve on CI Green (pull_request) Successful in 4m33s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 2m55s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 7m6s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 8m13s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 45s
AI Code Review / AI Code Review (pull_request) Successful in 7m46s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 1m4s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 1m4s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 11m8s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 11m16s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 3m2s
CI/CD Pipeline / Unit Tests (push) Successful in 15m20s
CI/CD Pipeline / Build Production API Image (push) Has been skipped
CI/CD Pipeline / Build Production Web Image (push) Has been skipped
CI/CD Pipeline / Build Production Worker Image (push) Has been skipped
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Unit Tests (pull_request) Successful in 13m40s
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Canary Release to Production (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / Integration Tests (push) Successful in 6m32s
CI/CD Pipeline / CI Gate (push) Has been skipped
CI/CD Pipeline / Integration Tests (pull_request) Successful in 6m32s
CI/CD Pipeline / CI Gate (pull_request) Successful in 7s
2026-08-25 01:00:55 +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 }
}