fix(worker): align gpu-direct title position/size with frontend preview #2095

Merged
auto-approve-bot merged 1 commits from fix/gpu-direct-title-position-alignment into develop 2026-09-29 17:04:38 +08:00
Owner

问题

PR #2093 透传 title_config.position 后,GPU 直连路径实际渲染的标题位置/大小与前端预览不一致:top 贴顶偏上、竖屏偏小、custom 拖拽失效、默认跑到下边缘、粗体未生效。

根因(7 个子问题)

  1. margin 基准错误:用 h*0.05(高度百分比),前端/CPU ASS 都按 video_width/720 等比缩放。1280×720 横屏 top y=40(baseline),字号 36 时中文字顶边约 40-30=10px,几乎出框;1080×1920 竖屏 y=96 而前端预览是 ~60px。
  2. margin_top 字段被忽略:前端默认传 margin_top=24@720p,用户拖动垂直间距完全没生效。
  3. position="custom" 不支持:前端拖拽标题时传 custom + pos_x/pos_y(百分比 0-100),GPU 路径缺失直接落到默认分支。
  4. 默认 position 反了:GPU 兜底 bottom,schema / 剪辑页 / AI 头像页默认都 top;未传字段时标题跑到下方。
  5. 字号/描边/阴影按高度缩放:横屏偏小、竖屏偏大,与前端 720p 宽度基准不一致。
  6. 粗体默认 False:ASS 路径和前端都默认 True,GPU 没开仿粗描边。
  7. 仿粗描边宽度硬编码 1px:不同分辨率视觉权重不一致。

修复

  • 新增 _scale_title_len(v, video_width) 按 720p 宽度等比缩放(对齐 ass_subtitle_builder._scale_len 和前端 titleCanvas.ts 的 scale = videoWidth/720)。
  • 长度字段(size、stroke width、shadow offset、margin、faux-bold border)统一先读 720p 基准值再缩放。
  • 位置:
    • top → y = scale(PAD 16 + margin_top 默认 24) = scale(40) @720p(baseline;对齐前端 firstLineY = size/2 + PAD + marginTop)
    • bottom → y = h-th - scale(PAD 16 + margin_bottom 24)
    • center → y=(h-text_h)/2(未变)
    • custom → pos_x/pos_y(0-100%)→ (w-text_w)*pct, (h-text_h)*pct
  • margin_top 字段透传,默认 24@720p。
  • 默认 position = top、默认 bold = True(仿粗描边宽度也缩放)。
  • 字幕同理:默认 size=24@720p、bottom margin=60@720p、描边 2@720p。

数值对比(1280×720)

项 修复前 修复后 前端预览
默认字号 36 (h*0.05) 64 (scale 36) 28-50(可配置)
top y 40 (baseline,字顶 ~10px) 71(PAD+margin 对齐) ~71px(顶边距)
bottom 字幕 margin 60 (h*0.06) 107 (scale 60) ~107px
默认粗体 无 borderw=2 同色描边 bold=true

备注

  • CPU/ASS 路径 TITLE_MARGIN_TOP=180@720p 明显偏大(25% 位置),可能是另一处遗留问题,本次不动 GPU 路径以外的逻辑,后续可单独排查。
  • drawtext y=Y 是字体 baseline 不是文本顶边;因 ffmpeg 表达式无法拿 font metrics,PAD+margin 作为 baseline 位置与前端顶边距近似对齐,误差在一个 ascent 范围内(中文约 0.85em)。

测试

  • 更新已有断言为宽度缩放后数值(fontsize 64/89/107、borderw 2/4/7、shadow +5、margin y=71/107)。
  • 新增:custom 百分比坐标、margin_top 透传、默认 bold=True、bold=False 无描边、bottom 位置、竖屏(1080×1920)宽度缩放验证。
  • 30/30 单测通过;ruff check/format、py_compile 干净。
