fix: 修复标题字号(px计算)+配音同步+片段切换可靠性
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 1m3s
AI Code Review / AI Code Review (pull_request) Failing after 2m10s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 2m24s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 2m50s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 2m58s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m31s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 4m22s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Successful in 1m24s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 2m2s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 4m18s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 10m55s
CI/CD Pipeline / CI Gate (pull_request) Failing after 7s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Has been cancelled
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 1m2s
CI/CD Pipeline / Production Browser E2E (pull_request) Failing after 688h30m35s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 688h30m39s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 688h30m40s
CI/CD Pipeline / Canary Release to Production (pull_request) Failing after 688h30m36s
CI/CD Pipeline / PR Build Worker Image (pull_request) Failing after 688h37m12s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 688h30m36s
CI/CD Pipeline / PR Build API Image (pull_request) Failing after 688h37m47s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 688h38m24s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 688h41m32s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 688h41m34s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 688h41m35s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 688h41m33s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 688h41m35s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 688h41m36s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 689h4m21s
CI/CD Pipeline / Integration Tests (pull_request) Failing after 689h4m23s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 689h15m16s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 1m3s
AI Code Review / AI Code Review (pull_request) Failing after 2m10s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 2m24s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 2m50s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 2m58s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m31s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 4m22s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Successful in 1m24s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 2m2s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 4m18s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 10m55s
CI/CD Pipeline / CI Gate (pull_request) Failing after 7s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Has been cancelled
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 1m2s
CI/CD Pipeline / Production Browser E2E (pull_request) Failing after 688h30m35s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 688h30m39s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 688h30m40s
CI/CD Pipeline / Canary Release to Production (pull_request) Failing after 688h30m36s
CI/CD Pipeline / PR Build Worker Image (pull_request) Failing after 688h37m12s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 688h30m36s
CI/CD Pipeline / PR Build API Image (pull_request) Failing after 688h37m47s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 688h38m24s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 688h41m32s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 688h41m34s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 688h41m35s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 688h41m33s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 688h41m35s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 688h41m36s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 689h4m21s
CI/CD Pipeline / Integration Tests (pull_request) Failing after 689h4m23s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 689h15m16s
1. 标题 fontSize 改用 ResizeObserver 获取容器实际高度,px 单位计算 修复百分比 fontSize 基于 36px 父元素导致字号≈1.4px 不可见的问题 2. 片段切换时同步 audio.currentTime 到视频全局时间 3. switchToSegment readyState 阈值提升到 2 (HAVE_CURRENT_DATA)
This commit is contained in:
@@ -134,6 +134,14 @@ const FrontendPreviewPlayer: React.FC<FrontendPreviewPlayerProps> = ({
|
||||
prevIsPlayingRef.current = isPlaying
|
||||
}, [isPlaying, currentTime])
|
||||
|
||||
// 片段切换时同步音频 — 将 audio.currentTime 对齐到视频全局时间
|
||||
useEffect(() => {
|
||||
const audio = audioRef.current
|
||||
if (!audio || !audio.src || !isPlaying) return
|
||||
// 用视频当前的全局时间对齐音频
|
||||
audio.currentTime = currentTime
|
||||
}, [currentSegmentIndex])
|
||||
|
||||
// seek 时同步音频
|
||||
const handleSeekTo = useCallback(
|
||||
(time: number) => {
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
* FrontendPreviewPlayer 的内容通过 absolute 定位填充容器
|
||||
* TitleOverlay 通过 absolute 定位 + z-index: 30 覆盖在最上层
|
||||
*/
|
||||
import React, { useMemo } from "react"
|
||||
import React, { useMemo, useRef, useState, useEffect } from "react"
|
||||
import { LoadingOutlined } from "@ant-design/icons"
|
||||
import type { AssetItem } from "@/api/assets"
|
||||
import type { EditingTemplate } from "@/api/editing-planner"
|
||||
@@ -79,12 +79,16 @@ function getPositionStyle(position: string): React.CSSProperties {
|
||||
* 构建 CSS 标题层的样式
|
||||
* 所有渲染参数与后端 FFmpeg ASS 字幕一致
|
||||
*/
|
||||
function buildTitleStyle(settings: TitleSettings): React.CSSProperties {
|
||||
const fontSizePercent = (Math.min(settings.size, 36) / ASS_VIDEO_HEIGHT) * 100
|
||||
function buildTitleStyle(settings: TitleSettings, containerHeight: number): React.CSSProperties {
|
||||
// 用 px 计算 fontSize,不再依赖父元素 font-size 的百分比
|
||||
const fontSizePx =
|
||||
containerHeight > 0
|
||||
? (Math.min(settings.size, 36) / ASS_VIDEO_HEIGHT) * containerHeight
|
||||
: (Math.min(settings.size, 36) / ASS_VIDEO_HEIGHT) * 400 // fallback
|
||||
|
||||
const base: React.CSSProperties = {
|
||||
fontFamily: settings.font || "思源黑体",
|
||||
fontSize: `${fontSizePercent}%`,
|
||||
fontSize: `${fontSizePx}px`,
|
||||
color: settings.color || "#ffffff",
|
||||
fontWeight: settings.bold ? 700 : 400,
|
||||
fontStyle: settings.italic ? "italic" : "normal",
|
||||
@@ -113,15 +117,34 @@ function buildTitleStyle(settings: TitleSettings): React.CSSProperties {
|
||||
* z-index: 20(在视频 z-index:1 和控制条 z-index:10 之上)
|
||||
*/
|
||||
const TitleOverlay: React.FC<{ titleSettings: TitleSettings }> = ({ titleSettings }) => {
|
||||
const containerRef = useRef<HTMLDivElement>(null)
|
||||
const [containerHeight, setContainerHeight] = useState(400) // fallback
|
||||
|
||||
// ResizeObserver 获取容器实际高度
|
||||
useEffect(() => {
|
||||
const el = containerRef.current
|
||||
if (!el) return
|
||||
const ro = new ResizeObserver((entries) => {
|
||||
for (const entry of entries) {
|
||||
const h = entry.contentRect.height
|
||||
if (h > 0) setContainerHeight(h)
|
||||
}
|
||||
})
|
||||
ro.observe(el)
|
||||
// 初始化也读一次
|
||||
const rect = el.getBoundingClientRect()
|
||||
if (rect.height > 0) setContainerHeight(rect.height)
|
||||
return () => ro.disconnect()
|
||||
}, [])
|
||||
|
||||
const positionStyle = useMemo(
|
||||
() => getPositionStyle(titleSettings.position),
|
||||
[titleSettings.position],
|
||||
)
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const titleStyle = useMemo(
|
||||
() => buildTitleStyle(titleSettings),
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
() => buildTitleStyle(titleSettings, containerHeight),
|
||||
[
|
||||
containerHeight,
|
||||
titleSettings.font,
|
||||
titleSettings.size,
|
||||
titleSettings.color,
|
||||
@@ -132,11 +155,11 @@ const TitleOverlay: React.FC<{ titleSettings: TitleSettings }> = ({ titleSetting
|
||||
],
|
||||
)
|
||||
|
||||
// 标题为空时显示占位文本
|
||||
const displayTitle = titleSettings.title?.trim() || "标题预览"
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={containerRef}
|
||||
style={{
|
||||
position: "absolute",
|
||||
inset: 0,
|
||||
|
||||
@@ -197,9 +197,9 @@ export function useSegmentScheduler(segments: PlaybackSegment[]): SegmentSchedul
|
||||
video.src = videoUrl
|
||||
video.load()
|
||||
|
||||
// ✅ 关键修复:如果 readyState >= 1(已有 metadata),说明加载已完成
|
||||
// ✅ 关键修复:如果 readyState >= 2(HAVE_CURRENT_DATA),说明已有帧数据
|
||||
// 直接设置 currentTime 并 resolve,不等待事件
|
||||
if (video.readyState >= 1) {
|
||||
if (video.readyState >= 2) {
|
||||
video.currentTime = localTime
|
||||
srcLoadedRef.current = true
|
||||
cleanup()
|
||||
|
||||
Reference in New Issue
Block a user