## 问题 PR #2093 透传 title_config.position 后,GPU 直连路径实际渲染的标题位置/大小与前端预览不一致:top 贴顶偏上、竖屏偏小、custom 拖拽失效、默认跑到下边缘、粗体未生效。 ## 根因(7 个子问题) 1. **margin 基准错误**:用 `h*0.05`(高度百分比),前端/CPU ASS 都按 **video_width/720** 等比缩放。1280×720 横屏 top y=40(baseline),字号 36 时中文字顶边约 40-30=10px,几乎出框;1080×1920 竖屏 y=96 而前端预览是 ~60px。 2. **margin_top 字段被忽略**:前端默认传 `margin_top=24@720p`,用户拖动垂直间距完全没生效。 3. **position="custom" 不支持**:前端拖拽标题时传 custom + pos_x/pos_y(百分比 0-100),GPU 路径缺失直接落到默认分支。 4. **默认 position 反了**:GPU 兜底 `bottom`,schema / 剪辑页 / AI 头像页默认都 `top`;未传字段时标题跑到下方。 5. **字号/描边/阴影按高度缩放**:横屏偏小、竖屏偏大,与前端 720p 宽度基准不一致。 6. **粗体默认 False**:ASS 路径和前端都默认 True,GPU 没开仿粗描边。 7. **仿粗描边宽度硬编码 1px**:不同分辨率视觉权重不一致。 ## 修复 - 新增 `_scale_title_len(v, video_width)` 按 720p 宽度等比缩放(对齐 `ass_subtitle_builder._scale_len` 和前端 `titleCanvas.ts` 的 `scale = videoWidth/720`)。 - 长度字段(size、stroke width、shadow offset、margin、faux-bold border)统一先读 720p 基准值再缩放。 - 位置: - `top` → `y = scale(PAD 16 + margin_top 默认 24) = scale(40) @720p`(baseline;对齐前端 `firstLineY = size/2 + PAD + marginTop`) - `bottom` → `y = h-th - scale(PAD 16 + margin_bottom 24)` - `center` → `y=(h-text_h)/2`(未变) - `custom` → `pos_x/pos_y`(0-100%)→ `(w-text_w)*pct, (h-text_h)*pct` - `margin_top` 字段透传,默认 24@720p。 - 默认 position = `top`、默认 bold = True(仿粗描边宽度也缩放)。 - 字幕同理:默认 size=24@720p、bottom margin=60@720p、描边 2@720p。 ## 数值对比(1280×720) | 项 | 修复前 | 修复后 | 前端预览 | |---|---|---|---| | 默认字号 | 36 (h*0.05) | 64 (scale 36) | 28-50(可配置) | | top y | 40 (baseline,字顶 ~10px) | 71(PAD+margin 对齐) | ~71px(顶边距) | | bottom 字幕 margin | 60 (h*0.06) | 107 (scale 60) | ~107px | | 默认粗体 | 无 | borderw=2 同色描边 | bold=true | ## 备注 - CPU/ASS 路径 `TITLE_MARGIN_TOP=180@720p` 明显偏大(25% 位置),可能是另一处遗留问题,本次不动 GPU 路径以外的逻辑,后续可单独排查。 - drawtext y=Y 是字体 baseline 不是文本顶边;因 ffmpeg 表达式无法拿 font metrics,PAD+margin 作为 baseline 位置与前端顶边距近似对齐,误差在一个 ascent 范围内(中文约 0.85em)。 ## 测试 - 更新已有断言为宽度缩放后数值(fontsize 64/89/107、borderw 2/4/7、shadow +5、margin y=71/107)。 - 新增:custom 百分比坐标、margin_top 透传、默认 bold=True、bold=False 无描边、bottom 位置、竖屏(1080×1920)宽度缩放验证。 - 30/30 单测通过;ruff check/format、py_compile 干净。
xiaoxia added 1 commit 2026-09-29 16:51:58 +08:00
fix(worker): align gpu-direct title position/size with frontend preview
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 1s
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 1s
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / PR Build Web 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 / Retag skipped Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped 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 / PR Build API Image (pull_request) Successful in 55s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m40s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 2m31s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m6s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 3m43s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 4m3s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 4m20s
CI/CD Pipeline / Validate - Style (pull_request) Successful in 5m25s
AI Code Review / AI Code Review (pull_request) Successful in 6m40s
CI/CD Pipeline / Validate - Security (pull_request) Successful in 11m58s
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 / CI Gate (pull_request) Successful in 1s
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
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 9m35s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 31s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 55s
20f32c576d
Root cause (7 issues in GPU drawtext rendering after PR #2093):
1. margin scaled by HEIGHT*0.05 instead of WIDTH/720 baseline → 1280x720 top
   placed at y=40 (baseline) making text top edge ~0px (partially off-screen);
   1080x1920 portrait placed at 96 vs frontend preview 60px
2. margin_top field from frontend ignored entirely (default 24@720p); user
   adjustments to vertical spacing had no effect
3. position='custom' (drag-to-position from frontend) not handled → fell
   through to default 'bottom'
4. default position was 'bottom' but schema/frontend default is 'top'
5. fontsize/stroke/shadow scaled by height ratio instead of width/720
6. bold defaulted False while ASS path and frontend default True
7. faux-bold stroke width was hardcoded raw 1px (unscaled), breaking
   bold appearance at non-720p resolutions

Fix:
- All length fields (size, stroke width, shadow offset, margins, faux-bold
  border) use 720p reference width scaling via _scale_title_len() to match
  frontend titleCanvas.ts and ass_subtitle_builder._scale_len()
- top position: y = scale(PAD 16 + margin_top default 24) = scale(40) @720p
- bottom position: y = h-th - scale(PAD 16 + margin_bottom default 24)
- center position: y=(h-text_h)/2 (unchanged)
- custom position: pos_x/pos_y (0-100%) → (w-text_w)*pct, (h-text_h)*pct
- margin_top from title_config is honored (frontend default 24@720p)
- default position = 'top' (matches schema DEFAULT_TITLE_SETTINGS)
- default bold = True, faux-bold border scaled by width
- subtitle similarly scaled: default size 24@720p, bottom margin 60@720p,
  stroke width 2@720p

Tests:
- Updated existing assertions to width-scaled values (1280w: title 64/89/107,
  subtitle 43/50, borderw 2/4/7, shadow 5, margins 71/107)
- Added tests for: custom pos (pct xy), margin_top respected, bold default,
  bold=false disables faux stroke, bottom position, portrait (1080x1920)
  width-scaled sizing
- 30/30 unit tests pass; ruff check/format/py_compile clean

🚀 预览环境已部署

项目 详情
PR号 #2095
预览链接 https://pr-2095.preview.xiaoxiajianji.com
API环境 staging

💡 预览环境使用 staging API 数据,请勿在预览环境中操作重要数据。

🔄 每次提交新代码后预览环境会自动更新。

🗑️ PR 关闭或合并后,预览环境会自动清理。

🚀 **预览环境已部署** | 项目 | 详情 | |------|------| | PR号 | #2095 | | 预览链接 | [https://pr-2095.preview.xiaoxiajianji.com](https://pr-2095.preview.xiaoxiajianji.com) | | API环境 | staging | > 💡 预览环境使用 staging API 数据,请勿在预览环境中操作重要数据。 > > 🔄 每次提交新代码后预览环境会自动更新。 > > 🗑️ PR 关闭或合并后,预览环境会自动清理。
auto-approve-bot merged commit 3be06c5763 into develop 2026-09-29 17:04:38 +08:00
auto-approve-bot deleted branch fix/gpu-direct-title-position-alignment 2026-09-29 17:04:42 +08:00

🗑️ 预览环境已清理

PR #2095 已关闭或合并,对应的预览环境已被清理。

如有需要,可以重新打开 PR 来重新生成预览环境。

🗑️ **预览环境已清理** PR #2095 已关闭或合并,对应的预览环境已被清理。 > 如有需要,可以重新打开 PR 来重新生成预览环境。
Sign in to join this conversation.