Compare commits

...

213 Commits

Author SHA1 Message Date
xiaoxia 0b8f8dd18c style: fix prettier formatting for PreviewVideoPanel.tsx
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 / 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 40s
CI/CD Pipeline / Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 43s
CI/CD Pipeline / PR Build API Image (pull_request) Has been skipped
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 57s
AI Code Review / AI Code Review (pull_request) Successful in 1m45s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m44s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m46s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Successful in 1m13s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 1m31s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 1m36s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 2m18s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 3m59s
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 5s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 37s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 47s
2026-08-04 14:17:40 +08:00
xiaoxia 8120d1c5a3 feat(title): 标题预览改为叠加在视频画面上,支持top/center/bottom定位
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 / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E 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 30s
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 / PR Build Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 44s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Successful in 41s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 1m13s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m41s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m40s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 1m28s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 2m3s
AI Code Review / AI Code Review (pull_request) Failing after 2m3s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 1m55s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 4m25s
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) Failing after 5s
2026-08-04 14:05:38 +08:00
xiaoxia 98f132f381 feat(title): 标题预览改为叠加在视频画面上,支持top/center/bottom定位 2026-08-04 14:05:22 +08:00
xiaoxia 624e747a2b Merge pull request 'fix(test): useAuth断言修复 + E2E Step4超时兜底' (#1233) from fix/test-auth-and-e2e-step4 into develop
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / PR Build Web Image (push) Has been skipped
CI/CD Pipeline / PR Build API 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 1m19s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m36s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 3m22s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 3m22s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 3m26s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 4m53s
CI/CD Pipeline / Integration Tests (push) Successful in 1m36s
CI/CD Pipeline / Build Staging API Image (push) Successful in 7m7s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 45s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 41s
CI/CD Pipeline / Unit Tests (push) Successful in 9m5s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 1m11s
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 / CI Gate (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 / Staging API Integration Tests (push) Successful in 3m8s
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
2026-08-04 12:25:17 +08:00
xiaoxia 0b98d05b81 fix: prettier format useAuth.test.tsx
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 / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 36s
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 / PR Build Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 46s
AI Code Review / AI Code Review (pull_request) Successful in 1m8s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 1m31s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m35s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 1m4s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m48s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 1m49s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Successful in 1m21s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 1m38s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 4m2s
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 11s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 1m7s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 1m29s
2026-08-04 12:19:05 +08:00
xiaoxia 54ded9fc0e fix(test): useAuth登录成功断言改为/app/dashboard,与useAuth.ts实际跳转目标一致
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 39s
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 / PR Build Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 1m1s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 1m4s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m48s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 2m1s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Successful in 1m22s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 1m42s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 1m44s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 2m25s
AI Code Review / AI Code Review (pull_request) Successful in 2m48s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 3m59s
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Integration Tests (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) Failing after 12s
2026-08-04 11:12:47 +08:00
xiaoxia 64978da5bf fix(e2e): Step4标题heading等待超时从默认5s增加到15s,防止预览渲染延迟导致超时 2026-08-04 11:12:47 +08:00
xiaoxia 7ef1299f64 Merge pull request 'fix(title): 预设样式解耦字号 + 右侧标题实时预览' (#1232) from fix/title-preset-decouple-and-preview into develop
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 1m9s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m19s
CI/CD Pipeline / Frontend Unit Tests (push) Failing after 2m21s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m55s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 4m3s
CI/CD Pipeline / Integration Tests (push) Successful in 1m44s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 6m10s
CI/CD Pipeline / Unit Tests (push) Successful in 9m3s
CI/CD Pipeline / Build Production Web Image (push) Has been skipped
CI/CD Pipeline / Build Production API Image (push) Has been skipped
CI/CD Pipeline / Build Production Worker Image (push) Has been skipped
CI/CD Pipeline / CI Gate (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 / Build Staging API Image (push) Successful in 13m58s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 40s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 49s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 1m3s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 3m11s
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
2026-08-04 10:09:22 +08:00
xiaoxia 3687da034d feat(title): GeneratePage向PreviewVideoPanel传递titleText和titleSettings
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 / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 41s
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 / PR Build Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 1m2s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 1m2s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m35s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 1m0s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 1m49s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m59s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Successful in 1m20s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 1m43s
AI Code Review / AI Code Review (pull_request) Successful in 2m28s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 3m5s
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 / Canary Release to Production (pull_request) Has been skipped
CI/CD Pipeline / Deploy 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 11s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 41s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 1m13s
2026-08-04 10:03:35 +08:00
xiaoxia c29119e179 feat(title): PreviewVideoPanel新增标题实时预览区域,支持样式渲染 2026-08-04 10:03:27 +08:00
xiaoxia cc072bf22d fix(title): 预设样式不再绑定字号,applyPreset/getActivePreset去掉size匹配 2026-08-04 10:03:19 +08:00
xiaoxia a356639a34 fix: 修复模板查询方法名(repo.get替代repo.get_by_id) (#1231)
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 1m3s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m25s
CI/CD Pipeline / Frontend Unit Tests (push) Failing after 1m48s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m38s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 4m28s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 5m16s
CI/CD Pipeline / Integration Tests (push) Successful in 3m22s
CI/CD Pipeline / Unit Tests (push) Successful in 10m35s
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 / CI Gate (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 / Build Staging API Image (push) Successful in 12m59s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 35s
CI/CD Pipeline / Staging API Integration Tests (push) Failing after 16s
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 36s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 1m22s
2026-08-04 01:35:23 +08:00
xiaoxia 405bce6e1b fix: 预览分辨率自动从模板mode推断(Fix3补充) (#1230)
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 58s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m5s
CI/CD Pipeline / Frontend Unit Tests (push) Failing after 1m42s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 3m7s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 3m7s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 4m50s
CI/CD Pipeline / Build Staging API Image (push) Successful in 6m3s
CI/CD Pipeline / Integration Tests (push) Successful in 2m3s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 59s
CI/CD Pipeline / Staging API Integration Tests (push) Failing after 19s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 40s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 1m29s
CI/CD Pipeline / Unit Tests (push) Successful in 9m57s
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 / CI Gate (push) Has been skipped
CI/CD Pipeline / Build Production Worker Image (push) Has been skipped
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
2026-08-04 01:08:22 +08:00
xiaoxia 0e5f2eb036 Merge pull request 'fix: 预览视频3问题修复 — URL签名/时长约束/分辨率 (#1209)' (#1229) from fix/preview-generation-3-issues into develop
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 1m0s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m17s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m34s
CI/CD Pipeline / Frontend Unit Tests (push) Failing after 3m30s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 4m11s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 4m46s
CI/CD Pipeline / Integration Tests (push) Successful in 1m27s
CI/CD Pipeline / Unit Tests (push) Successful in 9m4s
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 / CI Gate (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 / Build Staging API Image (push) Successful in 11m40s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 1m44s
CI/CD Pipeline / Staging API Integration Tests (push) Failing after 15s
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 35s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 1m48s
2026-08-04 00:21:41 +08:00
xiaoxia d494f368d0 test: generation.py worker侧修复的单测(时长约束+segment加载)
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 API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E 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 42s
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 51s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 57s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m23s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m38s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 1m53s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 1m52s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 3m20s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m28s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 3m1s
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Canary Release to Production (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
AI Code Review / AI Code Review (pull_request) Failing after 3m57s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 2m45s
CI/CD Pipeline / CI Gate (pull_request) Successful in 6s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 50s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 1m8s
2026-08-04 00:09:53 +08:00
xiaoxia b9566c7a93 fix: 修复TestToPreviewResponse URL签名mock + 匹配降级逻辑
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 / 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 35s
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 1m0s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 1m3s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m26s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 2m0s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 1m56s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 1m57s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m16s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 4m0s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 5m26s
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production API 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
AI Code Review / AI Code Review (pull_request) Failing after 5m57s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 2m52s
CI/CD Pipeline / CI Gate (pull_request) Failing after 7s
2026-08-03 23:27:01 +08:00
xiaoxia 9808a6eef9 fix: URL签名降级逻辑 — bucket未配置时返回原始URL而非构造URL
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 / 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 44s
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 48s
CI/CD Pipeline / Validate - Code Quality (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
2026-08-03 23:25:56 +08:00
CI Bot 91364f15d5 style: auto-format with black + isort + prettier [skip ci-format-check]
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 / 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 39s
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / PR Build Web Image (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 1m7s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 1m10s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m31s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m43s
AI Code Review / AI Code Review (pull_request) Failing after 2m13s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 1m55s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 1m56s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m16s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 3m59s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 4m59s
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 (pull_request) Successful in 2m8s
CI/CD Pipeline / CI Gate (pull_request) Failing after 5s
2026-08-03 15:10:36 +00:00
xiaoxia b8e466eb75 chore: 移除独立测试文件(已合并到 test_generation_preview.py)
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 / 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 41s
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 55s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 1m5s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m28s
CI/CD Pipeline / Validate - Code Quality (pull_request) Failing after 1m38s
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
2026-08-03 23:09:00 +08:00
xiaoxia 6800ee31a3 test: 更新预览测试 — URL签名mock + 分辨率逻辑匹配修复后行为
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 / Build Staging Web 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 / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
2026-08-03 23:08:44 +08:00
CI Bot 11fa1f31da style: auto-format with black + isort + prettier [skip ci-format-check]
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 / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 45s
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 58s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 1m1s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m32s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m46s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 1m50s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 1m49s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m23s
AI Code Review / AI Code Review (pull_request) Failing after 4m10s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 4m37s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 7m19s
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 (pull_request) Successful in 4m43s
CI/CD Pipeline / CI Gate (pull_request) Failing after 6s
2026-08-03 14:47:58 +00:00
xiaoxia 3b90da1050 test: 预览生成3个修复的单元测试(URL签名/时长约束/分辨率)
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 API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 39s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 55s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 55s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m25s
AI Code Review / AI Code Review (pull_request) Successful in 1m37s
CI/CD Pipeline / Validate - Code Quality (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
2026-08-03 22:45:55 +08:00
xiaoxia 4861d298cc fix: 预览视频时长约束(模板segment duration_max) + 分辨率尊重video_ratio 2026-08-03 22:45:45 +08:00
xiaoxia 2788d00593 fix: 预览视频URL签名 — 私有bucket返回预签名URL避免403黑屏 2026-08-03 22:45:30 +08:00
xiaoxia a9d55dc2cf fix: 预览视频URL签名 — 私有bucket返回预签名URL避免403黑屏 2026-08-03 22:45:17 +08:00
xiaoxia 52ab7a91e0 fix(auth): 401拦截器排除auth端点 + login默认跳转/app/dashboard (#1228)
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Has been skipped
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 - Type Check (mypy) (push) Successful in 1m7s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 1m8s
CI/CD Pipeline / Frontend Unit Tests (push) Failing after 2m22s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 3m1s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 5m15s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 6m2s
CI/CD Pipeline / Integration Tests (push) Successful in 2m6s
CI/CD Pipeline / Unit Tests (push) Successful in 10m29s
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 / Deploy Production (push) Has been skipped
CI/CD Pipeline / CI Gate (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Build Staging API Image (push) Successful in 12m41s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 40s
CI/CD Pipeline / Staging API Integration Tests (push) Failing after 18s
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 32s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 53s
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com>
Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
2026-08-03 14:46:00 +08:00
xiaoxia 3e24f8de4a fix: MediaKit 重试逻辑 + TimeInterval默认策略 + 错误日志增强 (#1227)
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 52s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m11s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m33s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 3m25s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 4m23s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 4m52s
CI/CD Pipeline / Integration Tests (push) Successful in 1m32s
CI/CD Pipeline / Unit Tests (push) Successful in 9m8s
CI/CD Pipeline / Frontend Lint (push) Failing after 13m58s
CI/CD Pipeline / Build Production API Image (push) Has been skipped
CI/CD Pipeline / Build Production Worker Image (push) Has been skipped
CI/CD Pipeline / Build Production Web Image (push) Has been skipped
CI/CD Pipeline / CI Gate (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 / Build Staging API Image (push) Successful in 14m6s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 33s
CI/CD Pipeline / Staging API Integration Tests (push) Failing after 17s
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 36s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 44s
2026-08-03 13:51:27 +08:00
xiaoxia 1b0c2a7889 Merge pull request 'fix: MediaKit 视频理解轮询限时30s + 修复URL映射覆盖' (#1225) from fix/mediakit-timeout-issue1209 into develop
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 1m8s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m19s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m56s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 3m43s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 3m47s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 4m33s
CI/CD Pipeline / Integration Tests (push) Successful in 1m26s
CI/CD Pipeline / Unit Tests (push) Successful in 8m52s
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 / CI Gate (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 / Build Staging API Image (push) Successful in 10m54s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 32s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 40s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 50s
CI/CD Pipeline / Staging API Integration Tests (push) Failing after 1m45s
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
2026-08-03 13:05:48 +08:00
xiaoxia 8ea16aae74 fix: MediaKit 视频理解轮询限时至30s,修复 nginx 504 超时
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m22s
AI Code Review / AI Code Review (pull_request) Successful in 1m40s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m46s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 4m38s
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 / Build Staging Web 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 22s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 28s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 35s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 1m51s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 1m52s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 3m44s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 1m15s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 6m54s
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) Failing after 6s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 41s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 47s
- analyze_videos() 轮询参数从默认(3s*60=180s)改为(2s*15=30s)
- 预留30s给 LLM 调用,总耗时控制在 nginx 60s 限制内
- 超时自动降级到无分析模式,不影响主流程
- 修复 URL→asset_id 映射改用并行列表,消除重复 URL 覆盖风险
2026-08-03 12:40:03 +08:00
xiaoxia 598e2b6fb7 Merge pull request 'feat: #1209 AI智能选片段接入MediaKit视频理解' (#1224) from feat/mediakit-video-understand-issue1209 into develop
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 - Type Check (mypy) (push) Successful in 1m2s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 1m3s
CI/CD Pipeline / Build Staging API Image (push) Successful in 2m38s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 2m42s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 3m39s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 5m15s
CI/CD Pipeline / Integration Tests (push) Successful in 2m55s
CI/CD Pipeline / Unit Tests (push) Successful in 9m58s
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 / CI Gate (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 / Build Staging Worker Image (push) Successful in 12m9s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 54s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 39s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 51s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 2m58s
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
2026-08-03 10:53:55 +08:00
xiaoxia 82bc193693 feat: #1209 AI智能选片段接入MediaKit视频理解
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 / Build Staging Web 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 / Validate - Migration (alembic) (pull_request) Successful in 40s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 45s
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 49s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m35s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m51s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 2m51s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m39s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 2m30s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 4m47s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 4m47s
AI Code Review / AI Code Review (pull_request) Failing after 6m38s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 8m29s
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 12s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 51s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 1m1s
- MediaKitClient 新增 analyze_videos() 方法,调用视频理解智能策略 API
- _call_ai_recommend_service 支持 asset_analyses 参数,将视频内容分析注入 prompt
- API 路由层自动获取素材 URL 并调用 MediaKit 分析视频内容
- LLM 可根据视频实际内容做智能编排(叙事连贯、场景匹配等)
- 降级策略:MediaKit 不可用时保持原有 fallback 行为
- 15 个新单元测试全部通过
2026-08-03 09:43:34 +08:00
xiaoxia 9ac15d8edc fix: 预置音色 preview 改为顺序生成,避免 DashScope 429 限流 (#1223)
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 54s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m11s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 3m7s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 3m7s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 3m49s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 4m26s
CI/CD Pipeline / Integration Tests (push) Successful in 1m33s
CI/CD Pipeline / Build Staging API Image (push) Successful in 6m5s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 32s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 36s
CI/CD Pipeline / Unit Tests (push) Successful in 9m28s
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 / CI Gate (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 / Staging E2E Tests (push) Failing after 2m54s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 3m54s
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
fix: 预置音色 preview 改为顺序生成 (#1223)
2026-08-03 00:36:07 +08:00
xiaoxia 7ef0366de0 feat: 预置音色 preview_url 动态生成,解决签名 URL 过期问题 (#1222)
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Has been skipped
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 47s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m18s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 2m16s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m27s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 4m55s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 5m26s
CI/CD Pipeline / Integration Tests (push) Successful in 1m33s
CI/CD Pipeline / Unit Tests (push) Successful in 8m41s
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 / CI Gate (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 / Build Staging API Image (push) Successful in 12m6s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 34s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 30s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 4m39s
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
CI/CD Pipeline / Staging E2E Tests (push) Failing after 5m7s
feat: 预置音色 preview_url 动态生成 (#1222)

- _resolve_preset_preview_url: 缓存优先+CosyVoice合成+降级fallback
- _resolve_all_preset_preview_urls: ThreadPoolExecutor并行生成
- 修改list_preset_voices/list_voices_unified注入CosyVoiceService
- 13个新单测,117个相关测试全绿
2026-08-02 22:14:12 +08:00
CI Bot 0f73df242e style: auto-format with black + isort + prettier [skip ci-format-check]
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 54s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m13s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m35s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 3m19s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 5m5s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 5m59s
CI/CD Pipeline / Integration Tests (push) Successful in 1m34s
CI/CD Pipeline / Unit Tests (push) Successful in 8m46s
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 / CI Gate (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 / Build Staging API Image (push) Successful in 12m12s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 34s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 35s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 4m4s
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
CI/CD Pipeline / Staging E2E Tests (push) Failing after 4m18s
2026-08-02 08:48:44 +00:00
xiaoxia 3e9873073e feat: 预览渲染进度实时上报(0%→10%→30%→40%→80%→85%→95%→100%)
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 1m5s
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Validate - Code Quality (push) Failing after 5m14s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 52s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 44s
CI/CD Pipeline / Unit Tests (push) Successful in 8m52s
CI/CD Pipeline / Integration Tests (push) Successful in 1m25s
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 3m18s
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 / Build Staging API Image (push) Successful in 12m19s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m32s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 5m24s
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 / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / CI Gate (push) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 1m37s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 7m19s
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
CI/CD Pipeline / Staging E2E Tests (push) Failing after 7m27s
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 / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (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 / Check if frontend-only change (pull_request) Successful in 47s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 48s
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
AI Code Review / AI Code Review (pull_request) Successful in 54s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 56s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m26s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m41s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 24s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 26s
CI/CD Pipeline / Validate - Code Quality (pull_request) Failing after 2m31s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 2m3s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 2m4s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m35s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 1m25s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 7m4s
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) Failing after 6s
新增 _update_task_progress 辅助函数,在渲染流程6个关键节点更新进度:
- 10%: 任务启动
- 30%: 素材下载完成
- 40%: 开始渲染
- 80%: 渲染完成
- 85%: 开始上传OSS
- 95%: 上传完成

修复前端预览生成进度始终显示0%的问题。
2026-08-02 16:42:39 +08:00
xiaoxia 0ef4e1d633 feat: 渲染失败检测+任务超时机制 (#1219)
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Has been skipped
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 / PR Build API Image (push) Has been skipped
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 51s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 59s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m49s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 3m26s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 4m55s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 5m47s
CI/CD Pipeline / Integration Tests (push) Successful in 1m56s
CI/CD Pipeline / Unit Tests (push) Successful in 9m42s
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 / CI Gate (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 / Build Staging API Image (push) Successful in 12m15s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 34s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 37s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 2m27s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 4m39s
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
- 新增 video_validation 模块(moov atom 检测 + ffprobe 验证 + 退出码映射)
- render_adapter 渲染后自动校验输出再上传 OSS
- 三个渲染任务加 10 分钟 soft_time_limit
- Worker 启动时清理 GenerationTask + Job 两张表的孤儿任务
- 21 个新单元测试,全量 13713 测试通过
2026-08-02 15:34:22 +08:00
xiaoxia 380274e635 ci: retrigger CI (AI Code Review timeout)
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 / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E 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 44s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 51s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 57s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m21s
AI Code Review / AI Code Review (pull_request) Successful in 1m27s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m41s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 1m48s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 1m50s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 2m15s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m20s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 3m30s
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 (pull_request) Successful in 1m24s
CI/CD Pipeline / CI Gate (pull_request) Successful in 5s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 37s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 47s
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / PR Build Worker Image (push) Has been skipped
CI/CD Pipeline / PR Build Web Image (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 53s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 59s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 1m40s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m15s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 4m12s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 4m48s
CI/CD Pipeline / Integration Tests (push) Successful in 1m33s
CI/CD Pipeline / Unit Tests (push) Successful in 8m49s
CI/CD Pipeline / Build Production API Image (push) Has been skipped
CI/CD Pipeline / Build Production Worker Image (push) Has been skipped
CI/CD Pipeline / CI Gate (push) Has been skipped
CI/CD Pipeline / Build Production Web Image (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 / Build Staging API Image (push) Successful in 11m20s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 33s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 35s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 4m8s
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
CI/CD Pipeline / Staging E2E Tests (push) Failing after 4m25s
2026-08-02 12:42:00 +08:00
xiaoxia f395337e6f fix: 补齐剩余3个预置音色的preview_url
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 API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E 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 41s
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 54s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 1m0s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m18s
AI Code Review / AI Code Review (pull_request) Failing after 1m40s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m42s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 2m14s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 3m15s
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 / PR Build API Image (pull_request) Successful in 2m45s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 2m45s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m30s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 1m23s
CI/CD Pipeline / CI Gate (pull_request) Successful in 6s
为 longsanshu_v3(龙三叔)、longyingjing_v3(龙应静)、longshuo_v3(龙硕)
填充 CosyVoice 生成的试听音频 URL(DashScope 签名 URL)。

至此8个预置音色全部拥有 preview_url,#1195 试听功能完整工作。
2026-08-02 12:33:32 +08:00
xiaoxia 2f26541188 fix: 修复预置音色voice_id并填充preview_url (#1216)
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
CI/CD Pipeline / PR Build Worker Image (push) Has been skipped
CI/CD Pipeline / PR Build Web Image (push) Has been skipped
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m16s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 1m36s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m26s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 3m17s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 3m46s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 5m9s
CI/CD Pipeline / Integration Tests (push) Successful in 1m52s
CI/CD Pipeline / Unit Tests (push) Successful in 8m39s
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 / CI Gate (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 / Build Staging API Image (push) Successful in 12m15s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 31s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 40s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 45s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 3m1s
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
2026-08-02 08:55:14 +08:00
xiaoxia 62909de618 Merge pull request 'fix(#1197): 修复预览结果渲染 React Error #31 — 对象被直接渲染为 React child' (#1215) from fix/1197-react-error-31 into develop
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 - Type Check (mypy) (push) Successful in 1m3s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 1m28s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 3m27s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 3m54s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 4m42s
CI/CD Pipeline / Integration Tests (push) Successful in 1m44s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 7m12s
CI/CD Pipeline / Unit Tests (push) Successful in 11m18s
CI/CD Pipeline / Build Production API Image (push) Has been skipped
CI/CD Pipeline / Build Production Worker Image (push) Has been skipped
CI/CD Pipeline / Build Production Web Image (push) Has been skipped
CI/CD Pipeline / CI Gate (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 / Build Staging API Image (push) Successful in 14m41s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 31s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 46s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 47s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 3m4s
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
2026-08-02 08:25:58 +08:00
前端Agent d8a4c21556 fix(#1197): 修复预览结果渲染 React Error #31 — 对象被直接渲染为 React child
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 / Build Staging Web 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 33s
CI/CD Pipeline / Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build Worker Image (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 1m1s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 1m2s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m44s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Successful in 1m19s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m59s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 1m38s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 2m16s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 1m46s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 4m0s
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 / Canary Release to Production (pull_request) Has been skipped
CI/CD Pipeline / Deploy 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 6s
AI Code Review / AI Code Review (pull_request) Successful in 4m29s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 39s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 45s
根因:useStep3Preview 轮询失败时直接将 data.error_message 设为 state,
当后端返回对象类型(如 {} 或 {detail: "..."})时,
Step3GeneratePreview / PreviewVideoPanel 将其作为 React child 渲染,
触发 Error #31 "Objects are not valid as a React child"。

修复:
1. useStep3Preview.ts — 复用 safeExtractError 封装 safeString/safeNumber,
   确保 error_message、task_id、video_url 等字段始终为字符串,
   clip_count、duration 等字段始终为数字
2. Step3GeneratePreview.tsx — previewError 增加 typeof 守卫
3. PreviewVideoPanel.tsx — previewError 增加 typeof 守卫
2026-08-02 08:17:42 +08:00
xiaoxia 92b29de511 feat: #1208 AI封面接入MediaKit视频截帧 (#1214)
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 54s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m27s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m22s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 4m44s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 4m45s
CI/CD Pipeline / Integration Tests (push) Successful in 1m16s
CI/CD Pipeline / Unit Tests (push) Successful in 8m25s
CI/CD Pipeline / Frontend Unit Tests (push) Failing after 11m23s
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 / CI Gate (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 / Build Staging API Image (push) Successful in 11m38s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 31s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 37s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 1m44s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 4m20s
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
2026-08-02 01:14:02 +08:00
xiaoxia 4bbc71d9d1 fix: _download_video_assets 允许仅传 asset_ids(模板预览模式) (#1213)
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 1m3s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m13s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 3m2s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 3m2s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 3m44s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 4m54s
CI/CD Pipeline / Build Staging API Image (push) Successful in 5m59s
CI/CD Pipeline / Integration Tests (push) Successful in 1m34s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 34s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 36s
CI/CD Pipeline / Unit Tests (push) Successful in 8m53s
CI/CD Pipeline / Build Production API Image (push) Has been skipped
CI/CD Pipeline / Build Production Worker Image (push) Has been skipped
CI/CD Pipeline / Build Production Web Image (push) Has been skipped
CI/CD Pipeline / CI Gate (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 / Staging API Integration Tests (push) Successful in 4m12s
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
CI/CD Pipeline / Staging E2E Tests (push) Failing after 5m9s
2026-08-01 21:59:19 +08:00
xiaoxia 2bb8cc3bb3 Merge pull request 'fix: nginx index.html 禁用浏览器缓存,防止部署后旧 chunk 引用导致白屏' (#1212) from fix/nginx-index-no-cache into develop
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 - Type Check (mypy) (push) Successful in 47s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 1m5s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m31s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 3m18s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 4m20s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 5m37s
CI/CD Pipeline / Integration Tests (push) Successful in 1m27s
CI/CD Pipeline / Unit Tests (push) Successful in 8m36s
CI/CD Pipeline / Build Production API Image (push) Has been skipped
CI/CD Pipeline / Build Production Worker Image (push) Has been skipped
CI/CD Pipeline / Build Production Web Image (push) Has been skipped
CI/CD Pipeline / CI Gate (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 / Build Staging API Image (push) Successful in 12m37s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 32s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 37s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 1m28s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 3m54s
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
2026-08-01 18:28:49 +08:00
前端Agent df38778875 fix: 禁止浏览器缓存 index.html,防止部署后旧 chunk 引用导致白屏
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 / 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 / Validate - Migration (alembic) (pull_request) Successful in 33s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 41s
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 54s
AI Code Review / AI Code Review (pull_request) Successful in 1m18s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m23s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m53s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 1m52s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 1m53s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 3m17s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m31s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 1m15s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 6m50s
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 6s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 55s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 1m20s
- nginx.conf 和 nginx-staging.conf 新增 location = /index.html 精确匹配块
- 设置 Cache-Control: no-cache, no-store, must-revalidate
- 设置 Pragma: no-cache 和 expires 0
- 确保每次部署后浏览器获取最新的 index.html(含新 chunk hash)
2026-08-01 18:19:50 +08:00
CI Bot 09b8a9b52b style: auto-format with black + isort + prettier [skip ci-format-check]
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
CI/CD Pipeline / PR Build Worker Image (push) Has been skipped
CI/CD Pipeline / PR Build Web Image (push) Has been skipped
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 59s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 1m3s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 3m16s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 3m21s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 5m2s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 5m14s
CI/CD Pipeline / Integration Tests (push) Successful in 1m19s
CI/CD Pipeline / Unit Tests (push) Successful in 8m47s
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 / CI Gate (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 / Build Staging API Image (push) Successful in 12m7s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 32s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 34s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 1m39s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 3m58s
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
2026-08-01 17:08:38 +08:00
xiaoxia fea91876f9 fix(#1197): AI Code Review第3轮修复
1. 权限校验str()转换,避免UUID/str类型不匹配
2. 配置写回注释说明,明确拷贝-修改-写回流程完整
2026-08-01 17:08:38 +08:00
xiaoxia 7f960600da fix(#1197): 修复AI Code Review阻塞级问题
1. 预览分辨率按video_ratio动态计算(支持9:16竖屏/1:1/4:3等)
2. 入队失败时标记任务为failed,避免僵尸pending数据
2026-08-01 17:08:38 +08:00
xiaoxia 046610ae24 fix(#1197): 修复AI Code Review两个阻塞级问题
1. generation.py: 预览模式拷贝dict避免污染源对象(模板配置)
2. generation_preview.py: 权限校验修复——created_by_user_id为空时拒绝访问
3. 新增empty_user_id测试用例,验证越权防护
2026-08-01 17:08:38 +08:00
xiaoxia bbddcb076a test(#1197): 补充路由层+Worker层测试,提升增量覆盖率至60%+
- 新增 _to_preview_response 辅助函数测试(7个用例)
- 新增 POST /preview 路由测试(8个用例:成功/429/503/400/500/入队失败)
- 新增 GET /preview/{task_id} 路由测试(5个用例:404/403/非预览/状态查询/结果查询)
- 新增 Worker 预览分辨率强制逻辑测试(4个用例)
- 总计 24 个新测试,累计 53 个测试全绿
2026-08-01 17:08:38 +08:00
xiaoxia 73af3851cb ci: re-trigger CI after infrastructure fix (run#20321+) 2026-08-01 17:08:38 +08:00
xiaoxia 77ea4f607c ci: re-trigger CI (ci-base image check) 2026-08-01 17:08:38 +08:00
xiaoxia 3b6b32d561 feat(#1197): Phase 1 预览生成接口(单版本预览)
实现预览生成功能的后端 Phase 1:单版本预览接口(创建 + 查询)。

变更内容:
- 领域模型 GenerationTask 新增 is_preview: bool = False 字段
- SQLAlchemy GenerationTaskModel 新增 is_preview 列(带索引)
- 新增 alembic migration 053_generation_task_is_preview
- 仓储层 _to_domain/create/update 同步 is_preview 字段(getattr 兼容旧数据)
- CreateGenerationTaskCommand/UseCase 新增 is_preview 参数
- Schema 新增 CreatePreviewGenerationTaskRequest 和 PreviewGenerationTaskResponse
- 新增 /api/v1/generation/preview 路由(POST 创建 + GET 查询)
- Worker generate_video 检测 is_preview=True 时强制 854x480 + 1M 低码率
- 新增 29 个单元测试,全部通过

验收:
-  所有新增文件写完,所有修改点完成
-  单元测试 29 个,全部通过
-  现有 generation 相关 174 个测试全部通过
-  代码通过 ruff + black 检查
-  在 feat/preview-generation-1197 分支上
2026-08-01 17:08:38 +08:00
xiaoxia 911c3b6866 Merge pull request 'fix(#1196): TTS面板克隆音色试听改用/voice-clones/{id}/preview接口' (#1205) from fix/tts-clone-voice-preview-1196 into develop
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 49s
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Validate - Code Quality (push) Successful in 5m20s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m3s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 54s
CI/CD Pipeline / Unit Tests (push) Successful in 8m34s
CI/CD Pipeline / Integration Tests (push) Successful in 3m7s
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 3m49s
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 / Build Staging API Image (push) Successful in 16m0s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 6m3s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 3m33s
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 / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / CI Gate (push) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 32s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 1m26s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 4m23s
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
2026-08-01 16:29:54 +08:00
前端Agent b7ef9fed14 fix(#1196): TTS面板克隆音色试听改用/voice-clones/{id}/preview接口
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 24s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 44s
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 / Validate - Migration (alembic) (pull_request) Successful in 42s
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 / PR Build Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m16s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m46s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 3m6s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Successful in 55s
AI Code Review / AI Code Review (pull_request) Successful in 2m55s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m14s
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 / Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 2m28s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 2m38s
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 35s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 33s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 36s
- VoiceSelector: 克隆音色试听按钮加loading态,点击时显示合成中
- useTtsPanel: handlePlayVoiceSample接收完整voice对象,区分preset/clone
- 克隆音色调用/voice-clones/{id}/preview接口实时合成试听音频
- 预设音色保持原有逻辑直接播放preview_url
- 统一错误处理和竞态防护
2026-08-01 16:22:48 +08:00
xiaoxia 25bbfdda6b Merge pull request 'fix(#1195): 我的音色页面试听按钮对接/voice-clones/{id}/preview接口' (#1204) from fix/voice-clone-preview-frontend-1195 into develop
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Validate - Code Quality (push) Successful in 4m38s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 53s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 42s
CI/CD Pipeline / Unit Tests (push) Successful in 8m49s
CI/CD Pipeline / Integration Tests (push) Successful in 2m54s
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 3m14s
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 / Build Staging API Image (push) Successful in 17m43s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m38s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 6m51s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 1m17s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 1m37s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 3m52s
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 / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 42s
CI/CD Pipeline / CI Gate (push) Has been skipped
2026-08-01 16:20:34 +08:00
前端Agent 74f146bbaf fix(#1195): 我的音色页面试听按钮对接/voice-clones/{id}/preview接口
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 / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 40s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 52s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 56s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m21s
CI/CD Pipeline / Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build API Image (pull_request) Has been skipped
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 2m9s
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Successful in 47s
AI Code Review / AI Code Review (pull_request) Successful in 2m22s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 1m47s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m20s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 1m50s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 4m54s
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 6s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 38s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 49s
- API层新增VoiceClonePreviewResponse类型和getVoiceClonePreview函数
- useMyVoices hook播放逻辑改为先调接口获取音频URL再播放
- 新增loadingPreviewId状态,试听合成中显示加载态
- VoiceCard增加isLoading prop,加载时按钮显示"合成中…"并禁用
- 错误处理:接口失败Toast提示,播放失败友好提示
2026-08-01 16:11:48 +08:00
xiaoxia 3cbefef773 fix(ci): vitest增量无测试文件视为通过 + ci-base添加git
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / PR Build Worker Image (push) Has been skipped
CI/CD Pipeline / PR Build Web Image (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 1m0s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m24s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m19s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 3m6s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 4m42s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 5m8s
CI/CD Pipeline / Integration Tests (push) Successful in 1m33s
CI/CD Pipeline / Unit Tests (push) Successful in 8m22s
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 / Deploy Production (push) Has been skipped
CI/CD Pipeline / CI Gate (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Build Staging API Image (push) Successful in 13m23s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 32s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 31s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 2m1s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 4m27s
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
2026-08-01 15:37:47 +08:00
xiaoxia ba1120dec5 feat(#1197): Phase1 预览生成前端改造 (#1206)
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 51s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m35s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 3m5s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 3m27s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 4m49s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 5m37s
CI/CD Pipeline / Integration Tests (push) Successful in 1m22s
CI/CD Pipeline / Unit Tests (push) Successful in 8m12s
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 / CI Gate (push) Has been skipped
CI/CD Pipeline / Build Production Worker Image (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 / Build Staging API Image (push) Successful in 12m42s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 32s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 35s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 1m42s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 4m6s
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com>
Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
2026-08-01 14:26:00 +08:00
xiaoxia 49a56afc51 fix(ci): 添加docker CLI到ci-base镜像,支持buildx构建
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 - Type Check (mypy) (push) Successful in 1m2s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 1m11s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m17s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 3m31s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 4m8s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 5m52s
CI/CD Pipeline / Unit Tests (push) Successful in 8m41s
CI/CD Pipeline / Integration Tests (push) Successful in 3m10s
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 / CI Gate (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 / Build Staging API Image (push) Successful in 18m13s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 43s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 55s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 20m18s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 22m48s
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
2026-08-01 01:45:07 +08:00
xiaoxia abe856b0b6 ci: 验证docker socket挂载修复
CI/CD Pipeline / Build Production API Image (push) Has been skipped
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Validate - Code Quality (push) Successful in 3m15s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m9s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 1m0s
CI/CD Pipeline / Unit Tests (push) Failing after 13m5s
CI/CD Pipeline / Integration Tests (push) Failing after 1m1s
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 1m48s
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 / Build Staging API Image (push) Failing after 57s
CI/CD Pipeline / Build Staging Web Image (push) Failing after 1m1s
CI/CD Pipeline / Build Staging Worker Image (push) Failing after 48s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been skipped
CI/CD Pipeline / Staging E2E Tests (push) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (push) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (push) Has been skipped
CI/CD Pipeline / Build Production Web Image (push) Has been skipped
CI/CD Pipeline / CI Gate (push) Has been skipped
CI/CD Pipeline / Build Production Worker Image (push) Has been skipped
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
2026-08-01 01:42:21 +08:00
xiaoxia 455ec8f561 ci: 触发新CI验证新镜像
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 51s
CI/CD Pipeline / Build Staging API Image (push) Failing after 1m2s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m3s
CI/CD Pipeline / Build Staging Worker Image (push) Failing after 1m2s
CI/CD Pipeline / Build Staging Web Image (push) Failing after 1m5s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been skipped
CI/CD Pipeline / Staging E2E Tests (push) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (push) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (push) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 1m56s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 3m44s
CI/CD Pipeline / Integration Tests (push) Failing after 53s
CI/CD Pipeline / Unit Tests (push) Successful in 7m19s
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 / CI Gate (push) Has been skipped
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
2026-08-01 00:47:44 +08:00
xiaoxia 24995e01d3 fix(ci): Dockerfile添加阿里云apt源替换,加速构建
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-08-01 00:32:42 +08:00
xiaoxia b168f1c582 fix(ci): psycopg2-binary降级到2.9.9,阿里云pip镜像没有2.9.10
Worker Base Image Build / Build Worker Base Images (worker-base-builder-cache, infra/docker/worker-base-builder.Dockerfile, worker-base-builder, builder) (push) Failing after 23s
Worker Base Image Build / Build Worker Base Images (worker-base-runtime-cache, infra/docker/worker-base-runtime.Dockerfile, worker-base-runtime, runtime) (push) Failing after 28s
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-08-01 00:30:32 +08:00
xiaoxia f7284534bc fix(ci): ci-base镜像添加libpq-dev/nodejs/npm,预装requirements-base.txt
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-08-01 00:26:38 +08:00
xiaoxia 446a9029e8 fix(ci): 加固checkout步骤,curl加-f标志并改为下载后执行,防止静默失败
CI/CD Pipeline / Build Production Worker Image (push) Has been skipped
CI/CD Pipeline / Build Production Web Image (push) Has been skipped
CI/CD Pipeline / Build Production API Image (push) Has been skipped
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Validate - Code Quality (push) Failing after 29m13s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Failing after 28m8s
CI/CD Pipeline / Validate - Migration (alembic) (push) Failing after 11m57s
CI/CD Pipeline / Unit Tests (push) Failing after 26m25s
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (push) Failing after 52s
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 / Build Staging API Image (push) Failing after 51s
CI/CD Pipeline / Build Staging Web Image (push) Failing after 59s
CI/CD Pipeline / Build Staging Worker Image (push) Failing after 55s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been skipped
CI/CD Pipeline / Staging E2E Tests (push) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (push) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (push) Has been skipped
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Integration Tests (push) Failing after 2m3s
CI/CD Pipeline / CI Gate (push) Has been skipped
2026-08-01 00:26:15 +08:00
xiaoxia 90a39d74ff fix(ci): ci-base镜像添加curl,修复checkout步骤静默失败
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / PR Build Worker Image (push) Has been skipped
CI/CD Pipeline / PR Build Web Image (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (push) Failing after 45s
CI/CD Pipeline / Build Staging Worker Image (push) Failing after 51s
CI/CD Pipeline / Build Staging Web Image (push) Failing after 53s
CI/CD Pipeline / Build Staging API Image (push) Failing after 55s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been skipped
CI/CD Pipeline / Staging E2E Tests (push) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (push) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (push) Has been skipped
CI/CD Pipeline / Validate - Type Check (mypy) (push) Failing after 4m13s
CI/CD Pipeline / Validate - Code Quality (push) Failing after 14m24s
CI/CD Pipeline / Validate - Migration (alembic) (push) Failing after 31m28s
CI/CD Pipeline / Integration Tests (push) Failing after 9m2s
CI/CD Pipeline / Unit Tests (push) Successful in 1h13m16s
CI/CD Pipeline / Build Production Web Image (push) Has been skipped
CI/CD Pipeline / Build Production API Image (push) Has been skipped
CI/CD Pipeline / Build Production Worker Image (push) Has been skipped
CI/CD Pipeline / CI Gate (push) Has been skipped
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
ci-base镜像缺少curl导致checkout步骤(curl|bash)静默失败,
所有后续步骤因代码未检出而失败。添加curl安装并使用阿里云pip镜像加速构建。
2026-07-31 21:56:43 +08:00
xiaoxia a62cadf654 fix(ci): 更新ci.Dockerfile - 移除已删除的requirements-quality.txt,flake8替换为ruff
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Validate - Code Quality (push) Failing after 1s
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / Validate - Type Check (mypy) (push) Failing after 1s
CI/CD Pipeline / Validate - Migration (alembic) (push) Failing after 2s
CI/CD Pipeline / Build Staging API Image (push) Failing after 1s
CI/CD Pipeline / Build Staging Web Image (push) Failing after 1s
CI/CD Pipeline / Build Staging Worker Image (push) Failing after 1s
CI/CD Pipeline / Unit Tests (push) Failing after 1s
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 / Deploy Staging (Watchtower auto-deploy) (push) Has been skipped
CI/CD Pipeline / Staging E2E Tests (push) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (push) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (push) Failing after 1s
CI/CD Pipeline / Integration Tests (push) Failing after 1s
CI/CD Pipeline / ACR Image Cleanup (push) Has been skipped
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 / CI Gate (push) Has been skipped
CI/CD Pipeline / Build Production Worker Image (push) Has been skipped
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
- requirements-quality.txt已合并到requirements-dev.txt
- flake8已被ruff替代(lint工具升级)
- 保持与实际构建的ci-base镜像一致
2026-07-31 19:35:07 +08:00
xiaoxia dc38c3fa8b refactor(BindContactModal): 绑定联系方式弹窗目录化拆分(180→119行, -34%) (#1183)
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 / Build Staging Worker Image (push) Successful in 3m26s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 2m26s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 3m59s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 4m22s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 5m22s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 6m17s
CI/CD Pipeline / Integration Tests (push) Successful in 2m51s
CI/CD Pipeline / Unit Tests (push) Successful in 11m48s
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 / CI Gate (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 / Build Staging API Image (push) Successful in 12m46s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 29s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 35s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 3m7s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 5m47s
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
2026-07-30 19:58:49 +08:00
xiaoxia 73a3606626 fix(#1196): 智能剪辑TTS面板改用统一/voices接口,支持克隆音色选择 (#1199)
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 45s
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Validate - Code Quality (push) Successful in 6m9s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 3m1s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 2m46s
CI/CD Pipeline / Unit Tests (push) Successful in 10m51s
CI/CD Pipeline / Integration Tests (push) Successful in 2m49s
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 3m16s
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 / Build Staging API Image (push) Successful in 12m31s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 5m11s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 3m24s
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 / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / CI Gate (push) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 33s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 4m30s
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
CI/CD Pipeline / Staging E2E Tests (push) Failing after 4m46s
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com>
Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
2026-07-30 17:11:16 +08:00
xiaoxia a05cc94fdb fix(#1197): 第3步生成预览增加真正的预览生成逻辑,禁止直接跳过 (#1200)
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Validate - Code Quality (push) Successful in 5m4s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 2m3s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 2m26s
CI/CD Pipeline / Unit Tests (push) Successful in 10m32s
CI/CD Pipeline / Integration Tests (push) Successful in 3m3s
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 3m38s
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 / Build Staging API Image (push) Successful in 13m23s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 3m5s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 5m48s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 1m13s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 2m52s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 4m58s
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 / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 43s
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
CI/CD Pipeline / CI Gate (push) Has been skipped
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com>
Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
2026-07-30 17:06:12 +08:00
xiaoxia c861e90289 feat(#1195): 新增克隆音色试听接口 /voice-clones/{id}/preview (#1201)
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Validate - Code Quality (push) Successful in 6m40s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 3m23s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 3m45s
CI/CD Pipeline / Unit Tests (push) Successful in 10m32s
CI/CD Pipeline / Integration Tests (push) Successful in 2m21s
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 2m22s
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 / Build Staging API Image (push) Successful in 12m24s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m27s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 5m3s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 1m10s
CI/CD Pipeline / Staging E2E Tests (push) Successful in 2m51s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 5m16s
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 / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 41s
CI/CD Pipeline / CI Gate (push) Has been skipped
2026-07-30 16:47:50 +08:00
xiaoxia e6effdb6f2 fix(#1195): 克隆音色增加试听播放功能,预留后端试听接口对接位 (#1203)
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 2m27s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 2m34s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 3m17s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 3m23s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 5m45s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 6m32s
CI/CD Pipeline / Integration Tests (push) Successful in 2m28s
CI/CD Pipeline / Unit Tests (push) Successful in 10m6s
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 / CI Gate (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 / Build Staging API Image (push) Successful in 13m1s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 29s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 34s
CI/CD Pipeline / Staging E2E Tests (push) Successful in 2m52s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 5m5s
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com>
Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
2026-07-30 16:11:12 +08:00
xiaoxia 265ec7460b feat(#1196): /voices?type=clone从克隆音色档案读取(智能剪辑配音可选克隆音色) (#1202)
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 / Frontend Unit Tests (push) Successful in 1m29s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 2m23s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 3m15s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 4m18s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 4m57s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 6m9s
CI/CD Pipeline / Integration Tests (push) Successful in 2m39s
CI/CD Pipeline / Unit Tests (push) Successful in 10m33s
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 / CI Gate (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 / Build Staging API Image (push) Successful in 12m15s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 30s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 34s
CI/CD Pipeline / Staging E2E Tests (push) Successful in 3m37s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 6m16s
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com>
Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
2026-07-30 14:46:12 +08:00
xiaoxia 406fd20e0e fix(test): batch_download测试适配celery未初始化场景 (#1198)
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 / Frontend Unit Tests (push) Successful in 1m42s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m50s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 1m58s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 3m18s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 3m19s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 5m17s
CI/CD Pipeline / Build Staging API Image (push) Successful in 6m13s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 28s
CI/CD Pipeline / Integration Tests (push) Successful in 2m0s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 39s
CI/CD Pipeline / Unit Tests (push) Successful in 8m46s
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 / CI Gate (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 / Staging API Integration Tests (push) Successful in 4m38s
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
CI/CD Pipeline / Staging E2E Tests (push) Failing after 5m5s
2026-07-30 13:23:45 +08:00
xiaoxia 223bb530dd refactor(StickerPanel): 贴纸面板目录化拆分(516→103行, -80%) (#1191)
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 2m20s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 2m28s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 3m21s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 3m49s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 5m18s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 5m41s
CI/CD Pipeline / Integration Tests (push) Successful in 2m25s
CI/CD Pipeline / Unit Tests (push) Failing after 10m1s
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 / CI Gate (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 / Build Staging API Image (push) Successful in 12m34s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 30s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 33s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 4m37s
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
CI/CD Pipeline / Staging E2E Tests (push) Failing after 5m1s
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com>
Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
2026-07-30 12:16:12 +08:00
xiaoxia 44e6c8e10f refactor(PipConfigPanel): 混剪配置面板目录化拆分(547→106行, -81%) (#1192)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com>
Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
2026-07-30 12:13:47 +08:00
xiaoxia cfe97ea059 refactor(TtsPanel): TTS配音面板目录化拆分(332→173行, -48%) (#1193)
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 56s
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Validate - Code Quality (push) Successful in 6m55s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 2m40s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 2m54s
CI/CD Pipeline / Unit Tests (push) Failing after 10m45s
CI/CD Pipeline / Integration Tests (push) Successful in 2m14s
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 2m58s
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 / Build Staging API Image (push) Successful in 13m30s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 5m42s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 3m31s
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 / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / CI Gate (push) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 2m31s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 7m21s
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
CI/CD Pipeline / Staging E2E Tests (push) Failing after 7m49s
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com>
Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
2026-07-30 12:06:17 +08:00
xiaoxia 4f09989df5 test(wave207): InMemory小型仓储单测补全 +41测
CI/CD Pipeline / Check if frontend-only change (push) Blocked by required conditions
CI/CD Pipeline / Validate - Code Quality (push) Blocked by required conditions
CI/CD Pipeline / Validate - Type Check (mypy) (push) Blocked by required conditions
CI/CD Pipeline / Validate - Migration (alembic) (push) Blocked by required conditions
CI/CD Pipeline / Unit Tests (push) Blocked by required conditions
CI/CD Pipeline / Integration Tests (push) Blocked by required conditions
CI/CD Pipeline / Frontend Lint (push) Blocked by required conditions
CI/CD Pipeline / Frontend Unit Tests (push) Blocked by required conditions
CI/CD Pipeline / PR Build API Image (push) Blocked by required conditions
CI/CD Pipeline / PR Build Web Image (push) Blocked by required conditions
CI/CD Pipeline / PR Build Worker Image (push) Blocked by required conditions
CI/CD Pipeline / Build Staging API Image (push) Blocked by required conditions
CI/CD Pipeline / Build Staging Web Image (push) Blocked by required conditions
CI/CD Pipeline / Build Staging Worker Image (push) Blocked by required conditions
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Blocked by required conditions
CI/CD Pipeline / Staging E2E Tests (push) Blocked by required conditions
CI/CD Pipeline / Staging API Integration Tests (push) Blocked by required conditions
CI/CD Pipeline / Build Production API Image (push) Blocked by required conditions
CI/CD Pipeline / Build Production Web Image (push) Blocked by required conditions
CI/CD Pipeline / Build Production Worker Image (push) Blocked by required conditions
CI/CD Pipeline / Deploy Production (push) Blocked by required conditions
CI/CD Pipeline / Production Browser E2E (push) Blocked by required conditions
CI/CD Pipeline / ACR Image Cleanup (push) Blocked by required conditions
CI/CD Pipeline / Canary Release to Production (push) Blocked by required conditions
CI/CD Pipeline / CI Gate (push) Blocked by required conditions
2026-07-30 11:50:48 +08:00
xiaoxia 613c75d041 refactor(CloneModal): 音色克隆弹窗目录化拆分(602→440行, -27%) (#1186)
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Has been skipped
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 / Frontend Unit Tests (push) Successful in 1m54s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 2m5s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 2m33s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 3m18s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 5m22s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 6m27s
CI/CD Pipeline / Integration Tests (push) Successful in 3m28s
CI/CD Pipeline / Unit Tests (push) Failing after 10m19s
CI/CD Pipeline / Build Production API Image (push) Has been skipped
CI/CD Pipeline / Build Production Worker Image (push) Has been skipped
CI/CD Pipeline / CI Gate (push) Has been skipped
CI/CD Pipeline / Build Production Web Image (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 / Build Staging API Image (push) Successful in 14m30s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 33s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 35s
CI/CD Pipeline / Staging E2E Tests (push) Successful in 1m42s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 3m51s
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com>
Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
2026-07-30 11:01:16 +08:00
xiaoxia fb29f058e9 refactor(TimelinePanel): 时间线面板拆分(317→226行, -29%) (#1178)
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 1m13s
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Validate - Code Quality (push) Successful in 5m8s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m25s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 2m8s
CI/CD Pipeline / Unit Tests (push) Failing after 8m53s
CI/CD Pipeline / Integration Tests (push) Successful in 3m29s
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 2m42s
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 / Build Staging API Image (push) Successful in 16m56s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m43s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 7m47s
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 / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / CI Gate (push) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 1m40s
CI/CD Pipeline / Staging E2E Tests (push) Successful in 2m50s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 5m21s
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com>
Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
2026-07-30 10:44:48 +08:00
xiaoxia e27d6d9429 test(wave219): template_clip_config +33测 (#1194)
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m56s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 2m23s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m59s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 5m21s
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 / Frontend Unit Tests (push) Successful in 1m34s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 8m30s
CI/CD Pipeline / Integration Tests (push) Successful in 3m24s
CI/CD Pipeline / Unit Tests (push) Failing after 7m46s
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 / CI Gate (push) Has been skipped
CI/CD Pipeline / Build Staging API Image (push) Successful in 19m35s
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 1m45s
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 42s
CI/CD Pipeline / Staging E2E Tests (push) Successful in 2m32s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 4m17s
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
2026-07-30 09:59:59 +08:00
xiaoxia 540500d9f8 test(wave218): batch_download +16测 (#1190)
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m3s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 1m35s
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 / Build Staging Web Image (push) Successful in 2m18s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 2m37s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 5m3s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 8m25s
CI/CD Pipeline / Integration Tests (push) Successful in 4m7s
CI/CD Pipeline / Unit Tests (push) Failing after 9m31s
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 / CI Gate (push) Has been skipped
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Build Staging API Image (push) Successful in 20m50s
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
2026-07-30 09:34:07 +08:00
xiaoxia 675945f4fb test: wave217 title_library use cases +29单测(8个UC含智能选标题) (#1189)
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Validate - Code Quality (push) Successful in 6m5s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 2m3s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 2m7s
CI/CD Pipeline / Unit Tests (push) Successful in 8m25s
CI/CD Pipeline / Integration Tests (push) Successful in 2m58s
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 2m37s
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 / Build Staging API Image (push) Successful in 14m42s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m1s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 3m3s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 48s
CI/CD Pipeline / Staging E2E Tests (push) Successful in 1m36s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 4m1s
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 / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 45s
CI/CD Pipeline / CI Gate (push) Has been skipped
2026-07-30 08:45:19 +08:00
xiaoxia 5e74d0b565 test: wave216 template use cases +46单测(12个UC全量覆盖) (#1188)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-07-30 08:45:15 +08:00
xiaoxia ebe68429bc test: wave215 video_share +77单测(领域模型 + 9个Use Cases) (#1187)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-07-30 08:44:53 +08:00
xiaoxia 18f534bbd6 refactor(PageHead): 页面头部组件目录化拆分(182→97行, -47%) (#1177)
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Validate - Code Quality (push) Successful in 5m15s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 2m3s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 2m22s
CI/CD Pipeline / Unit Tests (push) Successful in 7m55s
CI/CD Pipeline / Integration Tests (push) Successful in 3m4s
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 1m42s
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 / Build Staging API Image (push) Successful in 19m15s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m35s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 8m9s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 1m38s
CI/CD Pipeline / Staging E2E Tests (push) Blocked by required conditions
CI/CD Pipeline / Staging API Integration Tests (push) Blocked by required conditions
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 / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (push) Blocked by required conditions
CI/CD Pipeline / Canary Release to Production (push) Blocked by required conditions
CI/CD Pipeline / CI Gate (push) Has been skipped
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com>
Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
2026-07-30 08:40:05 +08:00
xiaoxia f36aaea374 test(wave213): 绑定联系方式UseCase单测 +41测 (#1184)
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 1m27s
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Validate - Code Quality (push) Successful in 5m15s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 2m3s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 2m22s
CI/CD Pipeline / Unit Tests (push) Successful in 7m55s
CI/CD Pipeline / Integration Tests (push) Successful in 3m4s
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 1m42s
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 / Build Staging API Image (push) Successful in 19m15s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m35s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 8m9s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 1m38s
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 / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / CI Gate (push) Has been skipped
CI/CD Pipeline / Staging E2E Tests (push) Successful in 2m28s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 4m49s
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
2026-07-30 08:20:36 +08:00
xiaoxia 80bc63d58b test: wave214 text_splitter +33单测(TTS长文本分段工具) (#1185)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-07-30 08:20:02 +08:00
xiaoxia 73a566c621 test(wave212): 登录UseCase单测补全 +47测 (#1182)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-07-30 08:19:40 +08:00
xiaoxia 9057ba25c8 test(wave202): multi_track_mixer_pure 单测补全 +103测 (#1169)
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 1m1s
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Validate - Code Quality (push) Successful in 4m38s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m45s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 1m39s
CI/CD Pipeline / Unit Tests (push) Successful in 9m3s
CI/CD Pipeline / Integration Tests (push) Successful in 2m7s
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 2m42s
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 / Build Staging API Image (push) Successful in 17m33s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m4s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 6m28s
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 / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / CI Gate (push) Has been skipped
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
2026-07-30 07:50:43 +08:00
xiaoxia 35b38ed48c test(wave211): 密码重置UseCase单测 +25测 (#1181)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-07-30 07:45:04 +08:00
xiaoxia 15142e3168 test(wave210): 验证码服务单测补全 +70测 (#1180)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-07-30 07:40:32 +08:00
xiaoxia 953dd9a6e6 test(wave203): bgm_mixer_pure 单测补全 +88测 (#1170)
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Validate - Code Quality (push) Successful in 4m40s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m57s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 2m38s
CI/CD Pipeline / Unit Tests (push) Successful in 9m44s
CI/CD Pipeline / Integration Tests (push) Successful in 2m21s
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 3m36s
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 / Build Staging API Image (push) Successful in 20m7s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 3m4s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 9m5s
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 / CI Gate (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 59s
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
2026-07-30 07:35:15 +08:00
xiaoxia 060307197c test(wave209): JWT服务与处理器单测补全 +64测 (#1179)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-07-30 07:34:57 +08:00
xiaoxia 6930d4543f test(wave191): edit_template 剪辑模板实体 +35测 (#1153)
CI/CD Pipeline / Check if frontend-only change (push) Blocked by required conditions
CI/CD Pipeline / Validate - Code Quality (push) Blocked by required conditions
CI/CD Pipeline / Validate - Type Check (mypy) (push) Blocked by required conditions
CI/CD Pipeline / Validate - Migration (alembic) (push) Blocked by required conditions
CI/CD Pipeline / Unit Tests (push) Blocked by required conditions
CI/CD Pipeline / Integration Tests (push) Blocked by required conditions
CI/CD Pipeline / Frontend Lint (push) Blocked by required conditions
CI/CD Pipeline / Frontend Unit Tests (push) Blocked by required conditions
CI/CD Pipeline / PR Build API Image (push) Blocked by required conditions
CI/CD Pipeline / PR Build Web Image (push) Blocked by required conditions
CI/CD Pipeline / PR Build Worker Image (push) Blocked by required conditions
CI/CD Pipeline / Build Staging API Image (push) Blocked by required conditions
CI/CD Pipeline / Build Staging Web Image (push) Blocked by required conditions
CI/CD Pipeline / Build Staging Worker Image (push) Blocked by required conditions
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Blocked by required conditions
CI/CD Pipeline / Staging E2E Tests (push) Blocked by required conditions
CI/CD Pipeline / Staging API Integration Tests (push) Blocked by required conditions
CI/CD Pipeline / Build Production API Image (push) Blocked by required conditions
CI/CD Pipeline / Build Production Web Image (push) Blocked by required conditions
CI/CD Pipeline / Build Production Worker Image (push) Blocked by required conditions
CI/CD Pipeline / Deploy Production (push) Blocked by required conditions
CI/CD Pipeline / Production Browser E2E (push) Blocked by required conditions
CI/CD Pipeline / ACR Image Cleanup (push) Blocked by required conditions
CI/CD Pipeline / CI Gate (push) Blocked by required conditions
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
2026-07-30 07:03:55 +08:00
xiaoxia 7b2f35ad3d test(wave200): pip_engine_pure 单测补全 +86测 (#1166)
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 1m20s
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Validate - Code Quality (push) Successful in 5m49s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 2m31s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 2m32s
CI/CD Pipeline / Unit Tests (push) Failing after 7m41s
CI/CD Pipeline / Integration Tests (push) Successful in 1m12s
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 3m1s
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 / Build Staging API Image (push) Successful in 14m15s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m46s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 6m4s
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 / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / CI Gate (push) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 41s
CI/CD Pipeline / Staging E2E Tests (push) Successful in 1m49s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 4m1s
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
2026-07-30 07:02:48 +08:00
xiaoxia 5256bd0d8b refactor(auth): API层目录化拆分(189行→9个单职责文件) (#1176)
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 46s
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Validate - Code Quality (push) Successful in 4m41s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m58s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 2m33s
CI/CD Pipeline / Unit Tests (push) Successful in 9m54s
CI/CD Pipeline / Integration Tests (push) Successful in 2m17s
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 3m14s
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 / Build Staging API Image (push) Successful in 12m40s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 3m25s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 5m46s
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 / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / CI Gate (push) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 34s
CI/CD Pipeline / Staging E2E Tests (push) Successful in 2m39s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 3m40s
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com>
Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
2026-07-30 01:29:23 +08:00
xiaoxia 33270dd026 refactor(editing-planner): 拆分 StickerPropsEditor 预览和文字属性(200→130行, -35%) (#1144)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com>
Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
2026-07-30 01:23:58 +08:00
xiaoxia 0f27bfa999 refactor(titles): 拆分 useTitleLibrary 为4个子Hook(174→58行, -67%) (#1156)
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Validate - Code Quality (push) Successful in 4m41s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m58s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 2m33s
CI/CD Pipeline / Unit Tests (push) Successful in 9m54s
CI/CD Pipeline / Integration Tests (push) Successful in 2m17s
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 3m14s
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 / Build Staging API Image (push) Successful in 12m40s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 3m25s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 5m46s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 46s
CI/CD Pipeline / Staging E2E Tests (push) Waiting to run
CI/CD Pipeline / Staging API Integration Tests (push) Waiting to run
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 / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (push) Waiting to run
CI/CD Pipeline / Canary Release to Production (push) Blocked by required conditions
CI/CD Pipeline / CI Gate (push) Has been skipped
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com>
Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
2026-07-30 01:19:17 +08:00
xiaoxia f15bc2f2a2 refactor(products): 拆分 useProductList 为3个子Hook(202→91行, -55%) (#1155)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com>
Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
2026-07-30 01:15:19 +08:00
xiaoxia 2273fb329f test(wave208): 密码哈希与验证模块单测 +50测 (#1175)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-07-30 01:13:58 +08:00
xiaoxia 4bd3da73e3 fix(ci): pr_auto_scan修复崩溃bug + 统一merge门禁为CI Gate
CI/CD Pipeline / CI Gate (push) Has been skipped
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Validate - Code Quality (push) Successful in 5m0s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 2m17s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 2m24s
CI/CD Pipeline / Unit Tests (push) Successful in 9m36s
CI/CD Pipeline / Integration Tests (push) Successful in 3m35s
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 1m17s
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 / Build Staging API Image (push) Successful in 15m37s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m35s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 6m7s
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 / Deploy Production (push) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 59s
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
- api_request: 修复HTTPError空响应体导致JSON解析崩溃
- REQUIRED_CONTEXTS_FULL: 改为只检查CI Gate,与pr-automation/分支保护保持一致
- FRONTEND_ONLY_CONTEXT: 改为CI Gate,内部自动处理跳过逻辑
2026-07-30 00:41:43 +08:00
xiaoxia 7674a04a33 fix(ci): CI Gate添加integration-tests必填检查,防止集成测试失败仍被合并
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-07-30 00:40:52 +08:00
xiaoxia 9d97e8aacc refactor(assets): 拆分 batchOperations 为4个子Hook(238→8行, -97%) (#1168)
CI/CD Pipeline / Check if frontend-only change (push) Waiting to run
CI/CD Pipeline / Validate - Code Quality (push) Waiting to run
CI/CD Pipeline / Validate - Type Check (mypy) (push) Waiting to run
CI/CD Pipeline / Validate - Migration (alembic) (push) Waiting to run
CI/CD Pipeline / Unit Tests (push) Blocked by required conditions
CI/CD Pipeline / Integration Tests (push) Blocked by required conditions
CI/CD Pipeline / Frontend Lint (push) Blocked by required conditions
CI/CD Pipeline / Frontend Unit Tests (push) Blocked by required conditions
CI/CD Pipeline / PR Build API Image (push) Blocked by required conditions
CI/CD Pipeline / PR Build Web Image (push) Blocked by required conditions
CI/CD Pipeline / PR Build Worker Image (push) Blocked by required conditions
CI/CD Pipeline / Build Staging API Image (push) Waiting to run
CI/CD Pipeline / Build Staging Web Image (push) Waiting to run
CI/CD Pipeline / Build Staging Worker Image (push) Waiting to run
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Blocked by required conditions
CI/CD Pipeline / Staging E2E Tests (push) Blocked by required conditions
CI/CD Pipeline / Staging API Integration Tests (push) Blocked by required conditions
CI/CD Pipeline / Build Production API Image (push) Blocked by required conditions
CI/CD Pipeline / Build Production Web Image (push) Blocked by required conditions
CI/CD Pipeline / Build Production Worker Image (push) Blocked by required conditions
CI/CD Pipeline / Deploy Production (push) Blocked by required conditions
CI/CD Pipeline / Production Browser E2E (push) Blocked by required conditions
CI/CD Pipeline / ACR Image Cleanup (push) Blocked by required conditions
CI/CD Pipeline / CI Gate (push) Blocked by required conditions
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com>
Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
2026-07-30 00:27:41 +08:00
xiaoxia 89639a6d3e test(wave206): InMemory仓储单测补全 +72测 (#1173)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-07-30 00:26:11 +08:00
xiaoxia c4144d9e3f test(wave205): path_security 单测重构与补全 +46测 (#1172)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-07-30 00:26:05 +08:00
xiaoxia de201436ea test(wave204): text_splitter + pagination 单测补全 +37测 (#1171)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-07-30 00:26:00 +08:00
xiaoxia 77a49e3365 test(wave201): concat_engine_pure 单测补全 +85测 (#1167)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-07-30 00:25:42 +08:00
xiaoxia d7e362a637 test(wave199): sticker_engine_pure 单测补全 +141测 (#1165)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-07-30 00:25:30 +08:00
xiaoxia 028c6613ce test(shared): wave198 ai_service 单测 +41 (#1164)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-07-30 00:25:24 +08:00
xiaoxia 696cdda87b refactor(products): VideoPlayer 改用 useVideoPlayer Hook 消除重复代码(199→123行, -38%) (#1163)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-07-30 00:25:18 +08:00
xiaoxia a85167a529 refactor(generate): 拆分 useGenerateVideo 核心逻辑(211→154行, -27%) (#1162)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-07-30 00:25:12 +08:00
xiaoxia e0b95e69d4 test(domain): wave197 asset/asset_library 兼容层 +12 (#1161)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-07-30 00:25:07 +08:00
xiaoxia 63d4048354 test(domain): wave196 小模块批量单测 +74 (#1160)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-07-30 00:25:01 +08:00
xiaoxia e811516c6e test(domain): wave195 verification_code 单测 +27 (#1159)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-07-30 00:24:55 +08:00
xiaoxia f9a106f36b test(domain): wave194 tts_config 单测 +52 (#1158)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-07-30 00:24:49 +08:00
xiaoxia e902fbd65e test(domain): wave193 video_share 单测 +43 (#1157)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-07-30 00:24:43 +08:00
xiaoxia e8352227af test(wave192): media_validation 媒体校验 +56测 (#1154)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-07-30 00:24:27 +08:00
xiaoxia ef7596739c test(wave190): preset_voices 预置音色 +35测 (#1152)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-07-30 00:24:16 +08:00
xiaoxia c2329f3f98 test(wave189): preset_bgm 预设BGM库 +44测 (#1151)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-07-30 00:24:10 +08:00
xiaoxia 0a0914322c test(wave188): video_concat 视频拼接配置 +58测 (#1150)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-07-30 00:24:04 +08:00
xiaoxia 042cae7a61 test(wave187): speed_config 调速配置 +77测 (#1148)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-07-30 00:23:59 +08:00
xiaoxia be885eb8f0 test(wave186): subtitle 字幕时间轴模型 +52测 (#1147)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-07-30 00:23:53 +08:00
xiaoxia fd61800be9 test(wave185): audio_track_config 多轨道音频配置 +80测 (#1146)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-07-30 00:23:47 +08:00
xiaoxia 183681c07d test(wave184): intro_outro_config 片头片尾配置 +59测 (#1145)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-07-30 00:23:41 +08:00
xiaoxia 8d906bac72 test(wave183): voice_presets 配音音色预设 +59测 (#1143)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-07-30 00:23:30 +08:00
xiaoxia 8f925e6c65 test(wave182): config_schemas 配置Schema +110测 (#1142)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-07-30 00:23:23 +08:00
xiaoxia 9732cc51ee refactor(generate): 拆分 useGenerateFormState 为4个子Hook(309→196行, -37%) (#1141)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-07-30 00:23:17 +08:00
xiaoxia 815b3758a7 refactor(tasks): 拆分 TaskTable 列定义和空状态到子模块(190→72行, -62%) (#1140)
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 47s
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Validate - Code Quality (push) Successful in 5m25s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 2m37s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 2m25s
CI/CD Pipeline / Unit Tests (push) Successful in 9m11s
CI/CD Pipeline / Integration Tests (push) Successful in 2m40s
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 3m7s
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 / Build Staging API Image (push) Successful in 15m16s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 3m2s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 7m58s
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 / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / CI Gate (push) Has been skipped
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
2026-07-30 00:22:45 +08:00
xiaoxia eb73eeab69 refactor(assets): 拆分 batchOperations 为4个独立Hook(238→4行, -98%) (#1149)
CI/CD Pipeline / Build Production API Image (push) Has been skipped
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Validate - Code Quality (push) Successful in 5m47s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m4s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 1m14s
CI/CD Pipeline / Unit Tests (push) Successful in 8m34s
CI/CD Pipeline / Integration Tests (push) Successful in 3m7s
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 54s
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 / Build Staging API Image (push) Successful in 19m10s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m31s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 7m42s
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 / CI Gate (push) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 35s
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 25s
CI/CD Pipeline / Staging E2E Tests (push) Successful in 4m15s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 9m16s
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com>
Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
2026-07-29 22:21:52 +08:00
xiaoxia 2325ffbc57 fix(ci): 修复pr_auto_scan.py的typo - base_ref字段名应为ref而非re
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 33s
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Validate - Code Quality (push) Successful in 5m36s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m42s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 2m11s
CI/CD Pipeline / Unit Tests (push) Successful in 9m14s
CI/CD Pipeline / Integration Tests (push) Successful in 2m30s
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 2m35s
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 / Build Staging API Image (push) Successful in 12m57s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m33s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 5m14s
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 / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / CI Gate (push) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 28s
CI/CD Pipeline / Staging E2E Tests (push) Successful in 4m23s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 5m59s
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
- 修复base_ref获取字段名错误(re->ref),导致所有PR被误判为目标分支不匹配而跳过
- 修复dry-run模式打印缺少f前缀的小bug
2026-07-29 22:20:24 +08:00
xiaoxia faeed6f014 refactor(editing-planner): 拆分 SubtitleStylePanel 为子组件(190→146行, -23%) (#1139)
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 3m4s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 3m6s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 3m11s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 3m17s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 5m3s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 8m4s
CI/CD Pipeline / Unit Tests (push) Successful in 12m32s
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 / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Integration Tests (push) Successful in 4m36s
CI/CD Pipeline / CI Gate (push) Has been skipped
CI/CD Pipeline / Build Staging API Image (push) Successful in 13m9s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 33s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 35s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 4m31s
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
CI/CD Pipeline / Staging E2E Tests (push) Failing after 4m37s
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com>
Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
2026-07-29 17:30:42 +08:00
xiaoxia 6dc388a794 fix(cosyvoice): 修复prefix过滤中文失效导致400错误 (#1138)
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 50s
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Validate - Code Quality (push) Successful in 6m33s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 2m33s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 2m29s
CI/CD Pipeline / Unit Tests (push) Successful in 11m50s
CI/CD Pipeline / Integration Tests (push) Successful in 4m26s
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 1m18s
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 / Build Staging API Image (push) Successful in 13m40s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 3m28s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 5m31s
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 / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / CI Gate (push) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 31s
CI/CD Pipeline / Staging E2E Tests (push) Successful in 3m32s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 5m42s
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
修复prefix过滤中文失效导致400错误
2026-07-29 14:40:58 +08:00
xiaoxia 7ca5b3732f refactor(voice-materials): 拆分 VoiceMaterialRow 进度条和标签组件 (#1087)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
- 拆分 VoiceMaterialRow 为独立子组件
- 进度条逻辑抽取到 useRowProgress hook
- 标签展示组件独立为 TagDisplay
- 代码行数从 186 行减少到 138 行 (-26%)
2026-07-29 14:35:45 +08:00
xiaoxia 7cdf56a1ac test(wave175): template_clip_converter 模板片段转换 +64测 (#1137)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
- 新增 template_clip_converter 模块单元测试
- 覆盖模板解析、片段转换、边界条件等场景
2026-07-29 14:34:37 +08:00
xiaoxia fac825b60b test(wave181): noise_reduction_config 降噪配置 +65测 (#1135)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-07-29 14:29:37 +08:00
xiaoxia 409efe141a test(wave174): tts_job TTS任务配置 +60测 (#1136) 2026-07-29 14:29:37 +08:00
xiaoxia 6e676a07f7 test(wave180): render_layer_utils 渲染层工具 +78测 (#1134)
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Validate - Code Quality (push) Successful in 7m34s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 2m15s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 2m30s
CI/CD Pipeline / Unit Tests (push) Successful in 10m51s
CI/CD Pipeline / Integration Tests (push) Successful in 4m28s
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 1m23s
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 / Build Staging API Image (push) Successful in 4m42s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m3s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 12m48s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 1m37s
CI/CD Pipeline / Staging E2E Tests (push) Successful in 3m8s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 6m17s
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 / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 39s
CI/CD Pipeline / CI Gate (push) Has been skipped
2026-07-29 14:28:17 +08:00
xiaoxia e629cf0e69 test(wave179): transition_config 转场配置 +79测 (#1133) 2026-07-29 14:28:17 +08:00
xiaoxia dd17312181 test(wave178): ai_parsing AI解析配置 +53测 (#1132) 2026-07-29 14:28:17 +08:00
xiaoxia 05fbf64697 test(wave177): voice_clone_profile 音色克隆配置 +58测 (#1131) 2026-07-29 14:28:17 +08:00
xiaoxia 096dc66e2c test(wave176): clip_operations 片段操作工具 +56测 (#1130) 2026-07-29 14:28:17 +08:00
CI Bot 1230eaa24b style: auto-format with black + isort + prettier [skip ci-format-check]
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 - Type Check (mypy) (push) Successful in 2m11s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 2m19s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 3m4s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 3m5s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 5m18s
CI/CD Pipeline / Build Staging API Image (push) Successful in 6m5s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 6m37s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 31s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 35s
CI/CD Pipeline / Staging E2E Tests (push) Successful in 3m18s
CI/CD Pipeline / Unit Tests (push) Successful in 10m16s
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 / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Integration Tests (push) Successful in 5m8s
CI/CD Pipeline / CI Gate (push) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 4m48s
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
2026-07-29 05:07:44 +00:00
xiaoxia 0d7effe88f fix(ci): 修复两个外围失败 - E2E容器残留清理 + ACR pr-days参数支持
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 1m21s
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Validate - Code Quality (push) Failing after 2m57s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 2m4s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 2m4s
CI/CD Pipeline / Unit Tests (push) Successful in 8m22s
CI/CD Pipeline / Integration Tests (push) Successful in 2m30s
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 2m48s
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 / Build Staging API Image (push) Successful in 12m46s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m11s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 5m20s
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 / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / CI Gate (push) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 3m5s
CI/CD Pipeline / Staging API Integration Tests (push) Failing after 3m42s
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
CI/CD Pipeline / Staging E2E Tests (push) Failing after 5m12s
2026-07-29 13:04:50 +08:00
xiaoxia 76fdab4f63 fix(ci): 修复develop分支CI核心链路失败(Code Quality + Unit Tests + Frontend) (#1129)
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 - Type Check (mypy) (push) Successful in 2m2s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 2m46s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 2m56s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 3m27s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 5m17s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 5m33s
CI/CD Pipeline / Integration Tests (push) Successful in 2m51s
CI/CD Pipeline / Unit Tests (push) Successful in 10m13s
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 / CI Gate (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 / Build Staging API Image (push) Successful in 12m49s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 29s
CI/CD Pipeline / ACR Image Cleanup (push) Failing after 14s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 17s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 5m31s
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
2026-07-29 12:12:37 +08:00
CI Bot 4c31026f81 style: auto-format with black + isort + prettier [skip ci-format-check]
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 - Type Check (mypy) (push) Successful in 2m37s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m38s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 2m48s
CI/CD Pipeline / Frontend Unit Tests (push) Failing after 3m29s
CI/CD Pipeline / Validate - Code Quality (push) Failing after 4m7s
CI/CD Pipeline / Integration Tests (push) Successful in 2m46s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 7m34s
CI/CD Pipeline / Unit Tests (push) Failing after 8m43s
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 / CI Gate (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 / Build Staging API Image (push) Successful in 15m49s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 30s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 17s
CI/CD Pipeline / ACR Image Cleanup (push) Failing after 17s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 4m22s
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
2026-07-29 02:18:43 +00:00
xiaoxia 44ee898aa0 fix(ci): 修复CI Gate中python3 -c多行代码的YAML缩进错误\n\nPR #1113引入的AI门禁代码中,python3 -c的多行代码缩进为0,\n导致YAML run: | 块标量提前结束,整个ci-pipeline.yml解析失败,\ndevelop分支push完全不触发CI。\n\n修复方式:将python代码整体右移到与run块基准缩进对齐(10空格),\n保留python内部相对缩进结构,YAML解析时自动去除基准缩进。
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 1m0s
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Validate - Code Quality (push) Failing after 2m39s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m59s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 2m11s
CI/CD Pipeline / Unit Tests (push) Failing after 7m39s
CI/CD Pipeline / Integration Tests (push) Successful in 2m22s
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (push) Failing after 1m41s
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 / Build Staging API Image (push) Successful in 13m53s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m24s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 5m38s
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 / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / CI Gate (push) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (push) Failing after 43s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 5m17s
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
CI/CD Pipeline / Staging E2E Tests (push) Failing after 5m42s
2026-07-29 10:16:06 +08:00
xiaoxia 049dd028c0 test(wave173): pip_config画中画配置 +67测 2026-07-29 01:40:02 +08:00
xiaoxia e64e1c2938 chore(ci): 删除意外提交的bak文件 2026-07-29 00:46:33 +08:00
xiaoxia 93e59df866 fix(ci): develop分支pr-automation优化 - concurrency + 短作业模式 2026-07-29 00:45:11 +08:00
xiaoxia 577f744912 fix(ci): 修复生产构建shell和preview-deploy npm install 2026-07-28 21:01:37 +08:00
xiaoxia d727220562 fix: 修复 url_security 测试中 _is_trusted_domain 和 _check_ssrf_ip 的 mock 兼容性
- _is_trusted_domain 改为 wrapper 函数,自动使用模块级 TRUSTED_DOMAINS
- validate_url_safety 对 IP 地址显式调用本地 _check_ssrf_ip 别名,
  使测试 mock 能正确拦截

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-28 16:31:50 +08:00
xiaoxia 0ce9a22b91 fix: 补全 shared/worker 层对 domain 常量的 re-export
domain 层重构后,部分常量未从 shared/worker 层 re-export,
导致 10 个测试模块因 ImportError 无法收集。

修复:
- packages/shared/url_security.py: 补全 ALLOWED_AUDIO_MIME_TYPES,
  ALLOWED_IMAGE_MIME_TYPES, MAX_URL_LENGTH 的 re-export,
  以及 _check_internal_hostnames, _check_ssrf_ip, _is_trusted_domain
  私有别名
- apps/worker/video_processing/speed_engine.py: 补全 MAX_SPEED,
  MIN_SPEED 的 re-export

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-28 16:29:14 +08:00
xiaoxia 28173f1713 test(wave158): edit_plan剪辑计划单测 +32 (#1088) 2026-07-28 16:20:20 +08:00
xiaoxia 23edd869c6 refactor(editing-planner): 拆分 useEditPlanClipMutations 抽出重排序和素材导入(183→134行, -27%) (#1101) 2026-07-28 16:20:00 +08:00
xiaoxia a863324942 test(wave139): url_security domain 层 +86 单测 (#1056) 2026-07-28 16:19:09 +08:00
xiaoxia 3859ce1750 test(wave152): duplication查重记录单测 +40 (#1080) 2026-07-28 16:19:06 +08:00
xiaoxia ba35e3f518 test(wave153): edit_plan_clip剪辑计划片段单测 +44 (#1081) 2026-07-28 16:19:04 +08:00
xiaoxia ec231ac88f test(wave154): classification素材分类单测 +34 (#1082) 2026-07-28 16:19:02 +08:00
xiaoxia ccfaf9aa7e test(wave155): generated_video生成视频实体单测 +16 (#1084) 2026-07-28 16:19:01 +08:00
xiaoxia fe97f016b2 test(wave156): generation_task生成任务实体单测 +24 (#1085) 2026-07-28 16:18:59 +08:00
xiaoxia 8a0b9b8878 test(wave157): domain层7个小模块批量单测 +44 (#1086) 2026-07-28 16:18:57 +08:00
xiaoxia 15f12ead3f refactor(generate): 拆分 useStep5Voice 模式选择逻辑到子Hook(184→165行, -10%) (#1089) 2026-07-28 16:18:55 +08:00
xiaoxia cfabbd3d61 test(wave159): template_clip_config模板片段配置单测 +41 (#1090) 2026-07-28 16:18:52 +08:00
xiaoxia 19ca924087 refactor(generate): 拆分 Step7ConfirmGenerate 为子组件(194→88行, -55%) (#1091) 2026-07-28 16:18:51 +08:00
xiaoxia 0830b3ac45 test(wave160): job领域模型 +69测 (#1092) 2026-07-28 16:18:49 +08:00
xiaoxia cb7daa0674 refactor(editing-planner): 拆分 useTimelineMenus 为子Hook(192→67行, -65%) (#1093) 2026-07-28 16:18:47 +08:00
xiaoxia 33fd762eea test(wave161): domain entities领域模型 +64测 (#1094) 2026-07-28 16:18:46 +08:00
xiaoxia 3eb541ca39 refactor: split useProductActions into product-actions sub-hooks (#1095) 2026-07-28 16:18:44 +08:00
xiaoxia b353ac0328 test(wave162): asset_scoring素材评分 +66测 (#1096) 2026-07-28 16:18:42 +08:00
xiaoxia 0638c685b6 refactor(generate): 拆分 Step6CoverSettings 为子组件(187→109行, -42%) (#1097) 2026-07-28 16:18:41 +08:00
xiaoxia fdcdd4c6ef test(wave163): watermark_config水印配置 +88测 (#1098) 2026-07-28 16:18:39 +08:00
xiaoxia 8378c2e8ff refactor(generate): 拆分 GenerateResultPanel 为子组件(187→99行, -47%) (#1099) 2026-07-28 16:18:38 +08:00
xiaoxia 575a83fcaf test(wave164): filter_presets滤镜预设 +53测 (#1100) 2026-07-28 16:18:36 +08:00
xiaoxia 17ad2235b6 test(wave165): trim_config裁剪配置 +78测 (#1102) 2026-07-28 16:18:34 +08:00
xiaoxia 7c47856671 refactor(editing-planner): 拆分 GreenScreenPanel 为子组件(200→94行, -53%) (#1103) 2026-07-28 16:18:32 +08:00
xiaoxia 8fa161cfb8 test(wave166): plan_generator_utils计划生成工具 +54测 (#1104) 2026-07-28 16:18:30 +08:00
xiaoxia cbf481a7fb test(wave167): sticker_config贴纸配置 +91测 (#1105) 2026-07-28 16:18:29 +08:00
xiaoxia fe485cf0fc test(wave168): transition_presets转场预设 +35测 (#1106) 2026-07-28 16:18:27 +08:00
xiaoxia 04886e0b30 test(wave169): ass_subtitle_builder ASS字幕构建 +76测 (#1108) 2026-07-28 16:18:25 +08:00
xiaoxia cbc7b48a05 test(wave170): chroma_key_config绿幕抠像配置 +70测 (#1109) 2026-07-28 16:18:24 +08:00
xiaoxia f10f691e40 test(wave171): subtitle_style字幕样式 +106测 (#1110) 2026-07-28 16:18:22 +08:00
xiaoxia 4c2509648e test(wave172): color_grade_config色彩调色 +72测 (#1111) 2026-07-28 16:18:20 +08:00
xiaoxia e6006e849b ci: P1+P2优化 - AI门禁/跳过逻辑/生产构建门禁/daily-check修复 (#1113) 2026-07-28 16:18:19 +08:00
xiaoxia 1076ce4216 fix: 克隆音色上传修复 422 错误 (#1107)
CI/CD Pipeline / Check if frontend-only change (push) Waiting to run
CI/CD Pipeline / Validate - Code Quality (push) Waiting to run
CI/CD Pipeline / Validate - Type Check (mypy) (push) Waiting to run
CI/CD Pipeline / Validate - Migration (alembic) (push) Waiting to run
CI/CD Pipeline / Unit Tests (push) Blocked by required conditions
CI/CD Pipeline / Integration Tests (push) Blocked by required conditions
CI/CD Pipeline / Frontend Lint (push) Waiting to run
CI/CD Pipeline / Frontend Unit Tests (push) Blocked by required conditions
CI/CD Pipeline / PR Build API Image (push) Waiting to run
CI/CD Pipeline / PR Build Web Image (push) Waiting to run
CI/CD Pipeline / PR Build Worker Image (push) Waiting to run
CI/CD Pipeline / Build Staging API Image (push) Waiting to run
CI/CD Pipeline / Build Staging Web Image (push) Waiting to run
CI/CD Pipeline / Build Staging Worker Image (push) Waiting to run
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Blocked by required conditions
CI/CD Pipeline / Staging E2E Tests (push) Blocked by required conditions
CI/CD Pipeline / Staging API Integration Tests (push) Blocked by required conditions
CI/CD Pipeline / Build Production API Image (push) Blocked by required conditions
CI/CD Pipeline / Build Production Web Image (push) Blocked by required conditions
CI/CD Pipeline / Build Production Worker Image (push) Blocked by required conditions
CI/CD Pipeline / Deploy Production (push) Blocked by required conditions
CI/CD Pipeline / Production Browser E2E (push) Blocked by required conditions
CI/CD Pipeline / ACR Image Cleanup (push) Blocked by required conditions
CI/CD Pipeline / Canary Release to Production (push) Blocked by required conditions
CI/CD Pipeline / CI Gate (push) Blocked by required conditions
Squash merge
2026-07-28 16:18:00 +08:00
xiaoxia 39187a0660 fix: 修复 20 个 ruff lint 错误,解除所有 PR CI 阻塞
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
F401 (14): 移除未使用的 import
  - multi_track_mixer_pure.py: Any, Optional, math
  - pip_engine_pure.py: Any
  - speed_engine.py: Optional, MAX_SPEED, MIN_SPEED
  - url_security.py: ALLOWED_AUDIO_MIME_TYPES, ALLOWED_IMAGE_MIME_TYPES,
    ALLOWED_VIDEO_MIME_TYPES, MAGIC_NUMBERS, MAX_URL_LENGTH,
    check_internal_hostname, is_trusted_domain

E741 (3): 重命名模糊变量 l → layer
  - pip_engine_pure.py: lambda 参数
  - test_pip_engine_pure.py: 两处列表推导

B017 (2): pytest.raises(Exception) → FrozenInstanceError
  - test_video_filter_builder.py
  - test_transition_presets.py

B905 (1): zip() 补充 strict=True
  - pip_engine_pure.py
2026-07-28 16:03:01 +08:00
xiaoxia f0bbedab23 refactor(voice-materials): 拆分 VoiceMaterialRow 进度条和标签组件(186→138行, -26%) (#1087)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
Squash merge PR #1087: refactor(voice-materials): 拆分 VoiceMaterialRow 进度条和标签组件(186→138行, -26%)

- 拆分 VoiceMaterialRow 为子组件
- 抽取 TagDisplay 标签展示组件
- 抽取 useRowProgress 进度条Hook
- 186→138行, 精简 -26%
2026-07-28 15:55:52 +08:00
xiaoxia 750444c8bb fix(ci): 修复测试收集错误 — 模块名冲突 + SQLite 兼容 PG 特定 SQL (#1075)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
- 修复test_url_security.py等模块名冲突导致的测试收集错误
- SQLite兼容PG特定SQL语法
- 调整pytest.ini配置

Squash merged from fix/ci-test-collection-errors
2026-07-28 14:34:04 +08:00
xiaoxia 581a146d2f refactor(voice-materials): 拆分 MaterialForm 为子组件(186→108行, -42%)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-07-28 14:07:02 +08:00
xiaoxia eab45e0819 refactor(voices): 拆分 CloneVoiceCard 为子组件(180→79行, -56%)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-07-28 14:05:47 +08:00
xiaoxia e243d70082 refactor(voice-materials): 拆分 TagSelector 逻辑到 useTagInput Hook(163→107行, -34%)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-07-28 14:04:54 +08:00
xiaoxia d4c3743e45 fix(ci): 修复CI Gate失败时返回exit 0的P0 Bug (develop) (#1058)
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Validate - Code Quality (push) Failing after 2m42s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m35s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 1m37s
CI/CD Pipeline / Unit Tests (push) Failing after 6m22s
CI/CD Pipeline / Integration Tests (push) Successful in 4m51s
CI/CD Pipeline / Frontend Lint (push) Failing after 46s
CI/CD Pipeline / Frontend Unit Tests (push) Failing after 1m17s
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 / Build Staging API Image (push) Successful in 18m12s
CI/CD Pipeline / Build Staging Web Image (push) Failing after 3m5s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 8m19s
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 / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-07-28 13:33:25 +08:00
xiaoxia 07d9b56fe7 refactor(duplication): 拆分 DuplicationUpload 页面(233→85行, -64%) (#1064)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-07-28 13:23:29 +08:00
xiaoxia 11b3f83368 refactor(generate): 拆分 useStep2Materials Hook(205→79行, -61%) (#1063)
CI/CD Pipeline / Check if frontend-only change (push) Waiting to run
CI/CD Pipeline / Validate - Code Quality (push) Waiting to run
CI/CD Pipeline / Validate - Type Check (mypy) (push) Waiting to run
CI/CD Pipeline / Validate - Migration (alembic) (push) Waiting to run
CI/CD Pipeline / Unit Tests (push) Blocked by required conditions
CI/CD Pipeline / Integration Tests (push) Blocked by required conditions
CI/CD Pipeline / Frontend Lint (push) Waiting to run
CI/CD Pipeline / Frontend Unit Tests (push) Blocked by required conditions
CI/CD Pipeline / PR Build API Image (push) Waiting to run
CI/CD Pipeline / PR Build Web Image (push) Waiting to run
CI/CD Pipeline / PR Build Worker Image (push) Waiting to run
CI/CD Pipeline / Build Staging API Image (push) Waiting to run
CI/CD Pipeline / Build Staging Web Image (push) Waiting to run
CI/CD Pipeline / Build Staging Worker Image (push) Waiting to run
CI/CD Pipeline / Build Production API Image (push) Waiting to run
CI/CD Pipeline / Build Production Web Image (push) Waiting to run
CI/CD Pipeline / Build Production Worker Image (push) Waiting to run
CI/CD Pipeline / Deploy Production (push) Blocked by required conditions
CI/CD Pipeline / Production Browser E2E (push) Blocked by required conditions
CI/CD Pipeline / CI Gate (push) Blocked by required conditions
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
2026-07-28 13:09:11 +08:00
xiaoxia 1dc40b4760 refactor(voice): 拆分 useCloneModal Hook(215→46行, -79%) (#1062) 2026-07-28 13:08:00 +08:00
xiaoxia 29448aaf9b refactor(voice-materials): 拆分 useVoiceMaterialActions Hook(216→80行, -63%) (#1059)
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been skipped
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Validate - Code Quality (push) Failing after 3m25s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m29s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 1m33s
CI/CD Pipeline / Unit Tests (push) Failing after 6m45s
CI/CD Pipeline / Integration Tests (push) Successful in 3m13s
CI/CD Pipeline / Frontend Lint (push) Failing after 51s
CI/CD Pipeline / Frontend Unit Tests (push) Failing after 1m41s
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 / Build Staging API Image (push) Successful in 16m10s
CI/CD Pipeline / Build Staging Web Image (push) Failing after 2m55s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 4m22s
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) Failing after 14m50s
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / CI Gate (push) Has been skipped
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com>
Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
2026-07-28 12:49:52 +08:00
xiaoxia c73c4be367 test(wave150): tts_config 单测补全 +57 (#1073)
CI/CD Pipeline / CI Gate (push) Has been skipped
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Validate - Code Quality (push) Failing after 5m18s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m32s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 1m34s
CI/CD Pipeline / Unit Tests (push) Failing after 5m48s
CI/CD Pipeline / Integration Tests (push) Successful in 3m23s
CI/CD Pipeline / Frontend Lint (push) Successful in 1m15s
CI/CD Pipeline / Frontend Unit Tests (push) Failing after 2m45s
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 / Build Staging API Image (push) Successful in 19m3s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 1m59s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 6m36s
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 / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 1m59s
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
2026-07-28 12:25:56 +08:00
xiaoxia c31bc96855 test(wave149): video_concat 单测补全 +65 (#1072)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-07-28 12:25:54 +08:00
xiaoxia 002384bad4 test(wave148): voice_presets 单测补全 +59 (#1071)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-07-28 12:24:10 +08:00
xiaoxia db6b742ebb test(wave147): transition_config 单测补全 +78 (#1070)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-07-28 12:24:00 +08:00
xiaoxia 88ca8b4406 test(wave145): template_clip_converter 单测补全 +79 (#1068)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-07-28 12:23:52 +08:00
xiaoxia 005b34d0dd test(wave143): voice_clone_profile 单测补全 +82 (#1066)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-07-28 12:23:48 +08:00
xiaoxia e2ddc679bb test(wave146): trim_config 单测补全 +78 (#1069)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com>
Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
2026-07-28 12:23:46 +08:00
xiaoxia 39990f7a07 Merge pull request 'test(wave144): tts_job 单测补全 +90' (#1067) from test/wave144-tts-job into develop
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-07-28 12:21:57 +08:00
xiaoxia 6fcb4cd70c Merge pull request 'test(wave142): transition_presets 单测补全 +65' (#1065) from test/wave142-transition-presets into develop
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-07-28 12:21:33 +08:00
xiaoxia 14fefc7b1a test(wave141): xfade_builder domain 层 +63 单测 (#1061)
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Validate - Code Quality (push) Failing after 1m35s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 58s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 1m3s
CI/CD Pipeline / Unit Tests (push) Failing after 4m57s
CI/CD Pipeline / Integration Tests (push) Successful in 2m24s
CI/CD Pipeline / Frontend Lint (push) Successful in 31s
CI/CD Pipeline / Frontend Unit Tests (push) Failing after 1m11s
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 / Build Staging API Image (push) Successful in 13m35s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 1m32s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 3m57s
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 / CI Gate (push) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 2m20s
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
PR #1061
- xfade_builder domain 层单测补全
- 新增 tests/unit/domain/test_xfade_builder.py +579行
2026-07-28 11:34:47 +08:00
xiaoxia fa8ab58fc0 test(wave144): add tts_job unit tests (+90)
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 6s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 35s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 1m26s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 1m26s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 19s
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 Web Image (pull_request) Successful in 34s
CI/CD Pipeline / Validate - Code Quality (pull_request) Failing after 2m14s
AI Code Review / AI Code Review (pull_request) Successful in 58s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 52s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 3m2s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m8s
CI/CD Pipeline / Frontend Unit 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 Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Unit Tests (pull_request) Successful in 3m10s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 2m27s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 46m10s
CI/CD Pipeline / Deploy Production (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 / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Successful in 7s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 24s
- TTSJobStatus 枚举: 5种状态值、字符串比较、从字符串构建
- TERMINAL_STATUSES: 3个终态验证
- create 工厂方法: 基础创建、voice_id/project_id/voice_clone_profile_id
- 采样率/格式(format参数化mp3/wav/pcm)、max_retries、metadata
- 参数校验: 空user_id/空input_text/超长文本(10000)/无效格式/纯空白
- 属性: is_terminal(5种)、is_retryable(5种)、is_completed(4种)
- transition_to: 合法转换(8条)、非法转换(4条)、字符串输入、updated_at更新
- mark_*方法: mark_processing(started_at)、mark_completed(全部字段)、mark_failed、mark_cancelled
- prepare_retry: 正常重试、清除error/started_at/completed_at、超上限不可重试
- to_dict: 字段完整性、值正确性、completed/failed状态、空时间字段、ISO时间格式
2026-07-28 10:42:11 +08:00
xiaoxia 4765d83c8b test(wave142): add transition_presets unit tests (+65)
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 47s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 1m36s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 1m49s
CI/CD Pipeline / Validate - Code Quality (pull_request) Failing after 2m23s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 51s
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 Web Image (pull_request) Successful in 1m57s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 1m50s
AI Code Review / AI Code Review (pull_request) Successful in 2m4s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 57s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 5m26s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m24s
CI/CD Pipeline / Frontend Unit 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 Staging (Watchtower auto-deploy) (pull_request) Has been skipped
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 46m10s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 2m46s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 4m3s
CI/CD Pipeline / Deploy Production (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 / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Successful in 9s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 21s
- TransitionPreset 数据类: 属性、默认值、frozen不可变、相等性
- 预设库完整性: 数量、ID唯一、必填字段、时长范围、分类验证
- get_transition_preset: 存在/不存在、随机、空串、大小写、同实例
- list_transition_presets: 无筛选、按分类、关键词搜索(name/desc/tags)、大小写不敏感、组合筛选、顺序保持
- get_default_transition: 默认值、时长、分类、同实例
- 14个典型预设属性参数化验证
2026-07-28 10:34:58 +08:00
xiaoxia 395bc37cf0 refactor(templates): 拆分 TemplateDetailModal 为子组件(219→106行, -52%) (#1055)
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 1m46s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m48s
CI/CD Pipeline / PR Build API Image (push) Has been skipped
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 / Frontend Lint (push) Successful in 50s
CI/CD Pipeline / Validate - Code Quality (push) Failing after 3m24s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m12s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 6m43s
CI/CD Pipeline / Build Staging API Image (push) Successful in 17m9s
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-07-28 10:34:19 +08:00
xiaoxia 15b6e17552 test(wave138): video_filter_builder domain 层 +71 单测 (#1053)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-07-28 10:33:08 +08:00
xiaoxia a6e147ed30 refactor(voice-materials): 拆分 VoiceMaterialCard 为子组件(245→72行, -71%) (#1054)
CI/CD Pipeline / Check if frontend-only change (push) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (push) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (push) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (push) Has been cancelled
CI/CD Pipeline / Unit Tests (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Lint (push) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (push) Has been cancelled
CI/CD Pipeline / PR Build API Image (push) Has been cancelled
CI/CD Pipeline / PR Build Web Image (push) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (push) Has been cancelled
CI/CD Pipeline / Build Staging API Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (push) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build Production API Image (push) Has been cancelled
CI/CD Pipeline / Build Production Web Image (push) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (push) Has been cancelled
CI/CD Pipeline / Deploy Production (push) Has been cancelled
CI/CD Pipeline / Production Browser E2E (push) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (push) Has been cancelled
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been cancelled
2026-07-28 10:19:04 +08:00
xiaoxia 0d46d71b2d test(wave137): pip_engine 纯逻辑抽离 + 96 单测 (#1052) 2026-07-28 10:18:55 +08:00
378 changed files with 52217 additions and 11393 deletions
+68 -24
View File
@@ -38,7 +38,7 @@ jobs:
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
curl -sH "Authorization: token $GITHUB_TOKEN" "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/raw/scripts/ci/step_checkout.sh?ref=${GITHUB_SHA}" | bash
curl -sfH "Authorization: token $GITHUB_TOKEN" -o /tmp/_ci_checkout.sh "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/raw/scripts/ci/step_checkout.sh?ref=${GITHUB_SHA}" && bash /tmp/_ci_checkout.sh
- name: Check changed files
id: check
shell: bash
@@ -91,7 +91,7 @@ jobs:
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
curl -sH "Authorization: token $GITHUB_TOKEN" "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/raw/scripts/ci/step_checkout.sh?ref=${GITHUB_SHA}" | bash
curl -sfH "Authorization: token $GITHUB_TOKEN" -o /tmp/_ci_checkout.sh "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/raw/scripts/ci/step_checkout.sh?ref=${GITHUB_SHA}" && bash /tmp/_ci_checkout.sh
- name: Record job start time
shell: sh
run: bash scripts/ci/step_timer_start.sh
@@ -181,7 +181,7 @@ jobs:
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
curl -sH "Authorization: token $GITHUB_TOKEN" "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/raw/scripts/ci/step_checkout.sh?ref=${GITHUB_SHA}" | bash
curl -sfH "Authorization: token $GITHUB_TOKEN" -o /tmp/_ci_checkout.sh "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/raw/scripts/ci/step_checkout.sh?ref=${GITHUB_SHA}" && bash /tmp/_ci_checkout.sh
- name: Record job start time
shell: sh
run: bash scripts/ci/step_timer_start.sh
@@ -260,7 +260,7 @@ jobs:
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
curl -sH "Authorization: token $GITHUB_TOKEN" "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/raw/scripts/ci/step_checkout.sh?ref=${GITHUB_SHA}" | bash
curl -sfH "Authorization: token $GITHUB_TOKEN" -o /tmp/_ci_checkout.sh "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/raw/scripts/ci/step_checkout.sh?ref=${GITHUB_SHA}" && bash /tmp/_ci_checkout.sh
- name: Record job start time
shell: sh
run: bash scripts/ci/step_timer_start.sh
@@ -341,7 +341,7 @@ jobs:
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
curl -sH "Authorization: token $GITHUB_TOKEN" "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/raw/scripts/ci/step_checkout.sh?ref=${GITHUB_SHA}" | bash
curl -sfH "Authorization: token $GITHUB_TOKEN" -o /tmp/_ci_checkout.sh "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/raw/scripts/ci/step_checkout.sh?ref=${GITHUB_SHA}" && bash /tmp/_ci_checkout.sh
- name: Record job start time
shell: sh
run: bash scripts/ci/step_timer_start.sh
@@ -412,7 +412,7 @@ jobs:
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
curl -sH "Authorization: token $GITHUB_TOKEN" "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/raw/scripts/ci/step_checkout.sh?ref=${GITHUB_SHA}" | bash
curl -sfH "Authorization: token $GITHUB_TOKEN" -o /tmp/_ci_checkout.sh "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/raw/scripts/ci/step_checkout.sh?ref=${GITHUB_SHA}" && bash /tmp/_ci_checkout.sh
- name: Record job start time
shell: sh
run: bash scripts/ci/step_timer_start.sh
@@ -458,13 +458,15 @@ jobs:
name: Frontend Lint
runs-on: ci-l2
timeout-minutes: 10
needs: check-frontend-only
if: needs.check-frontend-only.outputs.skip_frontend != 'true'
steps:
- name: Checkout code
shell: sh
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
curl -sH "Authorization: token $GITHUB_TOKEN" "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/raw/scripts/ci/step_checkout.sh?ref=${GITHUB_SHA}" | bash
curl -sfH "Authorization: token $GITHUB_TOKEN" -o /tmp/_ci_checkout.sh "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/raw/scripts/ci/step_checkout.sh?ref=${GITHUB_SHA}" && bash /tmp/_ci_checkout.sh
- name: Record job start time
shell: sh
run: bash scripts/ci/step_timer_start.sh
@@ -526,7 +528,7 @@ jobs:
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
curl -sH "Authorization: token $GITHUB_TOKEN" "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/raw/scripts/ci/step_checkout.sh?ref=${GITHUB_SHA}" | bash
curl -sfH "Authorization: token $GITHUB_TOKEN" -o /tmp/_ci_checkout.sh "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/raw/scripts/ci/step_checkout.sh?ref=${GITHUB_SHA}" && bash /tmp/_ci_checkout.sh
- name: Record job start time
shell: sh
run: bash scripts/ci/step_timer_start.sh
@@ -576,7 +578,12 @@ jobs:
name: PR Build ${{ matrix.service_display }} Image
runs-on: runtime-builder
timeout-minutes: ${{ matrix.timeout }}
if: github.event_name == 'pull_request'
needs: check-frontend-only
if: |
github.event_name == 'pull_request' && (
(matrix.service == 'web' && needs.check-frontend-only.outputs.skip_frontend != 'true') ||
(matrix.service != 'web' && needs.check-frontend-only.outputs.skip_backend != 'true')
)
strategy:
fail-fast: false
matrix:
@@ -605,7 +612,7 @@ jobs:
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
curl -sH "Authorization: token $GITHUB_TOKEN" "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/raw/scripts/ci/step_checkout.sh?ref=${GITHUB_SHA}" | bash
curl -sfH "Authorization: token $GITHUB_TOKEN" -o /tmp/_ci_checkout.sh "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/raw/scripts/ci/step_checkout.sh?ref=${GITHUB_SHA}" && bash /tmp/_ci_checkout.sh
- name: Record job start time
shell: sh
run: bash scripts/ci/step_timer_start.sh
@@ -789,7 +796,7 @@ jobs:
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
curl -sH "Authorization: token $GITHUB_TOKEN" "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/raw/scripts/ci/step_checkout.sh?ref=${GITHUB_SHA}" | bash
curl -sfH "Authorization: token $GITHUB_TOKEN" -o /tmp/_ci_checkout.sh "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/raw/scripts/ci/step_checkout.sh?ref=${GITHUB_SHA}" && bash /tmp/_ci_checkout.sh
- name: Record job start time
shell: sh
run: bash scripts/ci/step_timer_start.sh
@@ -921,7 +928,7 @@ jobs:
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
curl -sH "Authorization: token $GITHUB_TOKEN" "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/raw/scripts/ci/step_checkout.sh?ref=${GITHUB_SHA}" | bash
curl -sfH "Authorization: token $GITHUB_TOKEN" -o /tmp/_ci_checkout.sh "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/raw/scripts/ci/step_checkout.sh?ref=${GITHUB_SHA}" && bash /tmp/_ci_checkout.sh
- name: Record job start time
shell: sh
run: bash scripts/ci/step_timer_start.sh
@@ -1073,7 +1080,7 @@ jobs:
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
curl -sH "Authorization: token $GITHUB_TOKEN" "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/raw/scripts/ci/step_checkout.sh?ref=${GITHUB_SHA}" | bash
curl -sfH "Authorization: token $GITHUB_TOKEN" -o /tmp/_ci_checkout.sh "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/raw/scripts/ci/step_checkout.sh?ref=${GITHUB_SHA}" && bash /tmp/_ci_checkout.sh
- name: Record job start time
shell: sh
run: bash scripts/ci/step_timer_start.sh
@@ -1084,6 +1091,8 @@ jobs:
# DooD模式下不能用-v挂载(宿主机路径与CI容器路径不一致)
# 改用 docker create + docker cp 方式把代码拷进容器
CONTAINER_NAME="staging-e2e-$$"
# 强制清理可能残留的同名容器(上一次异常退出时未清理)
docker rm -f "$CONTAINER_NAME" 2>/dev/null || true
docker create --name "$CONTAINER_NAME" --ipc=host \
-e E2E_BASE_URL=https://staging.xiaoxiajianji.com \
-e E2E_API_BASE=https://staging-api.xiaoxiajianji.com/api/v1 \
@@ -1137,7 +1146,7 @@ jobs:
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
curl -sH "Authorization: token $GITHUB_TOKEN" "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/raw/scripts/ci/step_checkout.sh?ref=${GITHUB_SHA}" | bash
curl -sfH "Authorization: token $GITHUB_TOKEN" -o /tmp/_ci_checkout.sh "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/raw/scripts/ci/step_checkout.sh?ref=${GITHUB_SHA}" && bash /tmp/_ci_checkout.sh
- name: Record job start time
shell: sh
run: bash scripts/ci/step_timer_start.sh
@@ -1192,6 +1201,11 @@ jobs:
runs-on: runtime-builder
timeout-minutes: ${{ matrix.timeout }}
needs:
- validate-code-quality
- validate-type-check
- unit-tests
- frontend-lint
- frontend-unit-test
if: startsWith(github.ref, 'refs/tags/v') || (github.event_name == 'push' && github.ref_name == 'main')
strategy:
fail-fast: false
@@ -1221,7 +1235,7 @@ jobs:
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
curl -sH "Authorization: token $GITHUB_TOKEN" "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/raw/scripts/ci/step_checkout.sh?ref=${GITHUB_SHA}" | bash
curl -sfH "Authorization: token $GITHUB_TOKEN" -o /tmp/_ci_checkout.sh "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/raw/scripts/ci/step_checkout.sh?ref=${GITHUB_SHA}" && bash /tmp/_ci_checkout.sh
- name: Record job start time
shell: sh
run: bash scripts/ci/step_timer_start.sh
@@ -1265,7 +1279,7 @@ jobs:
docker buildx inspect --bootstrap
- name: Build and push production ${{ matrix.service_display }} image (with retry)
shell: sh
shell: bash
run: |
set -eu
REGISTRY="xiaoxia-registry.cn-hangzhou.cr.aliyuncs.com/xiaoxiakeji"
@@ -1345,7 +1359,7 @@ jobs:
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
curl -sH "Authorization: token $GITHUB_TOKEN" "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/raw/scripts/ci/step_checkout.sh?ref=${GITHUB_SHA}" | bash
curl -sfH "Authorization: token $GITHUB_TOKEN" -o /tmp/_ci_checkout.sh "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/raw/scripts/ci/step_checkout.sh?ref=${GITHUB_SHA}" && bash /tmp/_ci_checkout.sh
- name: Record job start time
shell: sh
run: bash scripts/ci/step_timer_start.sh
@@ -1476,7 +1490,7 @@ jobs:
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
curl -sH "Authorization: token $GITHUB_TOKEN" "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/raw/scripts/ci/step_checkout.sh?ref=${GITHUB_SHA}" | bash
curl -sfH "Authorization: token $GITHUB_TOKEN" -o /tmp/_ci_checkout.sh "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/raw/scripts/ci/step_checkout.sh?ref=${GITHUB_SHA}" && bash /tmp/_ci_checkout.sh
- name: Record job start time
shell: sh
run: bash scripts/ci/step_timer_start.sh
@@ -1536,7 +1550,7 @@ jobs:
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
curl -sH "Authorization: token $GITHUB_TOKEN" "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/raw/scripts/ci/step_checkout.sh?ref=${GITHUB_SHA}" | bash
curl -sfH "Authorization: token $GITHUB_TOKEN" -o /tmp/_ci_checkout.sh "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/raw/scripts/ci/step_checkout.sh?ref=${GITHUB_SHA}" && bash /tmp/_ci_checkout.sh
- name: Record job start time
shell: sh
run: bash scripts/ci/step_timer_start.sh
@@ -1595,7 +1609,7 @@ jobs:
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
curl -sH "Authorization: token $GITHUB_TOKEN" "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/raw/scripts/ci/step_checkout.sh?ref=${GITHUB_SHA}" | bash
curl -sfH "Authorization: token $GITHUB_TOKEN" -o /tmp/_ci_checkout.sh "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/raw/scripts/ci/step_checkout.sh?ref=${GITHUB_SHA}" && bash /tmp/_ci_checkout.sh
- name: Record job start time
shell: sh
run: bash scripts/ci/step_timer_start.sh
@@ -1684,9 +1698,8 @@ jobs:
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
curl -sH "Authorization: token $GITHUB_TOKEN" \
"${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/raw/scripts/ci/step_checkout.sh?ref=${GITHUB_SHA}" \
| bash
curl -sfH "Authorization: token $GITHUB_TOKEN" -o /tmp/_ci_checkout.sh \
"${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/raw/scripts/ci/step_checkout.sh?ref=${GITHUB_SHA}" && bash /tmp/_ci_checkout.sh
- name: Evaluate CI Gate
id: gate
@@ -1718,6 +1731,30 @@ jobs:
echo " frontend-lint: $RESULT_FRONTEND_LINT"
echo " frontend-unit-test: $RESULT_FRONTEND_UNIT"
echo " build-pr: $RESULT_BUILD_PR"
# 查询 AI Code Review 状态(跨workflow,读commit status
AI_REVIEW_STATUS="pending"
AI_REVIEW_DESC=""
STATUS_JSON=$(curl -sfH "Authorization: token $GITHUB_TOKEN" \
"${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/commits/${PR_HEAD_SHA}/status" 2>/dev/null || true)
if [ -n "$STATUS_JSON" ]; then
AI_STATUS=$(echo "$STATUS_JSON" | python3 -c "
import json,sys
try:
data=json.load(sys.stdin)
for s in data.get('statuses',[]):
if 'AI Code Review' in s.get('context',''):
print(s['state']+'|'+s.get('description',''))
break
except: pass
" 2>/dev/null)
if [ -n "$AI_STATUS" ]; then
AI_REVIEW_STATUS="${AI_STATUS%%|*}"
AI_REVIEW_DESC="${AI_STATUS#*|}"
fi
fi
echo " ai-code-review: $AI_REVIEW_STATUS ($AI_REVIEW_DESC)"
echo ""
# 判断PR类型
@@ -1733,11 +1770,13 @@ jobs:
"validate-migration:$RESULT_MIGRATION"
"frontend-lint:$RESULT_FRONTEND_LINT"
"build-pr:$RESULT_BUILD_PR"
"ai-code-review:$AI_REVIEW_STATUS"
)
# 后端检查
REQUIRED_BACKEND=(
"unit-tests:$RESULT_UNIT_TESTS"
"integration-tests:$RESULT_INTEGRATION"
)
# 前端检查
@@ -1767,6 +1806,11 @@ jobs:
for item in "${REQUIRED_GENERAL[@]}"; do
name="${item%%:*}"
result="${item##*:}"
# AI Code Review pending时不阻塞(可能还在跑),等它跑完自然会重跑Gate
if [ "$name" = "ai-code-review" ] && [ "$result" = "pending" ]; then
echo " ⏳ $name: pending(审查中,暂不阻塞)"
continue
fi
check_job "$name" "$result"
done
@@ -1805,7 +1849,7 @@ jobs:
echo "❌ CI Gate: FAILED"
echo "失败项: ${FAILED_ITEMS[*]}"
echo "gate_result=failure" >> $GITHUB_OUTPUT
exit 0
exit 1
fi
- name: Report CI trace
+2 -2
View File
@@ -87,7 +87,7 @@ jobs:
runs-on: ci-l2
timeout-minutes: 10
outputs:
report: ${{ steps.smoke.outputs.report }}
report: ${{ steps.report.outputs.report }}
steps:
- name: Checkout code
@@ -201,7 +201,7 @@ jobs:
runs-on: ci-l2
timeout-minutes: 15
outputs:
report: ${{ steps.smoke.outputs.report }}
report: ${{ steps.e2e.outputs.report }}
steps:
- name: Checkout code
+6 -1
View File
@@ -8,6 +8,11 @@ on:
permissions:
contents: read
concurrency:
group: pr-automation-${{ gitea.event.pull_request.number }}
cancel-in-progress: true
jobs:
auto-approve:
name: Auto Approve on CI Green
@@ -56,7 +61,7 @@ jobs:
name: Auto Merge on CI Green + Approved
runs-on: ci-check
if: github.event_name == 'pull_request' && !github.event.pull_request.draft && github.event.pull_request.base.ref == 'develop'
timeout-minutes: 45 # 长等待模式:等CI全绿后自动合并,不遗漏任何PR
timeout-minutes: 3 # 短作业模式:检查一次,不满足就退出,由pr-auto-scan每5分钟定时兜底
steps:
- name: Checkout code
shell: sh
+1 -1
View File
@@ -120,7 +120,7 @@ jobs:
PREVIEW_SSH_KEY: ${{ secrets.PREVIEW_SSH_KEY }}
run: |
set -eux
preview_host="${PREVIEW_SSH_HOST:-172.30.18.197}"
preview_host="${PREVIEW_SSH_HOST:-47.98.113.167}"
preview_user="${PREVIEW_SSH_USER:-deploy}"
preview_port="${PREVIEW_SSH_PORT:-22222}"
preview_dir="/var/www/preview/pr-${PR_NUMBER}"
+1 -1
View File
@@ -101,7 +101,7 @@ jobs:
# Install dependencies with retry
for i in 1 2 3; do
npm ci --no-audit --no-fund && break
echo "npm ci failed, retry $i/3..."
echo "npm install failed, retry $i/3..."
[ $i -eq 3 ] && exit 1
rm -rf node_modules
sleep 5
+61
View File
@@ -0,0 +1,61 @@
"""#1197 - 预览生成:generation_tasks 表新增 is_preview 字段
Revision ID: 053
Revises: 052
Create Date: 2026-08-15
Changes:
1. generation_tasks 表新增 is_preview 字段,标记是否为预览生成任务(低清 480p)
2. 默认 False,与现有正式生成任务兼容
3. 加索引以支持按预览/正式任务筛选
"""
import sqlalchemy as sa
from alembic import context, op
revision = "053_generation_task_is_preview"
down_revision = "052_generation_task_bgm_config"
branch_labels = None
depends_on = None
def upgrade() -> None:
conn = op.get_bind()
if context.get_context().dialect.name == "postgresql":
# 检查列是否已存在(幂等)
result = conn.execute(
sa.text(
"SELECT column_name FROM information_schema.columns "
"WHERE table_name = 'generation_tasks' AND column_name = 'is_preview'"
)
)
if result.scalar() is not None:
return
op.add_column(
"generation_tasks",
sa.Column("is_preview", sa.Boolean, nullable=False, server_default=sa.text("false")),
)
# 加索引
op.create_index(
"ix_generation_tasks_is_preview",
"generation_tasks",
["is_preview"],
)
def downgrade() -> None:
conn = op.get_bind()
if context.get_context().dialect.name == "postgresql":
result = conn.execute(
sa.text(
"SELECT column_name FROM information_schema.columns "
"WHERE table_name = 'generation_tasks' AND column_name = 'is_preview'"
)
)
if result.scalar() is None:
return
op.drop_index("ix_generation_tasks_is_preview", table_name="generation_tasks")
op.drop_column("generation_tasks", "is_preview")
+6
View File
@@ -7,6 +7,7 @@ from app.api.routes.chunked_upload import router as chunked_upload_router
from app.api.routes.classification_jobs import router as classification_jobs_router
from app.api.routes.duplication import router as duplication_router
from app.api.routes.feature_flags import router as feature_flags_router
from app.api.routes.generation_preview import router as generation_preview_router
from app.api.routes.generation_tasks import router as generation_tasks_router
from app.api.routes.health import router as health_check_router
from app.api.routes.ingest_jobs import router as ingest_jobs_router
@@ -87,6 +88,11 @@ api_router.include_router(
prefix="/generation",
tags=["Generation"],
)
api_router.include_router(
generation_preview_router,
prefix="/generation",
tags=["Generation"],
)
api_router.include_router(
titles_router,
prefix="/titles",
+333
View File
@@ -0,0 +1,333 @@
"""预览生成路由 — Phase 1:单版本预览接口(创建 + 查询)。
路径前缀:/api/v1/generation/preview(与 /generation/tasks 同体系)
"""
from __future__ import annotations
import logging
from app.auth import AuthenticatedUser, get_current_user
from app.core.storage import get_storage_service
from app.core.task_enqueue import (
GLOBAL_PENDING_LIMIT,
USER_PENDING_LIMIT,
GlobalQueueFull,
UserPendingLimitExceeded,
safe_enqueue_generation_task,
)
from app.dependencies import (
get_db_session,
get_generated_video_repository,
get_generation_task_repository,
)
from app.schemas.generation_task import (
CreatePreviewGenerationTaskRequest,
PreviewGenerationTaskResponse,
)
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from packages.adapters.sqlalchemy_impl.template_repository import (
SQLAlchemyTemplateRepository,
)
from packages.application import (
CreateGenerationTaskCommand,
CreateGenerationTaskUseCase,
GetGenerationTaskUseCase,
ListGeneratedVideosByTaskUseCase,
)
logger = logging.getLogger(__name__)
router = APIRouter()
PREVIEW_RESOLUTION = "854x480"
# 模板 mode → 视频比例映射
_TEMPLATE_MODE_TO_RATIO = {
"pip": "9:16",
"standard": "16:9",
"square": "1:1",
}
def _calc_preview_resolution(video_ratio: str = "") -> str:
"""根据视频比例计算预览分辨率(短边 480,长边按比例)。
支持的比例:16:9, 9:16, 1:1, 4:3, 3:4, 其他默认 16:9。
"""
ratio_map = {
"16:9": "854x480",
"9:16": "480x854",
"1:1": "480x480",
"4:3": "640x480",
"3:4": "480x640",
}
return ratio_map.get(video_ratio.strip(), PREVIEW_RESOLUTION)
def _infer_video_ratio_from_template(
template_id: str, db: Session, user_id: str = ""
) -> str:
"""从模板 mode 推断视频比例,前端未传 video_ratio 时使用。
Returns:
视频比例字符串(如 "9:16"),查询失败返回空字符串。
"""
if not template_id:
return ""
try:
repo = SQLAlchemyTemplateRepository(db)
template = repo.get(template_id, user_id)
if template:
mode = getattr(template, "mode", "") or ""
ratio = _TEMPLATE_MODE_TO_RATIO.get(mode.strip(), "")
if ratio:
logger.info(
"[预览生成] 从模板 mode=%s 推断 video_ratio=%s",
mode,
ratio,
)
return ratio
except Exception:
logger.warning(
"[预览生成] 查询模板失败,跳过 video_ratio 推断: template_id=%s",
template_id,
exc_info=True,
)
return ""
def _mark_task_failed(repo, task, reason: str) -> None:
"""入队失败时将任务标记为 failed,避免产生僵尸 pending 数据。"""
try:
task.mark_failed(error_message=f"入队失败:{reason}")
repo.update(task)
except Exception:
logger.exception("[预览生成] 标记任务失败时异常: task_id=%s", task.id)
def _sign_video_url(raw_url: str) -> str:
"""为私有 OSS bucket 的视频 URL 生成预签名下载链接。
有效期 2 小时,签名失败时降级返回原始 URL。
"""
if not raw_url:
return ""
try:
storage = get_storage_service()
signed = storage.get_download_url(raw_url, expires_seconds=7200)
# 如果返回的 URL 与原始 URL 完全不同且不是签名 URL(说明 bucket 未配置),
# 降级返回原始 URL
if signed and signed != raw_url:
return signed
if signed == raw_url:
return raw_url
# signed 为空或与 raw_url 无关,返回原始
return raw_url
except Exception:
logger.warning("[预览] URL签名失败,降级返回原始URL: %s", raw_url[:100], exc_info=True)
return raw_url
def _to_preview_response(task, generated_videos: list | None = None) -> PreviewGenerationTaskResponse:
"""将领域任务对象转换为预览响应 DTO。
Args:
task: GenerationTask 领域对象
generated_videos: 生成的视频列表(可选),取第一个作为 video_url
Returns:
PreviewGenerationTaskResponse
"""
video_url = ""
duration = 0.0
file_size = 0
if generated_videos:
first_video = generated_videos[0]
raw_url = getattr(first_video, "file_url", "") or ""
# P0 修复:私有 bucket 需要预签名 URL,否则前端 403 → 黑屏
video_url = _sign_video_url(raw_url)
duration = float(getattr(first_video, "duration", 0.0) or 0.0)
file_size = int(getattr(first_video, "file_size", 0) or 0)
# 从 extra_meta / metadata 中提取统计信息(如果有)
extra_meta = getattr(task, "extra_meta", {}) or {}
clip_count = int(extra_meta.get("clip_count", len(getattr(task, "asset_ids", [])) or 0))
transition_count = int(extra_meta.get("transition_count", max(0, clip_count - 1)))
material_usage = extra_meta.get("material_usage", {}) or {}
# 计算生成耗时
generate_duration = 0.0
started_at = getattr(task, "started_at", None)
completed_at = getattr(task, "completed_at", None)
if started_at and completed_at:
generate_duration = (completed_at - started_at).total_seconds()
return PreviewGenerationTaskResponse(
task_id=task.id,
status=task.status.value if hasattr(task.status, "value") else str(task.status),
progress=float(task.progress or 0.0),
is_preview=bool(getattr(task, "is_preview", True)),
resolution=getattr(task, "resolution", PREVIEW_RESOLUTION) or PREVIEW_RESOLUTION,
video_url=video_url,
duration=duration,
file_size=file_size,
clip_count=clip_count,
transition_count=transition_count,
material_usage=material_usage,
error_message=task.error_message or "",
created_at=task.created_at,
started_at=started_at,
finished_at=completed_at,
generate_duration=generate_duration,
)
@router.post("/preview", response_model=PreviewGenerationTaskResponse, status_code=201)
def create_preview_generation_task(
request: CreatePreviewGenerationTaskRequest,
authenticated_user: AuthenticatedUser = Depends(get_current_user),
generation_task_repository=Depends(get_generation_task_repository),
db: Session = Depends(get_db_session),
) -> PreviewGenerationTaskResponse:
"""创建预览生成任务。
预览为完整时长的低清版(480p + 低码率),效果与正式生成一致,仅清晰度降低。
Args:
request: 预览任务创建请求(template_id + asset_ids 等)
Returns:
201 + 预览任务详情
"""
user_id = authenticated_user.user.id
logger.info(
"[预览生成] 接收请求: user_id=%s, template_id=%s, asset_count=%d",
user_id,
request.template_id,
len(request.asset_ids),
)
# 预检查队列限流
try:
user_pending = generation_task_repository.count_pending_by_user(user_id)
global_pending = generation_task_repository.count_pending_total()
if user_pending + 1 > USER_PENDING_LIMIT:
raise UserPendingLimitExceeded(user_id=user_id, pending_count=user_pending + 1, limit=USER_PENDING_LIMIT)
if global_pending + 1 > GLOBAL_PENDING_LIMIT:
raise GlobalQueueFull(pending_count=global_pending + 1, limit=GLOBAL_PENDING_LIMIT)
except UserPendingLimitExceeded as e:
raise HTTPException(
status_code=429,
detail=f"您的待处理任务过多(当前 {e.pending_count - 1}/{e.limit}),请等待后再提交",
) from e
except GlobalQueueFull as e:
raise HTTPException(
status_code=503,
detail="系统繁忙,请稍后再试",
) from e
# 确定视频比例:优先前端传入,否则从模板 mode 推断
video_ratio = request.video_ratio or ""
if not video_ratio and request.template_id:
video_ratio = _infer_video_ratio_from_template(request.template_id, db, user_id)
use_case = CreateGenerationTaskUseCase(generation_task_repository)
try:
task = use_case.execute(
CreateGenerationTaskCommand(
project_id="",
asset_library_id="",
strategy_id="",
voice_library_id="",
template_id=request.template_id,
asset_ids=list(request.asset_ids),
title_ids=list(request.title_ids),
voice_ids=list(request.voice_ids),
created_by_user_id=user_id,
source_edit_plan_id="",
asset_select_mode="",
batch_id="",
video_title=request.video_title,
resolution=_calc_preview_resolution(video_ratio),
bgm_config=request.bgm_config or {},
auto_retry_enabled=False,
auto_retry_max=0,
is_preview=True,
)
)
except ValueError as e:
logger.warning("[预览生成] 创建失败: %s", e)
raise HTTPException(status_code=400, detail=str(e)) from e
except Exception as e:
logger.error("[预览生成] 创建失败: %s", e, exc_info=True)
raise HTTPException(status_code=500, detail="创建预览生成任务失败,请稍后再试") from e
# 入队执行;若入队失败则标记任务为 failed 避免僵尸数据
try:
if not safe_enqueue_generation_task(
task,
generation_task_repository,
user_id=user_id,
log_prefix="[预览生成]",
log_task_status=True,
):
logger.warning("[预览生成] 任务入队失败: task_id=%s", task.id)
_mark_task_failed(generation_task_repository, task, "任务入队失败")
raise HTTPException(status_code=500, detail="任务入队失败,请稍后重试")
except UserPendingLimitExceeded as e:
_mark_task_failed(generation_task_repository, task, "待处理任务超限")
raise HTTPException(
status_code=429,
detail=f"您的待处理任务过多(当前 {e.pending_count - 1}/{e.limit}),请等待后再提交",
) from None
except GlobalQueueFull:
_mark_task_failed(generation_task_repository, task, "系统队列已满")
raise HTTPException(
status_code=503,
detail="系统繁忙,请稍后再试",
) from None
return _to_preview_response(task)
@router.get("/preview/{task_id}", response_model=PreviewGenerationTaskResponse)
def get_preview_generation_task(
task_id: str,
authenticated_user: AuthenticatedUser = Depends(get_current_user),
generation_task_repository=Depends(get_generation_task_repository),
generated_video_repository=Depends(get_generated_video_repository),
) -> PreviewGenerationTaskResponse:
"""查询预览生成任务状态。
Args:
task_id: 任务 ID
Returns:
预览任务详情(含状态、进度、结果 URL 等)
"""
use_case = GetGenerationTaskUseCase(generation_task_repository)
task = use_case.execute(task_id)
if task is None:
raise HTTPException(status_code=404, detail=f"预览任务 {task_id} 不存在")
# 权限校验:任务必须属于当前用户(统一转 str 比较,避免 UUID/str 类型差异)
task_user_id = str(getattr(task, "created_by_user_id", "") or "")
if not task_user_id or task_user_id != str(authenticated_user.user.id):
raise HTTPException(status_code=403, detail="无权访问该任务")
# 校验是否为预览任务
if not getattr(task, "is_preview", False):
raise HTTPException(status_code=404, detail=f"预览任务 {task_id} 不存在")
# 查询生成的视频(取第一个)
generated_videos = []
status_val = task.status.value if hasattr(task.status, "value") else str(task.status)
if status_val == "completed":
list_use_case = ListGeneratedVideosByTaskUseCase(generated_video_repository)
generated_videos = list_use_case.execute(task_id)
return _to_preview_response(task, generated_videos=generated_videos)
@@ -24,6 +24,94 @@ logger = logging.getLogger(__name__)
router = APIRouter(tags=["Template Editor"])
def _build_asset_analyses(
asset_ids: list[str],
db: Session,
) -> dict[str, str]:
"""调用 MediaKit 视频理解,返回 {asset_id: 分析文本}.
如果 MediaKit 不可用或分析失败,返回空 dict(调用方降级处理)。
"""
if not asset_ids:
return {}
try:
from packages.adapters.sqlalchemy_impl.asset_repository import SQLAlchemyAssetRepository
from packages.shared.mediakit_client import get_mediakit_client
from packages.shared.storage import get_shared_storage_service
client = get_mediakit_client()
if not client.is_available:
logger.info("MediaKit 未配置,跳过视频理解分析")
return {}
asset_repo = SQLAlchemyAssetRepository(db)
storage_svc = get_shared_storage_service()
# 查找素材并获取下载 URL(使用并行列表保持索引对应,避免 URL 重复导致映射覆盖)
video_urls: list[str] = []
valid_asset_ids: list[str] = []
for aid in asset_ids[:10]: # MediaKit 单次最多 10 个视频
asset = asset_repo.get(aid)
if not asset or not asset.storage_key:
continue
# 只处理视频素材
mime = getattr(asset, "mime_type", "")
if not mime.startswith("video/"):
continue
try:
url = storage_svc.get_download_url(asset.storage_key)
if url:
video_urls.append(url)
valid_asset_ids.append(aid)
except Exception as e:
logger.warning("获取素材URL失败: asset_id=%s error=%s", aid, str(e))
if not video_urls:
logger.info("无可用视频素材,跳过视频理解分析")
return {}
# 调用 MediaKit 视频理解
prompt = (
"请简要描述这段视频的主要内容,包括:场景(室内/室外/具体场所)、"
"主体(人物/物体/动物)、动作/活动、氛围/情绪、主要色调。"
"控制在100字以内。"
)
# 限制轮询参数以适配 API 网关超时(nginx 60s
# 视频理解最多 30spoll_interval=2s * max_poll_attempts=15
# 剩余 30s 留给 LLM 调用
contents = client.analyze_videos(
video_urls=video_urls,
prompt=prompt,
level="Economy",
poll_interval=2.0,
max_poll_attempts=15,
)
if not contents:
logger.warning("MediaKit 视频理解未返回结果")
return {}
# 将结果映射回 asset_id(通过索引对应)
analyses: dict[str, str] = {}
for i, content in enumerate(contents):
if i < len(valid_asset_ids) and content:
analyses[valid_asset_ids[i]] = content
logger.info(
"MediaKit 视频理解完成: total=%d analyzed=%d",
len(video_urls),
len(analyses),
)
return analyses
except Exception as e:
logger.warning("MediaKit 视频理解异常,将降级到无分析模式: %s", str(e))
return {}
@router.post("/ai-recommend", response_model=AIRecommendResponse)
def editor_ai_recommend(
template_id: str,
@@ -46,12 +134,16 @@ def editor_ai_recommend(
from packages.shared.ai_service import run_ai_recommend
# 调用 MediaKit 视频理解,获取素材内容分析
asset_analyses = _build_asset_analyses(body.asset_ids, db)
result = run_ai_recommend(
plan_id=plan_id,
template_id=plan.template_id,
asset_ids=body.asset_ids,
editing_mode=body.editing_mode,
target_duration=body.target_duration,
asset_analyses=asset_analyses,
)
try:
@@ -184,6 +184,29 @@ def editor_generate_cover(
_, plan_svc = services
plan = plan_svc.get_plan_or_raise(plan_id)
# 获取第一个视频的下载 URL(用于 MediaKit 抽帧)
primary_video_url = None
if body.asset_ids and body.cover_type in ("ai_frame", "ai_regenerate"):
try:
from app.database import get_db_session
from packages.adapters.sqlalchemy_impl.asset_repository import SQLAlchemyAssetRepository
from packages.shared.storage import get_shared_storage_service
with get_db_session() as session:
asset_repo = SQLAlchemyAssetRepository(session)
first_asset = asset_repo.get(body.asset_ids[0])
if first_asset and first_asset.storage_key:
storage_svc = get_shared_storage_service()
primary_video_url = storage_svc.get_download_url(first_asset.storage_key)
logger.info(
"获取视频URL用于封面生成: asset_id=%s url=%s",
body.asset_ids[0],
primary_video_url[:80] if primary_video_url else None,
)
except Exception as e:
logger.warning("获取视频URL失败,将使用stub封面: %s", str(e))
from packages.shared.ai_service import run_generate_cover
cover_data = run_generate_cover(
@@ -191,6 +214,7 @@ def editor_generate_cover(
asset_ids=body.asset_ids,
cover_type=body.cover_type,
frame_time=body.frame_time,
primary_video_url=primary_video_url,
)
current_config = dict(plan.config) if plan.config else {}
+84 -1
View File
@@ -11,6 +11,7 @@ from app.dependencies import get_cosyvoice_service, get_voice_clone_profile_repo
from app.schemas.voice_clone import (
CreateVoiceCloneRequest,
ListVoiceCloneResponse,
VoiceClonePreviewResponse,
VoiceCloneProfileResponse,
VoiceCloneStatusResponse,
)
@@ -19,7 +20,7 @@ from fastapi import APIRouter, Depends, HTTPException, Query, Response, status
from packages.adapters.sqlalchemy_impl.voice_clone_profile_repository import (
SQLAlchemyVoiceCloneProfileRepository,
)
from packages.application.cosyvoice_service import CosyVoiceService
from packages.application.cosyvoice_service import CosyVoiceError, CosyVoiceService
from packages.application.voice_clone.use_cases import (
DeleteVoiceCloneUseCase,
GetVoiceCloneStatusUseCase,
@@ -36,6 +37,13 @@ logger = logging.getLogger(__name__)
router = APIRouter()
# 克隆音色试听缓存(减少重复TTS调用)
# key: clone_id, value: (audio_url, duration, file_size, text, timestamp)
_clone_preview_cache: dict[str, tuple[str, float, int, str, float]] = {}
CLONE_PREVIEW_CACHE_TTL = 7 * 24 * 3600 # 7天TTL
# 默认试听文本
CLONE_PREVIEW_TEMPLATE = "你好,这是我的克隆音色,很高兴能为你配音。"
def _to_response(profile) -> VoiceCloneProfileResponse:
# source_audio_url 是用户传入的原始 URL(可能是外部地址),不做预签名转换
@@ -223,3 +231,78 @@ def retry_voice_clone(
logger.error(f"Failed to mark profile as failed after dispatch error: {inner_e}")
return _to_response(profile)
@router.get("/{clone_id}/preview", response_model=VoiceClonePreviewResponse)
def get_voice_clone_preview(
clone_id: str,
text: str = Query("", description="自定义试听文本,为空则使用默认示例"),
authenticated_user: AuthenticatedUser = Depends(get_current_user),
repository: SQLAlchemyVoiceCloneProfileRepository = Depends(get_voice_clone_profile_repository),
cosyvoice: CosyVoiceService = Depends(get_cosyvoice_service),
) -> VoiceClonePreviewResponse:
"""获取克隆音色试听音频(实时 TTS 合成)。
- 克隆音色必须处于 ready 状态
- 使用默认试听文本时,结果缓存 7 天
- 可传入自定义 text 参数试听不同文本
"""
import time
use_case = GetVoiceCloneUseCase(repository)
try:
profile = use_case.execute(clone_id, authenticated_user.user.id)
except VoiceCloneNotFoundError as _e:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Voice clone not found") from _e
if not profile.is_ready:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"Voice clone is not ready (current status: {profile.status})",
)
# 有自定义文本时不缓存
use_cache = not text.strip()
if use_cache and clone_id in _clone_preview_cache:
audio_url, duration, file_size, cached_text, cached_at = _clone_preview_cache[clone_id]
if time.time() - cached_at < CLONE_PREVIEW_CACHE_TTL:
return VoiceClonePreviewResponse(
clone_id=clone_id,
voice_id=profile.voice_id,
audio_url=audio_url,
text=cached_text,
duration=duration,
file_size=file_size,
)
# 合成试听音频
preview_text = text.strip() or CLONE_PREVIEW_TEMPLATE
try:
result = cosyvoice.synthesize_speech(
text=preview_text,
voice_id=profile.voice_id,
format="mp3",
speed=1.0,
)
except CosyVoiceError as e:
raise HTTPException(status_code=502, detail=f"TTS 合成失败: {e}") from e
# 缓存(仅默认试听文本)
if use_cache:
_clone_preview_cache[clone_id] = (
result.audio_url,
result.duration,
result.file_size,
preview_text,
time.time(),
)
return VoiceClonePreviewResponse(
clone_id=clone_id,
voice_id=profile.voice_id,
audio_url=result.audio_url,
text=preview_text,
duration=result.duration,
file_size=result.file_size,
)
+129 -16
View File
@@ -5,6 +5,8 @@
from __future__ import annotations
import logging
import time
from typing import Literal, Optional
from app.api.routes._helpers import get_user_plan
@@ -42,6 +44,7 @@ from packages.domain.preset_voices import PRESET_VOICES, get_preset_voice_by_id
from packages.ports.user_repository import UserRepository
router = APIRouter()
logger = logging.getLogger(__name__)
# 预置音色试听音频缓存(内存缓存,减少重复TTS调用)
# key: voice_id, value: (audio_url, timestamp)
@@ -51,6 +54,65 @@ PREVIEW_CACHE_TTL = 7 * 24 * 3600 # 7天TTL
PREVIEW_TEMPLATE = "你好,我是{name},很高兴认识你。"
def _resolve_preset_preview_url(
voice_id: str,
fallback_url: str,
cosyvoice: CosyVoiceService,
) -> str:
"""为预置音色获取有效的 preview_url.
优先从内存缓存读取;缓存失效时调用 CosyVoice 重新合成;
合成失败时降级返回硬编码 URL(可能已过期,但不会报错)。
"""
# 检查缓存
if voice_id in _preset_preview_cache:
audio_url, cached_at = _preset_preview_cache[voice_id]
if time.time() - cached_at < PREVIEW_CACHE_TTL:
return audio_url
# 缓存失效,调用 CosyVoice 合成
preset = get_preset_voice_by_id(voice_id)
if preset is None:
return fallback_url
preview_text = PREVIEW_TEMPLATE.format(name=preset.name)
try:
result = cosyvoice.synthesize_speech(
text=preview_text,
voice_id=voice_id,
format="mp3",
speed=1.0,
)
audio_url = result.audio_url
_preset_preview_cache[voice_id] = (audio_url, time.time())
logger.info("Preset voice preview generated: %s", voice_id)
return audio_url
except Exception as e:
logger.warning("Failed to generate preview for %s, using fallback: %s", voice_id, e)
return fallback_url
def _resolve_all_preset_preview_urls(
presets: list,
cosyvoice: CosyVoiceService,
) -> dict[str, str]:
"""顺序解析所有预置音色的 preview_url.
采用顺序调用(而非并行)以避免触发 DashScope API 速率限制。
首次调用后结果缓存 7 天,后续请求直接命中缓存。
Returns:
voice_id -> preview_url 映射
"""
result_map: dict[str, str] = {}
for p in presets:
try:
result_map[p.voice_id] = _resolve_preset_preview_url(p.voice_id, p.preview_url, cosyvoice)
except Exception:
result_map[p.voice_id] = p.preview_url
return result_map
def _get_voice_repository(session: Session = Depends(get_db_session)) -> SQLAlchemyVoiceLibraryRepository:
return SQLAlchemyVoiceLibraryRepository(session)
@@ -118,8 +180,16 @@ def _to_unified_response(item, profile_id_map: dict | None = None, sign_url=None
)
def _preset_to_unified_response(preset) -> UnifiedVoiceItemResponse:
"""将预置音色转换为统一响应格式。"""
def _preset_to_unified_response(preset, preview_url_map: dict[str, str] | None = None) -> UnifiedVoiceItemResponse:
"""将预置音色转换为统一响应格式。
Args:
preset: 预置音色对象
preview_url_map: voice_id -> preview_url 动态映射,优先使用
"""
preview_url = preset.preview_url
if preview_url_map and preset.voice_id in preview_url_map:
preview_url = preview_url_map[preset.voice_id]
return UnifiedVoiceItemResponse(
id=preset.voice_id,
type="preset",
@@ -129,11 +199,40 @@ def _preset_to_unified_response(preset) -> UnifiedVoiceItemResponse:
language=preset.language,
voice_id=preset.voice_id,
voice_provider="cosyvoice",
preview_url=preset.preview_url,
preview_url=preview_url,
tags=preset.tags or [],
)
def _clone_profile_to_unified_response(profile) -> UnifiedVoiceItemResponse:
"""将克隆音色档案转换为统一响应格式。
注意:克隆音色是「音色模型」(可用于 TTS 合成任意文本),
不同于配音库条目(具体的配音作品)。
"""
return UnifiedVoiceItemResponse(
id=profile.id,
type="clone",
name=profile.name,
description=profile.description or "",
gender=profile.gender or "unknown",
language=profile.language or "zh-CN",
voice_id=profile.voice_id or "",
voice_provider=profile.voice_model or "cosyvoice",
audio_url="", # 克隆音色没有预合成音频,需通过 /voice-clones/{id}/preview 试听
preview_url="", # 试听需实时合成,前端调用 preview 接口
duration=0,
file_size=0,
status=profile.status.value if hasattr(profile.status, "value") else str(profile.status),
tags=[],
user_id=profile.user_id,
project_id=None,
voice_clone_profile_id=profile.id,
created_at=profile.created_at,
updated_at=profile.updated_at,
)
# ==================== 统一配音列表(预置 + 克隆)====================
@@ -150,6 +249,7 @@ def list_voices_unified(
voice_repository: SQLAlchemyVoiceLibraryRepository = Depends(_get_voice_repository),
clone_profile_repository: SQLAlchemyVoiceCloneProfileRepository = Depends(_get_clone_profile_repository),
sign_url=Depends(get_audio_url_signer),
cosyvoice: CosyVoiceService = Depends(get_cosyvoice_service),
) -> UnifiedVoiceListResponse:
"""获取配音列表(预置音色 + 用户克隆音色)。
@@ -165,19 +265,30 @@ def list_voices_unified(
has_preset = type is None or type == "preset"
has_clone = type is None or type == "clone"
# 获取预置音色
# 获取预置音色(动态生成 preview_url
if has_preset:
preset_items = [_preset_to_unified_response(p) for p in PRESET_VOICES]
preview_url_map = _resolve_all_preset_preview_urls(PRESET_VOICES, cosyvoice)
preset_items = [_preset_to_unified_response(p, preview_url_map) for p in PRESET_VOICES]
preset_count = len(preset_items)
# 获取克隆音色
# 获取克隆音色(从 voice_clone_profile 读取,ready 状态的克隆音色)
if has_clone:
use_case = ListVoiceLibraryUseCase(voice_repository)
clone_items_raw, clone_count = use_case.execute(user_id, status=status_filter, skip=skip, limit=limit)
# 批量查询 voice_id → profile_id 映射,填充 voice_clone_profile_id
voice_ids = [i.voice_id for i in clone_items_raw if i.voice_id]
profile_id_map = clone_profile_repository.find_profile_ids_by_voice_ids(voice_ids) if voice_ids else {}
clone_items = [_to_unified_response(i, profile_id_map, sign_url) for i in clone_items_raw]
# status_filter 映射:不传则默认只返回 ready 状态(可用的克隆音色)
# 前端可以传 status=all 获取所有状态,或传具体状态过滤
filter_status = None
if status_filter and status_filter != "all":
filter_status = status_filter
elif not status_filter:
filter_status = "ready"
clone_profiles = clone_profile_repository.list_by_user(
user_id,
status=filter_status,
limit=limit,
offset=skip,
)
clone_count = clone_profile_repository.count_by_user(user_id, status=filter_status)
clone_items = [_clone_profile_to_unified_response(p) for p in clone_profiles]
# 组装结果
if type == "preset":
@@ -204,11 +315,15 @@ def list_voices_unified(
@router.get("/presets", response_model=PresetVoiceListResponse)
def list_preset_voices() -> PresetVoiceListResponse:
def list_preset_voices(
cosyvoice: CosyVoiceService = Depends(get_cosyvoice_service),
) -> PresetVoiceListResponse:
"""获取预置音色列表。
不需要认证,返回所有系统预置的 CosyVoice 音色。
preview_url 通过 CosyVoice 动态生成,不依赖硬编码的过期 URL。
"""
preview_url_map = _resolve_all_preset_preview_urls(PRESET_VOICES, cosyvoice)
items = [
PresetVoiceItemResponse(
voice_id=p.voice_id,
@@ -216,7 +331,7 @@ def list_preset_voices() -> PresetVoiceListResponse:
description=p.description,
gender=p.gender,
language=p.language,
preview_url=p.preview_url,
preview_url=preview_url_map.get(p.voice_id, p.preview_url),
tags=p.tags or [],
)
for p in PRESET_VOICES
@@ -236,8 +351,6 @@ def get_preset_voice_preview(
- 相同 voice_id 重复调用直接返回缓存的音频URL
- 可传入自定义 text 参数试听不同文本
"""
import time
preset = get_preset_voice_by_id(voice_id)
if preset is None:
raise HTTPException(status_code=404, detail=f"预置音色不存在: {voice_id}")
+60
View File
@@ -1,4 +1,5 @@
import json
from datetime import datetime
from pydantic import BaseModel, Field, field_validator, model_validator
@@ -122,3 +123,62 @@ class ListGenerationTasksResponse(BaseModel):
"""用户级生成任务列表响应(跨 project)。"""
items: list[GenerationTaskResponse]
# ── 预览生成(Phase 1) ───────────────────────────────────────────────────────
class CreatePreviewGenerationTaskRequest(BaseModel):
"""创建预览生成任务请求。
仅支持模板模式:template_id + asset_ids 等素材 ID 列表。
预览为完整时长低清版(480p + 低码率)。
"""
template_id: str
asset_ids: list[str] = Field(default_factory=list)
title_ids: list[str] = Field(default_factory=list)
voice_ids: list[str] = Field(default_factory=list)
video_title: str = Field(default="", description="生成视频的标题/名称,为空则使用默认命名")
duration: float = Field(default=0.0, ge=0, description="期望视频时长(秒),0 表示由模板决定")
video_ratio: str = Field(default="", description="视频比例,如 16:9 / 9:16,为空使用模板默认")
bgm_config: dict = Field(
default_factory=dict,
description="自定义BGM配置,覆盖模板BGM设置。支持 enabled/source/asset_id/preset_id/audio_url/volume 等字段",
)
@model_validator(mode="after")
def _check_template_id(self) -> "CreatePreviewGenerationTaskRequest":
if not self.template_id.strip():
raise ValueError("template_id 不能为空")
return self
@model_validator(mode="after")
def _check_asset_ids(self) -> "CreatePreviewGenerationTaskRequest":
if not self.asset_ids and not self.title_ids and not self.voice_ids:
raise ValueError("asset_ids/title_ids/voice_ids 至少需要提供一个")
return self
class PreviewGenerationTaskResponse(BaseModel):
"""预览生成任务响应。
包含任务状态、进度、分辨率、生成结果 URL 等关键字段。
"""
task_id: str
status: str
progress: float
is_preview: bool = True
resolution: str = ""
video_url: str = ""
duration: float = 0.0
file_size: int = 0
clip_count: int = 0
transition_count: int = 0
material_usage: dict = Field(default_factory=dict)
error_message: str = ""
created_at: datetime | None = None
started_at: datetime | None = None
finished_at: datetime | None = None
generate_duration: float = 0.0
+22
View File
@@ -63,3 +63,25 @@ class ListVoiceCloneResponse(BaseModel):
items: List[VoiceCloneProfileResponse]
total: int
class VoiceClonePreviewResponse(BaseModel):
"""克隆音色试听响应。"""
clone_id: str
"""音色克隆档案 ID"""
voice_id: str
"""CosyVoice 音色 ID"""
audio_url: str
"""试听音频 URL"""
text: str
"""试听文本"""
duration: float = 0.0
"""音频时长(秒)"""
file_size: int = 0
"""文件大小(字节)"""
+2 -1
View File
@@ -201,7 +201,7 @@ test.describe("Core generation flow", () => {
await page.getByRole("button", { name: "下一步" }).click()
// Step 4: title
await expect(page.getByRole("heading", { name: /选择标题/ })).toBeVisible()
await expect(page.getByRole("heading", { name: /选择标题/ })).toBeVisible({ timeout: 15000 })
const titleText = `E2E Test ${suffix}`
await page.getByPlaceholder("输入自定义标题…").fill(titleText)
await page.getByRole("button", { name: "下一步" }).click()
@@ -297,3 +297,4 @@ test.describe("Core generation flow", () => {
expect(Array.isArray(tasksData.items)).toBe(true)
})
})
+2
View File
@@ -0,0 +1,2 @@
allowBuilds:
esbuild: set this to true or false
-188
View File
@@ -1,188 +0,0 @@
/**
* 认证相关 API
*/
import axios from "axios"
import apiClient from "./client"
// 类型定义
export interface LoginRequest {
email: string
password: string
}
export interface LoginResponse {
access_token: string
refresh_token?: string | null
token_type: string
expires_in: number
user_id: string
email: string
username: string
display_name: string
}
export interface RegisterRequest {
email: string
password: string
username: string
display_name?: string
}
export interface User {
id: string
user_id: string
email: string
username: string
display_name: string
is_email_verified: boolean
email_verified: boolean
created_at?: string
}
export interface UserResponse {
id?: string
user_id?: string
email: string
username: string
display_name: string
is_email_verified?: boolean
email_verified?: boolean
created_at?: string
}
export const normalizeUser = (data: UserResponse): User => {
const userId = data.id ?? data.user_id ?? ""
const emailVerified = data.is_email_verified ?? data.email_verified ?? false
return {
id: userId,
user_id: userId,
email: data.email,
username: data.username,
display_name: data.display_name,
is_email_verified: emailVerified,
email_verified: emailVerified,
created_at: data.created_at,
}
}
// 登录
export const login = async (data: LoginRequest): Promise<LoginResponse> => {
const response = await apiClient.post("/auth/login", data)
return response.data
}
// 刷新 access_token(使用裸 axios 避免拦截器递归)
export const refreshAccessToken = async (refreshToken: string): Promise<LoginResponse> => {
const baseURL = apiClient.defaults.baseURL ?? ""
const response = await axios.post(`${baseURL}/auth/refresh`, {
refresh_token: refreshToken,
})
return response.data
}
// 注册
export const register = async (data: RegisterRequest): Promise<{ message: string }> => {
const response = await apiClient.post("/auth/register", data)
return response.data
}
// 登出
export const logout = async (): Promise<void> => {
await apiClient.post("/auth/logout")
}
// 获取当前用户
export const getCurrentUser = async (): Promise<User> => {
const response = await apiClient.get<UserResponse>("/auth/me")
return normalizeUser(response.data)
}
// 请求密码重置
export const requestPasswordReset = async (email: string): Promise<{ message: string }> => {
const response = await apiClient.post("/auth/forgot-password", { email })
return response.data
}
// 重置密码
export const resetPassword = async (
token: string,
newPassword: string,
): Promise<{ message: string }> => {
const response = await apiClient.post("/auth/reset-password", {
token,
new_password: newPassword,
})
return response.data
}
// 验证邮箱
export const verifyEmail = async (token: string): Promise<{ message: string }> => {
const response = await apiClient.post("/auth/verify-email", { token })
return response.data
}
/* ========== 微信登录 ========== */
export interface WechatAuthUrlResponse {
auth_url: string
state: string
}
export interface WechatCallbackResponse {
access_token: string
refresh_token?: string | null
user_id: string
display_name: string
avatar_url: string
is_new_user: boolean
binding_complete: boolean
expires_in: number
}
export interface SendVerificationCodeRequest {
target: "email" | "phone"
value: string
purpose: "bind" | "login" | "reset_password"
}
export interface BindContactRequest {
email?: string
email_code?: string
phone?: string
phone_code?: string
}
export interface BindContactResponse {
success: boolean
user: User
}
// 获取微信授权链接
export const getWechatAuthUrl = async (): Promise<WechatAuthUrlResponse> => {
const response = await apiClient.get("/auth/wechat/url")
return response.data
}
// 微信回调登录
export const wechatCallback = async (
code: string,
state: string,
): Promise<WechatCallbackResponse> => {
const response = await apiClient.post("/auth/wechat/callback", { code, state })
return response.data
}
// 发送验证码
export const sendVerificationCode = async (
data: SendVerificationCodeRequest,
): Promise<{ message: string }> => {
const response = await apiClient.post("/auth/send-verification-code", data)
return response.data
}
// 绑定联系方式
export const bindContact = async (data: BindContactRequest): Promise<BindContactResponse> => {
const response = await apiClient.post("/auth/bind-contact", data)
return response.data
}
+20
View File
@@ -0,0 +1,20 @@
import apiClient from "../client"
import type { SendVerificationCodeRequest, BindContactRequest, BindContactResponse } from "./types"
/**
* 发送验证码
*/
export const sendVerificationCode = async (
data: SendVerificationCodeRequest,
): Promise<{ message: string }> => {
const response = await apiClient.post("/auth/send-verification-code", data)
return response.data
}
/**
* 绑定联系方式
*/
export const bindContact = async (data: BindContactRequest): Promise<BindContactResponse> => {
const response = await apiClient.post("/auth/bind-contact", data)
return response.data
}
+11
View File
@@ -0,0 +1,11 @@
import apiClient from "../client"
import type { User, UserResponse } from "./types"
import { normalizeUser } from "./user"
/**
* 获取当前用户
*/
export const getCurrentUser = async (): Promise<User> => {
const response = await apiClient.get<UserResponse>("/auth/me")
return normalizeUser(response.data)
}
+9
View File
@@ -0,0 +1,9 @@
import apiClient from "../client"
/**
* 验证邮箱
*/
export const verifyEmail = async (token: string): Promise<{ message: string }> => {
const response = await apiClient.post("/auth/verify-email", { token })
return response.data
}
+39
View File
@@ -0,0 +1,39 @@
/**
* 认证相关 API
* 保持向后兼容,从子模块 re-export
*/
// 类型
export type {
LoginRequest,
LoginResponse,
RegisterRequest,
User,
UserResponse,
WechatAuthUrlResponse,
WechatCallbackResponse,
SendVerificationCodeRequest,
BindContactRequest,
BindContactResponse,
} from "./types"
// 用户工具函数
export { normalizeUser } from "./user"
// 登录/注册/登出/刷新
export { login, refreshAccessToken, register, logout } from "./login"
// 当前用户
export { getCurrentUser } from "./currentUser"
// 密码重置
export { requestPasswordReset, resetPassword } from "./password"
// 邮箱验证
export { verifyEmail } from "./email"
// 微信登录
export { getWechatAuthUrl, wechatCallback } from "./wechat"
// 联系方式
export { sendVerificationCode, bindContact } from "./contact"
+37
View File
@@ -0,0 +1,37 @@
import axios from "axios"
import apiClient from "../client"
import type { LoginRequest, LoginResponse, RegisterRequest } from "./types"
/**
* 登录
*/
export const login = async (data: LoginRequest): Promise<LoginResponse> => {
const response = await apiClient.post("/auth/login", data)
return response.data
}
/**
* 刷新 access_token(使用裸 axios 避免拦截器递归)
*/
export const refreshAccessToken = async (refreshToken: string): Promise<LoginResponse> => {
const baseURL = apiClient.defaults.baseURL ?? ""
const response = await axios.post(`${baseURL}/auth/refresh`, {
refresh_token: refreshToken,
})
return response.data
}
/**
* 注册
*/
export const register = async (data: RegisterRequest): Promise<{ message: string }> => {
const response = await apiClient.post("/auth/register", data)
return response.data
}
/**
* 登出
*/
export const logout = async (): Promise<void> => {
await apiClient.post("/auth/logout")
}
+23
View File
@@ -0,0 +1,23 @@
import apiClient from "../client"
/**
* 请求密码重置
*/
export const requestPasswordReset = async (email: string): Promise<{ message: string }> => {
const response = await apiClient.post("/auth/forgot-password", { email })
return response.data
}
/**
* 重置密码
*/
export const resetPassword = async (
token: string,
newPassword: string,
): Promise<{ message: string }> => {
const response = await apiClient.post("/auth/reset-password", {
token,
new_password: newPassword,
})
return response.data
}
+82
View File
@@ -0,0 +1,82 @@
/**
* 认证相关类型定义
*/
export interface LoginRequest {
email: string
password: string
}
export interface LoginResponse {
access_token: string
refresh_token?: string | null
token_type: string
expires_in: number
user_id: string
email: string
username: string
display_name: string
}
export interface RegisterRequest {
email: string
password: string
username: string
display_name?: string
}
export interface User {
id: string
user_id: string
email: string
username: string
display_name: string
is_email_verified: boolean
email_verified: boolean
created_at?: string
}
export interface UserResponse {
id?: string
user_id?: string
email: string
username: string
display_name: string
is_email_verified?: boolean
email_verified?: boolean
created_at?: string
}
export interface WechatAuthUrlResponse {
auth_url: string
state: string
}
export interface WechatCallbackResponse {
access_token: string
refresh_token?: string | null
user_id: string
display_name: string
avatar_url: string
is_new_user: boolean
binding_complete: boolean
expires_in: number
}
export interface SendVerificationCodeRequest {
target: "email" | "phone"
value: string
purpose: "bind" | "login" | "reset_password"
}
export interface BindContactRequest {
email?: string
email_code?: string
phone?: string
phone_code?: string
}
export interface BindContactResponse {
success: boolean
user: User
}
+20
View File
@@ -0,0 +1,20 @@
import type { User, UserResponse } from "./types"
/**
* 规范化用户数据,兼容不同后端返回格式
*/
export const normalizeUser = (data: UserResponse): User => {
const userId = data.id ?? data.user_id ?? ""
const emailVerified = data.is_email_verified ?? data.email_verified ?? false
return {
id: userId,
user_id: userId,
email: data.email,
username: data.username,
display_name: data.display_name,
is_email_verified: emailVerified,
email_verified: emailVerified,
created_at: data.created_at,
}
}
+21
View File
@@ -0,0 +1,21 @@
import apiClient from "../client"
import type { WechatAuthUrlResponse, WechatCallbackResponse } from "./types"
/**
* 获取微信授权链接
*/
export const getWechatAuthUrl = async (): Promise<WechatAuthUrlResponse> => {
const response = await apiClient.get("/auth/wechat/url")
return response.data
}
/**
* 微信回调登录
*/
export const wechatCallback = async (
code: string,
state: string,
): Promise<WechatCallbackResponse> => {
const response = await apiClient.post("/auth/wechat/callback", { code, state })
return response.data
}
+15 -1
View File
@@ -57,7 +57,21 @@ apiClient.interceptors.response.use(
}
// 401 → 尝试刷新 Token
if (error.response?.status === 401 && originalRequest && !originalRequest._retry) {
// 排除 auth 端点:登录/注册/找回密码的 401 是正常业务响应(如密码错误),
// 不应触发 token 刷新或登出跳转,走后面的错误提示逻辑即可
const requestUrl = originalRequest?.url || ""
const isAuthEndpoint =
requestUrl.includes("/auth/login") ||
requestUrl.includes("/auth/register") ||
requestUrl.includes("/auth/forgot-password") ||
requestUrl.includes("/auth/reset-password")
if (
error.response?.status === 401 &&
originalRequest &&
!originalRequest._retry &&
!isAuthEndpoint
) {
const refreshToken = useAuthStore.getState().refreshToken
// 无 refresh_token → 直接登出
+8
View File
@@ -0,0 +1,8 @@
export type {
PreviewStatus,
CreatePreviewRequest,
CreatePreviewResponse,
PreviewTaskResponse,
} from "./types"
export { createPreview, getPreviewStatus } from "./preview"
+16
View File
@@ -0,0 +1,16 @@
import apiClient from "../client"
import type { CreatePreviewRequest, CreatePreviewResponse, PreviewTaskResponse } from "./types"
/** 创建预览生成任务(单版本) */
export const createPreview = async (
params: CreatePreviewRequest,
): Promise<CreatePreviewResponse> => {
const response = await apiClient.post<CreatePreviewResponse>("/generation/preview", params)
return response.data
}
/** 查询预览任务状态及结果 */
export const getPreviewStatus = async (taskId: string): Promise<PreviewTaskResponse> => {
const response = await apiClient.get<PreviewTaskResponse>(`/generation/preview/${taskId}`)
return response.data
}
+47
View File
@@ -0,0 +1,47 @@
/** 预览任务状态 */
export type PreviewStatus = "pending" | "generating" | "completed" | "failed" | "cancelled"
/** 创建预览任务请求 */
export interface CreatePreviewRequest {
template_id: string
asset_ids: string[]
title_ids?: string[]
voice_ids?: string[]
video_title?: string
duration?: number
video_ratio?: string
bgm_config?: {
enabled: boolean
preset_id?: string
volume?: number
}
}
/** 创建预览任务响应 */
export interface CreatePreviewResponse {
task_id: string
status: PreviewStatus
is_preview: boolean
resolution: string
created_at: string
}
/** 预览任务详情响应 */
export interface PreviewTaskResponse {
task_id: string
status: PreviewStatus
progress: number
is_preview: boolean
resolution: string
video_url?: string
duration?: number
file_size?: number
clip_count?: number
transition_count?: number
material_usage?: number
error_message?: string
created_at: string
started_at?: string
finished_at?: string
generate_duration?: number
}
+15
View File
@@ -11,6 +11,7 @@ import type {
VoiceCloneListParams,
ListVoiceCloneResponse,
VoiceCloneStatusResponse,
VoiceClonePreviewResponse,
} from "./types"
/** 获取克隆音色列表(返回前端兼容数组) */
@@ -85,3 +86,17 @@ export const retryVoiceClone = async (id: string): Promise<VoiceCloneProfile> =>
const response = await apiClient.post<VoiceCloneProfile>(`/voice-clones/${id}/retry`)
return response.data
}
/** 获取克隆音色试听音频(实时 TTS 合成) */
export const getVoiceClonePreview = async (
cloneId: string,
text?: string,
): Promise<VoiceClonePreviewResponse> => {
const searchParams = new URLSearchParams()
if (text) searchParams.set("text", text)
const qs = searchParams.toString()
const response = await apiClient.get<VoiceClonePreviewResponse>(
`/voice-clones/${cloneId}/preview${qs ? `?${qs}` : ""}`,
)
return response.data
}
+2
View File
@@ -14,6 +14,7 @@ export type {
VoiceCloneStatusResponse,
CreateVoiceCloneRequestFull,
VoiceCloneListParams,
VoiceClonePreviewResponse,
} from "./types"
// 工具函数
@@ -29,4 +30,5 @@ export {
updateVoiceClone,
getVoiceCloneStatus,
retryVoiceClone,
getVoiceClonePreview,
} from "./clones"
+21
View File
@@ -90,3 +90,24 @@ export interface VoiceCloneListParams {
skip?: number
limit?: number
}
/** 克隆音色试听响应 */
export interface VoiceClonePreviewResponse {
clone_id: string
/** 音色克隆档案 ID */
voice_id: string
/** CosyVoice 音色 ID */
audio_url: string
/** 试听音频 URL */
text: string
/** 试听文本 */
duration: number
/** 音频时长(秒) */
file_size: number
/** 文件大小(字节) */
}
@@ -0,0 +1,97 @@
import { useState, useEffect } from "react"
import { Form, message } from "antd"
import { sendVerificationCode, bindContact, type BindContactResponse } from "@/api/auth"
import { useCountdown } from "./useCountdown"
type ContactType = "email" | "phone"
interface UseBindContactFormOptions {
open: boolean
onSuccess?: (user: BindContactResponse["user"]) => void
onCancel?: () => void
}
/**
* 绑定联系方式表单 Hook
* 封装表单状态、验证码发送、提交绑定等业务逻辑
*/
export const useBindContactForm = ({ open, onSuccess }: UseBindContactFormOptions) => {
const [activeTab, setActiveTab] = useState<ContactType>("email")
const [form] = Form.useForm()
const [loading, setLoading] = useState(false)
const [codeLoading, setCodeLoading] = useState(false)
const { countdown, isRunning, start: startCountdown, reset: resetCountdown } = useCountdown(60)
/* ── 打开时重置表单 ── */
useEffect(() => {
if (open) {
form.resetFields()
resetCountdown()
}
}, [open, form, resetCountdown])
/* ── 发送验证码 ── */
const handleSendCode = async () => {
try {
const fieldName = activeTab === "email" ? "email" : "phone"
const value = form.getFieldValue(fieldName)
if (!value) {
message.warning(activeTab === "email" ? "请输入邮箱" : "请输入手机号")
return
}
setCodeLoading(true)
await sendVerificationCode({
target: activeTab,
value,
purpose: "bind",
})
message.success("验证码已发送")
startCountdown()
} catch {
// error handled by interceptor
} finally {
setCodeLoading(false)
}
}
/* ── 提交绑定 ── */
const handleSubmit = async () => {
// 1. 表单验证(失败时 Ant Design Form 会自动展示错误信息)
let values
try {
values = await form.validateFields()
} catch {
return
}
// 2. 调用绑定接口
setLoading(true)
try {
const payload =
activeTab === "email"
? { email: values.email, email_code: values.email_code }
: { phone: values.phone, phone_code: values.phone_code }
const result = await bindContact(payload)
message.success("绑定成功")
onSuccess?.(result.user)
} catch {
// API 错误由全局拦截器统一处理提示
} finally {
setLoading(false)
}
}
return {
activeTab,
setActiveTab,
form,
loading,
codeLoading,
countdown,
codeDisabled: isRunning,
handleSendCode,
handleSubmit,
}
}
@@ -0,0 +1,39 @@
import { useState, useEffect, useRef, useCallback } from "react"
/**
* 倒计时 Hook
* 用于验证码发送等场景的倒计时控制
*/
export const useCountdown = (initialSeconds = 60) => {
const [countdown, setCountdown] = useState(0)
const timerRef = useRef<ReturnType<typeof setInterval> | null>(null)
useEffect(() => {
if (countdown > 0) {
timerRef.current = setInterval(() => {
setCountdown((prev) => prev - 1)
}, 1000)
} else if (timerRef.current) {
clearInterval(timerRef.current)
timerRef.current = null
}
return () => {
if (timerRef.current) clearInterval(timerRef.current)
}
}, [countdown])
const start = useCallback(() => {
setCountdown(initialSeconds)
}, [initialSeconds])
const reset = useCallback(() => {
setCountdown(0)
}, [])
return {
countdown,
isRunning: countdown > 0,
start,
reset,
}
}
@@ -1,84 +1,20 @@
import React, { useState, useEffect, useRef } from "react"
import { Modal, Tabs, Form, Input, Button, message } from "antd"
import { sendVerificationCode, bindContact, type BindContactResponse } from "@/api/auth"
interface BindContactModalProps {
open: boolean
onSuccess?: (user: BindContactResponse["user"]) => void
onCancel?: () => void
}
import React from "react"
import { Modal, Tabs, Form, Input, Button } from "antd"
import { useBindContactForm } from "./hooks/useBindContactForm"
import type { BindContactModalProps } from "./types"
const BindContactModal: React.FC<BindContactModalProps> = ({ open, onSuccess, onCancel }) => {
const [activeTab, setActiveTab] = useState<"email" | "phone">("email")
const [form] = Form.useForm()
const [loading, setLoading] = useState(false)
const [codeLoading, setCodeLoading] = useState(false)
const [countdown, setCountdown] = useState(0)
const timerRef = useRef<ReturnType<typeof setInterval> | null>(null)
useEffect(() => {
if (countdown > 0) {
timerRef.current = setInterval(() => {
setCountdown((prev) => prev - 1)
}, 1000)
} else if (timerRef.current) {
clearInterval(timerRef.current)
timerRef.current = null
}
return () => {
if (timerRef.current) clearInterval(timerRef.current)
}
}, [countdown])
useEffect(() => {
if (open) {
form.resetFields()
setCountdown(0)
}
}, [open, form])
const handleSendCode = async () => {
try {
const value = form.getFieldValue(activeTab === "email" ? "email" : "phone")
if (!value) {
message.warning(activeTab === "email" ? "请输入邮箱" : "请输入手机号")
return
}
setCodeLoading(true)
await sendVerificationCode({
target: activeTab,
value,
purpose: "bind",
})
message.success("验证码已发送")
setCountdown(60)
} catch (error) {
// error handled by interceptor
} finally {
setCodeLoading(false)
}
}
const handleSubmit = async () => {
try {
const values = await form.validateFields()
setLoading(true)
const payload =
activeTab === "email"
? { email: values.email, email_code: values.code }
: { phone: values.phone, phone_code: values.code }
const result = await bindContact(payload)
message.success("绑定成功")
onSuccess?.(result.user)
} catch (error) {
// error handled by interceptor
} finally {
setLoading(false)
}
}
const {
activeTab,
setActiveTab,
form,
loading,
codeLoading,
countdown,
codeDisabled,
handleSendCode,
handleSubmit,
} = useBindContactForm({ open, onSuccess, onCancel })
return (
<Modal
@@ -111,7 +47,7 @@ const BindContactModal: React.FC<BindContactModalProps> = ({ open, onSuccess, on
<Input placeholder="请输入邮箱地址" size="large" />
</Form.Item>
<Form.Item
name="code"
name="email_code"
label="验证码"
rules={[{ required: true, message: "请输入验证码" }]}
>
@@ -121,7 +57,7 @@ const BindContactModal: React.FC<BindContactModalProps> = ({ open, onSuccess, on
size="large"
onClick={handleSendCode}
loading={codeLoading}
disabled={countdown > 0}
disabled={codeDisabled}
>
{countdown > 0 ? `${countdown}s 后重发` : "发送验证码"}
</Button>
@@ -146,7 +82,7 @@ const BindContactModal: React.FC<BindContactModalProps> = ({ open, onSuccess, on
<Input placeholder="请输入手机号" size="large" />
</Form.Item>
<Form.Item
name="code"
name="phone_code"
label="验证码"
rules={[{ required: true, message: "请输入验证码" }]}
>
@@ -156,7 +92,7 @@ const BindContactModal: React.FC<BindContactModalProps> = ({ open, onSuccess, on
size="large"
onClick={handleSendCode}
loading={codeLoading}
disabled={countdown > 0}
disabled={codeDisabled}
>
{countdown > 0 ? `${countdown}s 后重发` : "发送验证码"}
</Button>
@@ -178,3 +114,4 @@ const BindContactModal: React.FC<BindContactModalProps> = ({ open, onSuccess, on
}
export default BindContactModal
export type { BindContactModalProps } from "./types"
@@ -0,0 +1,14 @@
import type { BindContactResponse } from "@/api/auth"
/** BindContactModal 组件属性 */
export interface BindContactModalProps {
/** 是否打开 */
open: boolean
/** 绑定成功回调 */
onSuccess?: (user: BindContactResponse["user"]) => void
/** 取消回调 */
onCancel?: () => void
}
/** 联系方式类型 */
export type ContactType = "email" | "phone"
-182
View File
@@ -1,182 +0,0 @@
/**
* PageHead - 页面头部组件(Task 1.4
*
* 功能:
* - 页面标题展示
* - 面包屑导航(自动根据路由生成,也支持手动传入)
* - 右侧操作按钮区(slot,由页面自行填充)
* - 响应式:移动端简化布局(隐藏面包屑,缩小标题)
*
* 复用 global.css 中已有的 .xx-page-head 基础样式,
* 补充面包屑、操作区等扩展样式。
*/
import React from "react"
import { useLocation, useNavigate, Link } from "react-router-dom"
import { RightOutlined, HomeOutlined } from "@ant-design/icons"
import "./PageHead.css"
/* ── 类型定义 ─────────────────────────────────────────────── */
/** 面包屑项 */
export interface BreadcrumbItem {
/** 显示文字 */
label: string
/** 路由路径,不传则为当前页(不可点击) */
path?: string
}
/** PageHead 组件属性 */
export interface PageHeadProps {
/** 页面标题 */
title: string
/** 页面描述(可选,显示在标题下方) */
description?: React.ReactNode
/** 面包屑项(可选,不传则自动根据路由生成) */
breadcrumb?: BreadcrumbItem[]
/** 右侧操作区内容(按钮等) */
actions?: React.ReactNode
/** 是否隐藏面包屑 */
hideBreadcrumb?: boolean
}
/* ── 路由 → 标题映射(用于自动生成面包屑) ────────────────── */
const ROUTE_TITLE_MAP: Record<string, string> = {
"/app/dashboard": "首页",
"/app/generate": "智能剪辑",
"/app/assets": "视频库",
"/app/voices": "配音库",
"/app/titles": "标题库",
"/app/products": "成片库",
"/app/templates": "模板库",
"/app/history": "任务历史",
"/app/admin": "控制台",
"/app/admin/users": "用户管理",
"/app/admin/analytics": "数据分析",
"/app/admin/monitor": "系统监控",
"/app/admin/logs": "系统日志",
"/app/subscription": "订阅管理",
"/app/subscription/upgrade": "升级订阅",
"/app/subscription/billing": "账单管理",
"/app/profile": "个人设置",
"/app/editing-planner": "模板制作",
"/app/my-templates": "我的模板",
"/app/voice-clone": "我的音色",
"/app/voice-materials": "配音库",
"/app/accounts": "账号管理",
"/app/duplication": "查重",
"/app/duplication/results": "查重结果",
}
/* ── 自动生成面包屑 ─────────────────────────────────────── */
/** 根据当前路径生成面包屑 */
const generateBreadcrumb = (pathname: string): BreadcrumbItem[] => {
const items: BreadcrumbItem[] = [{ label: "首页", path: "/app/dashboard" }]
// 首页本身不需要面包屑
if (pathname === "/app" || pathname === "/app/dashboard") {
return items
}
// 逐级拆分路径,生成中间层级
const segments = pathname.split("/").filter(Boolean)
let currentPath = ""
for (let i = 0; i < segments.length; i++) {
currentPath += `/${segments[i]}`
const title = ROUTE_TITLE_MAP[currentPath]
if (title) {
// 最后一级不带 path(当前页面,不可点击)
const isLast = i === segments.length - 1
items.push({
label: title,
path: isLast ? undefined : currentPath,
})
} else {
// 动态路由段(如 :id),用路径片段做 label
const isLast = i === segments.length - 1
items.push({
label: segments[i],
path: isLast ? undefined : currentPath,
})
}
}
return items
}
/* ── 组件 ───────────────────────────────────────────────── */
const PageHead: React.FC<PageHeadProps> = ({
title,
description,
breadcrumb,
actions,
hideBreadcrumb = false,
}) => {
const location = useLocation()
const navigate = useNavigate()
// 使用传入的面包屑或自动生成
const breadcrumbItems = breadcrumb ?? generateBreadcrumb(location.pathname)
// 首页不显示面包屑
const showBreadcrumb =
!hideBreadcrumb &&
breadcrumbItems.length > 1 &&
location.pathname !== "/app" &&
location.pathname !== "/app/dashboard"
return (
<header className="xx-page-head">
<div className="xx-page-head-left">
{/* 面包屑导航 */}
{showBreadcrumb && (
<nav className="xx-page-breadcrumb" aria-label="面包屑导航">
<ol>
{breadcrumbItems.map((item, index) => {
const isLast = index === breadcrumbItems.length - 1
return (
<li key={`${item.label}-${index}`} className="xx-page-breadcrumb-item">
{index > 0 && <RightOutlined className="xx-page-breadcrumb-separator" />}
{item.path && !isLast ? (
<Link
to={item.path}
className="xx-page-breadcrumb-link"
onClick={(e) => {
e.preventDefault()
navigate(item.path!)
}}
>
{index === 0 ? <HomeOutlined className="xx-page-breadcrumb-home" /> : null}
<span>{item.label}</span>
</Link>
) : (
<span className="xx-page-breadcrumb-current" aria-current="page">
{index === 0 ? <HomeOutlined className="xx-page-breadcrumb-home" /> : null}
<span>{item.label}</span>
</span>
)}
</li>
)
})}
</ol>
</nav>
)}
{/* 标题 + 描述 */}
<div className="xx-page-head-title">
<h2>{title}</h2>
{description && <p>{description}</p>}
</div>
</div>
{/* 右侧操作区 */}
{actions && <div className="xx-page-head-actions">{actions}</div>}
</header>
)
}
export default PageHead
@@ -0,0 +1,27 @@
/** 路由 → 标题映射(用于自动生成面包屑) */
export const ROUTE_TITLE_MAP: Record<string, string> = {
"/app/dashboard": "首页",
"/app/generate": "智能剪辑",
"/app/assets": "视频库",
"/app/voices": "配音库",
"/app/titles": "标题库",
"/app/products": "成片库",
"/app/templates": "模板库",
"/app/history": "任务历史",
"/app/admin": "控制台",
"/app/admin/users": "用户管理",
"/app/admin/analytics": "数据分析",
"/app/admin/monitor": "系统监控",
"/app/admin/logs": "系统日志",
"/app/subscription": "订阅管理",
"/app/subscription/upgrade": "升级订阅",
"/app/subscription/billing": "账单管理",
"/app/profile": "个人设置",
"/app/editing-planner": "模板制作",
"/app/my-templates": "我的模板",
"/app/voice-clone": "我的音色",
"/app/voice-materials": "配音库",
"/app/accounts": "账号管理",
"/app/duplication": "查重",
"/app/duplication/results": "查重结果",
}
@@ -0,0 +1,91 @@
/**
* PageHead - 页面头部组件(Task 1.4
*
* 功能:
* - 页面标题展示
* - 面包屑导航(自动根据路由生成,也支持手动传入)
* - 右侧操作按钮区(slot,由页面自行填充)
* - 响应式:移动端简化布局(隐藏面包屑,缩小标题)
*
* 复用 global.css 中已有的 .xx-page-head 基础样式,
* 补充面包屑、操作区等扩展样式。
*/
import React from "react"
import { useLocation, useNavigate, Link } from "react-router-dom"
import { RightOutlined, HomeOutlined } from "@ant-design/icons"
import type { PageHeadProps } from "./types"
import { generateBreadcrumb } from "./utils"
import "./PageHead.css"
const PageHead: React.FC<PageHeadProps> = ({
title,
description,
breadcrumb,
actions,
hideBreadcrumb = false,
}) => {
const location = useLocation()
const navigate = useNavigate()
// 使用传入的面包屑或自动生成
const breadcrumbItems = breadcrumb ?? generateBreadcrumb(location.pathname)
// 首页不显示面包屑
const showBreadcrumb =
!hideBreadcrumb &&
breadcrumbItems.length > 1 &&
location.pathname !== "/app" &&
location.pathname !== "/app/dashboard"
return (
<header className="xx-page-head">
<div className="xx-page-head-left">
{/* 面包屑导航 */}
{showBreadcrumb && (
<nav className="xx-page-breadcrumb" aria-label="面包屑导航">
<ol>
{breadcrumbItems.map((item, index) => {
const isLast = index === breadcrumbItems.length - 1
return (
<li key={`${item.label}-${index}`} className="xx-page-breadcrumb-item">
{index > 0 && <RightOutlined className="xx-page-breadcrumb-separator" />}
{item.path && !isLast ? (
<Link
to={item.path}
className="xx-page-breadcrumb-link"
onClick={(e) => {
e.preventDefault()
navigate(item.path!)
}}
>
{index === 0 ? <HomeOutlined className="xx-page-breadcrumb-home" /> : null}
<span>{item.label}</span>
</Link>
) : (
<span className="xx-page-breadcrumb-current" aria-current="page">
{index === 0 ? <HomeOutlined className="xx-page-breadcrumb-home" /> : null}
<span>{item.label}</span>
</span>
)}
</li>
)
})}
</ol>
</nav>
)}
{/* 标题 + 描述 */}
<div className="xx-page-head-title">
<h2>{title}</h2>
{description && <p>{description}</p>}
</div>
</div>
{/* 右侧操作区 */}
{actions && <div className="xx-page-head-actions">{actions}</div>}
</header>
)
}
export default PageHead
export type { BreadcrumbItem, PageHeadProps } from "./types"
@@ -0,0 +1,23 @@
import type React from "react"
/** 面包屑项 */
export interface BreadcrumbItem {
/** 显示文字 */
label: string
/** 路由路径,不传则为当前页(不可点击) */
path?: string
}
/** PageHead 组件属性 */
export interface PageHeadProps {
/** 页面标题 */
title: string
/** 页面描述(可选,显示在标题下方) */
description?: React.ReactNode
/** 面包屑项(可选,不传则自动根据路由生成) */
breadcrumb?: BreadcrumbItem[]
/** 右侧操作区内容(按钮等) */
actions?: React.ReactNode
/** 是否隐藏面包屑 */
hideBreadcrumb?: boolean
}
@@ -0,0 +1,39 @@
import { ROUTE_TITLE_MAP } from "./constants"
import type { BreadcrumbItem } from "./types"
/** 根据当前路径生成面包屑 */
export const generateBreadcrumb = (pathname: string): BreadcrumbItem[] => {
const items: BreadcrumbItem[] = [{ label: "首页", path: "/app/dashboard" }]
// 首页本身不需要面包屑
if (pathname === "/app" || pathname === "/app/dashboard") {
return items
}
// 逐级拆分路径,生成中间层级
const segments = pathname.split("/").filter(Boolean)
let currentPath = ""
for (let i = 0; i < segments.length; i++) {
currentPath += `/${segments[i]}`
const title = ROUTE_TITLE_MAP[currentPath]
if (title) {
// 最后一级不带 path(当前页面,不可点击)
const isLast = i === segments.length - 1
items.push({
label: title,
path: isLast ? undefined : currentPath,
})
} else {
// 动态路由段(如 :id),用路径片段做 label
const isLast = i === segments.length - 1
items.push({
label: segments[i],
path: isLast ? undefined : currentPath,
})
}
}
return items
}
@@ -1,82 +0,0 @@
/**
* CloneModal — 音色克隆弹窗(共享组件)
*
* 功能:
* - 步骤引导:上传/录制音频 → 填写信息 → 提交克隆
* - 两种输入方式:上传文件(wav/mp3/m4a,≤10MB)或浏览器原生录音(MediaRecorder
* - 提交:先 uploadAsset 获取真实 URL,再 createVoiceClone
* - 进度展示:上传中 → 克隆中 → 完成(三阶段可视化)
* - 错误处理:上传失败、格式错误、大小超限、录音权限拒绝等提示
*
* V21 Design System — 零 antd 直接导入
*/
import React from "react"
import { Modal } from "@/components/ui"
import type { CloneModalProps } from "./types/cloneModal"
import useCloneModal from "./hooks/useCloneModal"
import InputView from "./clone-modal/InputView"
import ProgressView from "./clone-modal/ProgressView"
import "./clone-modal.css"
const CloneModal: React.FC<CloneModalProps> = ({ open, onClose, onSuccess }) => {
const {
phase,
voiceName,
voiceDescription,
selectedFile,
dragActive,
errorMessage,
isRecording,
recordTime,
recordedBlob,
canSubmit,
isProcessing,
setVoiceName,
setVoiceDescription,
setDragActive,
handleFileSelect,
handleRecordToggle,
handleClose,
handleSubmit,
} = useCloneModal({ open, onClose, onSuccess })
return (
<Modal
open={open}
onCancel={handleClose}
title="克隆我的音色"
width={560}
footer={null}
destroyOnClose
maskClosable={!isProcessing}
keyboard={!isProcessing}
>
{/* 输入阶段 */}
{phase === "input" && (
<InputView
voiceName={voiceName}
voiceDescription={voiceDescription}
selectedFile={selectedFile}
dragActive={dragActive}
isRecording={isRecording}
recordTime={recordTime}
recordedBlob={recordedBlob}
errorMessage={errorMessage}
canSubmit={canSubmit}
onVoiceNameChange={setVoiceName}
onVoiceDescChange={setVoiceDescription}
onDragActiveChange={setDragActive}
onFileSelect={handleFileSelect}
onRecordToggle={handleRecordToggle}
onClose={handleClose}
onSubmit={handleSubmit}
/>
)}
{/* 进度 / 完成阶段 */}
{phase !== "input" && <ProgressView phase={phase} />}
</Modal>
)
}
export default CloneModal
@@ -0,0 +1,20 @@
import type { ProgressStep } from "./types"
/** 进度阶段配置 */
export const PROGRESS_STEPS: ProgressStep[] = [
{ key: "uploading", label: "上传中", icon: "📤" },
{ key: "cloning", label: "克隆中", icon: "🧬" },
{ key: "done", label: "完成", icon: "✅" },
]
/** 支持的音频扩展名 */
export const ACCEPTED_EXTENSIONS = ["mp3", "wav", "m4a", "webm"]
/** 文件选择器 accept 属性 */
export const ACCEPTED_MIME = ".mp3,.wav,.m4a,.webm,audio/mpeg,audio/wav,audio/mp4,audio/webm"
/** 最大文件大小:10MB */
export const MAX_FILE_SIZE = 10 * 1024 * 1024
/** 最长录制时长:5 分钟(秒) */
export const MAX_RECORD_SECONDS = 5 * 60
@@ -0,0 +1,146 @@
import { useState, useRef, useCallback, useEffect } from "react"
import { MAX_RECORD_SECONDS } from "../constants"
import { formatRecordTime } from "../utils"
/**
* 录音 Hook
* 封装浏览器 MediaRecorder 录音逻辑,含计时、自动停止、时长上限
*/
export const useAudioRecorder = (onError?: (msg: string) => void) => {
const [isRecording, setIsRecording] = useState(false)
const [recordTime, setRecordTime] = useState(0)
const [recordedBlob, setRecordedBlob] = useState<Blob | null>(null)
const recordTimerRef = useRef<ReturnType<typeof setInterval> | null>(null)
const mediaRecorderRef = useRef<MediaRecorder | null>(null)
const audioChunksRef = useRef<Blob[]>([])
const isMountedRef = useRef(true)
const isStartingRef = useRef(false)
/** 重置录音状态 */
const reset = useCallback(() => {
if (!isMountedRef.current) return
setIsRecording(false)
setRecordTime(0)
setRecordedBlob(null)
audioChunksRef.current = []
isStartingRef.current = false
if (recordTimerRef.current) {
clearInterval(recordTimerRef.current)
recordTimerRef.current = null
}
if (mediaRecorderRef.current && mediaRecorderRef.current.state !== "inactive") {
mediaRecorderRef.current.stop()
}
mediaRecorderRef.current = null
}, [])
/** 开始/停止录制 */
const toggleRecord = useCallback(async () => {
if (isRecording) {
// 停止录制
setIsRecording(false)
if (recordTimerRef.current) {
clearInterval(recordTimerRef.current)
recordTimerRef.current = null
}
if (mediaRecorderRef.current && mediaRecorderRef.current.state !== "inactive") {
mediaRecorderRef.current.stop()
}
return
}
// 防止快速双击导致重复启动
if (isStartingRef.current) return
isStartingRef.current = true
// 开始录制
try {
const stream = await navigator.mediaDevices.getUserMedia({
audio: true,
})
if (!isMountedRef.current) {
stream.getTracks().forEach((track) => track.stop())
isStartingRef.current = false
return
}
const mediaRecorder = new MediaRecorder(stream)
mediaRecorderRef.current = mediaRecorder
audioChunksRef.current = []
mediaRecorder.ondataavailable = (event) => {
if (event.data.size > 0) {
audioChunksRef.current.push(event.data)
}
}
mediaRecorder.onstop = () => {
if (recordTimerRef.current) {
clearInterval(recordTimerRef.current)
recordTimerRef.current = null
}
if (!isMountedRef.current) return
// 使用浏览器实际生成的 MIME 类型,避免跨浏览器兼容性问题
const mimeType = mediaRecorder.mimeType || "audio/webm"
const blob = new Blob(audioChunksRef.current, { type: mimeType })
setRecordedBlob(blob)
// 停止音轨
stream.getTracks().forEach((track) => track.stop())
isStartingRef.current = false
}
mediaRecorder.start()
setIsRecording(true)
setRecordTime(0)
setRecordedBlob(null)
recordTimerRef.current = setInterval(() => {
setRecordTime((prev) => {
const next = prev + 1
if (next >= MAX_RECORD_SECONDS) {
// 达到时长上限,自动停止
setTimeout(() => {
if (!isMountedRef.current) return
setIsRecording(false)
if (recordTimerRef.current) {
clearInterval(recordTimerRef.current)
recordTimerRef.current = null
}
if (mediaRecorderRef.current && mediaRecorderRef.current.state !== "inactive") {
mediaRecorderRef.current.stop()
}
onError?.("已达最长录制时长(5分钟),已自动停止")
}, 0)
return MAX_RECORD_SECONDS
}
return next
})
}, 1000)
} catch {
isStartingRef.current = false
onError?.("无法访问麦克风,请检查浏览器权限设置")
}
}, [isRecording, onError])
// 组件卸载时清理
useEffect(() => {
isMountedRef.current = true
return () => {
isMountedRef.current = false
isStartingRef.current = false
if (recordTimerRef.current) clearInterval(recordTimerRef.current)
if (mediaRecorderRef.current && mediaRecorderRef.current.state !== "inactive") {
mediaRecorderRef.current.stop()
}
}
}, [])
return {
isRecording,
recordTime,
recordedBlob,
formattedTime: formatRecordTime(recordTime),
toggleRecord,
reset,
}
}
@@ -0,0 +1,460 @@
import React, { useState, useCallback, useRef, useEffect } from "react"
import { Modal, Button } from "@/components/ui"
import { createVoiceClone, toVoiceClone } from "@/api/voice-clone"
import { uploadAsset } from "@/api/assets"
import { PROGRESS_STEPS, ACCEPTED_MIME } from "./constants"
import { validateFile } from "./utils"
import { useAudioRecorder } from "./hooks/useAudioRecorder"
import type { CloneModalProps, ModalPhase } from "./types"
import "./clone-modal.css"
/** 根据 MIME 类型推断文件扩展名 */
const getExtensionFromMime = (mime: string): string => {
if (mime.includes("webm")) return "webm"
if (mime.includes("mp4") || mime.includes("m4a")) return "m4a"
if (mime.includes("ogg")) return "ogg"
if (mime.includes("wav")) return "wav"
return "webm"
}
const CloneModal: React.FC<CloneModalProps> = ({ open, onClose, onSuccess }) => {
const [phase, setPhase] = useState<ModalPhase>("input")
const [voiceName, setVoiceName] = useState("")
const [voiceDescription, setVoiceDescription] = useState("")
const [selectedFile, setSelectedFile] = useState<File | null>(null)
const [dragActive, setDragActive] = useState(false)
const [errorMessage, setErrorMessage] = useState("")
const fileInputRef = useRef<HTMLInputElement>(null)
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
/** 默认音色名称计数器(组件级 ref,避免多实例串号) */
const cloneCounterRef = useRef(1)
/** 组件挂载状态标记,防止卸载后更新 state */
const isMountedRef = useRef(true)
const isSubmittingRef = useRef(false)
/* ── 录音 Hook ──────────────────────────────────── */
const {
isRecording,
recordedBlob,
formattedTime,
toggleRecord,
reset: resetRecorder,
} = useAudioRecorder(setErrorMessage)
/** 生成下一个默认音色名称 */
const getNextDefaultName = useCallback((): string => {
const name = `我的声音 ${cloneCounterRef.current}`
cloneCounterRef.current += 1
return name
}, [])
/** 重置弹窗状态 */
const resetState = useCallback(() => {
setPhase("input")
setVoiceName(getNextDefaultName())
setVoiceDescription("")
setSelectedFile(null)
setDragActive(false)
if (isSubmittingRef.current) return
isSubmittingRef.current = true
setErrorMessage("")
resetRecorder()
}, [getNextDefaultName, resetRecorder])
/** 关闭弹窗 */
const handleClose = useCallback(() => {
resetState()
onClose()
}, [resetState, onClose])
/** 弹窗打开时重置状态 */
useEffect(() => {
if (open) {
resetState()
}
}, [open, resetState])
/** 组件挂载/卸载标记 + 清理定时器 */
useEffect(() => {
isMountedRef.current = true
return () => {
isMountedRef.current = false
if (timerRef.current) clearTimeout(timerRef.current)
}
}, [])
/* ── 文件上传 ──────────────────────────────────── */
const handleUploadClick = () => {
fileInputRef.current?.click()
}
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0]
if (file) {
const error = validateFile(file)
if (error) {
setErrorMessage(error)
setSelectedFile(null)
} else {
if (isSubmittingRef.current) return
isSubmittingRef.current = true
setErrorMessage("")
setSelectedFile(file)
resetRecorder()
}
}
e.target.value = ""
}
/* ── 拖拽 ──────────────────────────────────────── */
const handleDrag = (e: React.DragEvent) => {
e.preventDefault()
e.stopPropagation()
if (e.type === "dragenter" || e.type === "dragover") {
setDragActive(true)
} else if (e.type === "dragleave") {
setDragActive(false)
}
}
const handleDrop = (e: React.DragEvent) => {
e.preventDefault()
e.stopPropagation()
setDragActive(false)
const file = e.dataTransfer.files?.[0]
if (file) {
const error = validateFile(file)
if (error) {
setErrorMessage(error)
setSelectedFile(null)
} else {
if (isSubmittingRef.current) return
isSubmittingRef.current = true
setErrorMessage("")
setSelectedFile(file)
resetRecorder()
}
}
}
/* ── 计算属性 ──────────────────────────────────── */
const hasAudio = selectedFile !== null || recordedBlob !== null
const isProcessing = phase === "uploading" || phase === "cloning"
const canSubmit = hasAudio && !isProcessing
const progressIndex = PROGRESS_STEPS.findIndex((s) => s.key === phase)
/* ── 提交 ──────────────────────────────────────── */
const handleSubmit = async () => {
const name = voiceName.trim()
if (!name || name.length < 2 || name.length > 20) {
setErrorMessage("音色名称需在 2-20 个字符之间")
return
}
if (!hasAudio) {
setErrorMessage("请上传音频文件或录制一段声音")
return
}
if (isSubmittingRef.current) return
isSubmittingRef.current = true
setErrorMessage("")
try {
// 阶段 1:上传音频
setPhase("uploading")
let fileToUpload: File
if (selectedFile) {
fileToUpload = selectedFile
} else {
// 使用浏览器实际生成的 MIME 类型,避免跨浏览器格式不匹配
const mimeType = recordedBlob?.type || "audio/webm"
const ext = getExtensionFromMime(mimeType)
fileToUpload = new File([recordedBlob!], `recorded-${Date.now()}.${ext}`, {
type: mimeType,
})
}
const formData = new FormData()
formData.append("file", fileToUpload)
const uploadResult = await uploadAsset(formData)
// 组件已卸载则中止后续操作
if (!isMountedRef.current) return
// 阶段 2:克隆
setPhase("cloning")
const result = await createVoiceClone({
name,
description: voiceDescription.trim() || undefined,
audio_url: uploadResult.url,
})
// 组件已卸载则中止后续操作
if (!isMountedRef.current) return
isSubmittingRef.current = false
// 阶段 3:完成
setPhase("done")
// 2秒后自动关闭
timerRef.current = setTimeout(() => {
if (isMountedRef.current) {
onSuccess?.(toVoiceClone(result))
handleClose()
}
}, 2000)
} catch (err) {
isSubmittingRef.current = false
// 组件已卸载则不更新 state
if (!isMountedRef.current) return
setPhase("input")
setErrorMessage(err instanceof Error ? err.message : "克隆失败,请重试")
}
}
return (
<Modal
open={open}
onCancel={handleClose}
title="克隆我的音色"
width={560}
footer={null}
destroyOnClose
maskClosable={!isProcessing}
keyboard={!isProcessing}
>
{/* ── 输入阶段 ────────────────────────────────── */}
{phase === "input" && (
<div className="xx-clonemodal-body">
{/* 步骤引导 */}
<div className="xx-clonemodal-steps">
<div className="xx-clonemodal-step xx-clonemodal-step--active">
<div className="xx-clonemodal-step-number">1</div>
<span className="xx-clonemodal-step-label">/</span>
</div>
<div className="xx-clonemodal-step-connector" />
<div className="xx-clonemodal-step">
<div className="xx-clonemodal-step-number">2</div>
<span className="xx-clonemodal-step-label"></span>
</div>
<div className="xx-clonemodal-step-connector" />
<div className="xx-clonemodal-step">
<div className="xx-clonemodal-step-number">3</div>
<span className="xx-clonemodal-step-label"></span>
</div>
</div>
{/* 音色名称 */}
<div className="xx-clonemodal-field">
<label className="xx-clonemodal-label">
<span className="xx-clonemodal-required">*</span>
</label>
<input
type="text"
className="xx-clonemodal-input"
value={voiceName}
onChange={(e) => setVoiceName(e.target.value)}
placeholder="输入音色名称(2-20字符)"
maxLength={20}
/>
<div className="xx-clonemodal-char-count">{voiceName.length}/20</div>
</div>
{/* 上传区域 */}
<div className="xx-clonemodal-field">
<label className="xx-clonemodal-label"></label>
<div
className={`xx-clonemodal-upload-zone${dragActive ? " xx-clonemodal-upload-zone--active" : ""}${selectedFile ? " xx-clonemodal-upload-zone--has-file" : ""}`}
onClick={handleUploadClick}
onDragEnter={handleDrag}
onDragOver={handleDrag}
onDragLeave={handleDrag}
onDrop={handleDrop}
>
<div className="xx-clonemodal-upload-icon">{selectedFile ? "📄" : "🎵"}</div>
<p className="xx-clonemodal-upload-title">
{selectedFile ? selectedFile.name : "拖拽音频文件到此处,或点击上传"}
</p>
<p className="xx-clonemodal-upload-hint"> MP3WAVM4A 10MB</p>
<input
ref={fileInputRef}
type="file"
accept={ACCEPTED_MIME}
style={{ display: "none" }}
onChange={handleFileChange}
/>
</div>
</div>
{/* 或分隔 */}
<div className="xx-clonemodal-divider">
<div className="xx-clonemodal-divider-line" />
<span className="xx-clonemodal-divider-text"></span>
<div className="xx-clonemodal-divider-line" />
</div>
{/* 录制区域 */}
<div className="xx-clonemodal-field">
<label className="xx-clonemodal-label"></label>
<div className="xx-clonemodal-record-area">
<div className="xx-clonemodal-record-info">
<p className="xx-clonemodal-record-hint">
{isRecording
? `录制中 ${formattedTime}`
: recordedBlob
? `已录制 ${formattedTime}`
: "点击按钮开始录制(最长 5 分钟)"}
</p>
{isRecording && (
<div className="xx-clonemodal-record-wave">
<span className="xx-clonemodal-record-wave-bar" />
<span className="xx-clonemodal-record-wave-bar" />
<span className="xx-clonemodal-record-wave-bar" />
<span className="xx-clonemodal-record-wave-bar" />
<span className="xx-clonemodal-record-wave-bar" />
</div>
)}
</div>
<button
type="button"
className={`xx-clonemodal-record-btn${isRecording ? " xx-clonemodal-record-btn--recording" : ""}`}
onClick={toggleRecord}
title={isRecording ? "停止录制" : "开始录制"}
>
{isRecording ? "⏹" : "🎙️"}
</button>
</div>
</div>
{/* 音色描述 */}
<div className="xx-clonemodal-field">
<label className="xx-clonemodal-label"></label>
<textarea
className="xx-clonemodal-textarea"
value={voiceDescription}
onChange={(e) => setVoiceDescription(e.target.value)}
placeholder="可选,描述这个音色的特点(最多100字符)"
maxLength={100}
rows={3}
/>
<div className="xx-clonemodal-char-count">{voiceDescription.length}/100</div>
</div>
{/* 错误提示 */}
{errorMessage && (
<div className="xx-clonemodal-error">
<span className="xx-clonemodal-error-icon"></span>
<span>{errorMessage}</span>
</div>
)}
{/* 提示 */}
<div className="xx-clonemodal-tip">
<span className="xx-clonemodal-tip-icon">💡</span>
<span> 10 ~ 3 </span>
</div>
{/* 底部按钮 */}
<div className="xx-clonemodal-footer">
<Button buttonType="ghost" onClick={handleClose}>
</Button>
<Button buttonType="primary" disabled={!canSubmit} onClick={handleSubmit}>
🎤
</Button>
</div>
</div>
)}
{/* ── 进度阶段(上传中 / 克隆中) ────────────── */}
{isProcessing && (
<div className="xx-clonemodal-progress-body">
{/* 步骤指示器 */}
<div className="xx-clonemodal-steps-progress">
{PROGRESS_STEPS.map((step, idx) => {
const isActive = idx === progressIndex
const isDone = idx < progressIndex
const stepClass = [
"xx-clonemodal-step-progress",
isActive ? "xx-clonemodal-step-progress--active" : "",
isDone ? "xx-clonemodal-step-progress--done" : "",
]
.filter(Boolean)
.join(" ")
return (
<React.Fragment key={step.key}>
{idx > 0 && (
<div
className={`xx-clonemodal-step-connector${isDone ? " xx-clonemodal-step-connector--done" : ""}`}
/>
)}
<div className={stepClass}>
<div className="xx-clonemodal-step-icon">{isDone ? "✓" : step.icon}</div>
<span className="xx-clonemodal-step-label">{step.label}</span>
</div>
</React.Fragment>
)
})}
</div>
{/* 当前阶段描述 */}
<div className="xx-clonemodal-progress-info">
{phase === "uploading" && (
<>
<div className="xx-clonemodal-progress-spinner" />
<p className="xx-clonemodal-progress-text"></p>
<p className="xx-clonemodal-progress-sub"></p>
</>
)}
{phase === "cloning" && (
<>
<div className="xx-clonemodal-progress-spinner xx-clonemodal-progress-spinner--cloning" />
<p className="xx-clonemodal-progress-text">AI </p>
<p className="xx-clonemodal-progress-sub"></p>
</>
)}
</div>
</div>
)}
{/* ── 完成阶段 ────────────────────────────────── */}
{phase === "done" && (
<div className="xx-clonemodal-progress-body">
{/* 步骤指示器(全部完成) */}
<div className="xx-clonemodal-steps-progress">
{PROGRESS_STEPS.map((step, idx) => (
<React.Fragment key={step.key}>
{idx > 0 && (
<div className="xx-clonemodal-step-connector xx-clonemodal-step-connector--done" />
)}
<div className="xx-clonemodal-step-progress xx-clonemodal-step-progress--done">
<div className="xx-clonemodal-step-icon"></div>
<span className="xx-clonemodal-step-label">{step.label}</span>
</div>
</React.Fragment>
))}
</div>
<div className="xx-clonemodal-success">
<div className="xx-clonemodal-success-icon">🎉</div>
<h3 className="xx-clonemodal-success-title"></h3>
<p className="xx-clonemodal-success-desc">
</p>
</div>
</div>
)}
</Modal>
)
}
export default CloneModal
export type { CloneModalProps, ModalPhase } from "./types"
@@ -0,0 +1,21 @@
import type { VoiceClone } from "@/api/voice-clone"
/** 弹窗阶段 */
export type ModalPhase = "input" | "uploading" | "cloning" | "done"
/** CloneModal 组件属性 */
export interface CloneModalProps {
/** 弹窗是否可见 */
open: boolean
/** 关闭弹窗回调 */
onClose: () => void
/** 克隆成功回调(返回新创建的音色) */
onSuccess?: (voice: VoiceClone) => void
}
/** 进度步骤配置 */
export interface ProgressStep {
key: string
label: string
icon: string
}
@@ -0,0 +1,23 @@
import { ACCEPTED_EXTENSIONS, MAX_FILE_SIZE } from "./constants"
/**
* 验证音频文件
* @returns 错误信息,null 表示验证通过
*/
export const validateFile = (file: File): string | null => {
const ext = file.name.split(".").pop()?.toLowerCase()
if (!ext || !ACCEPTED_EXTENSIONS.includes(ext)) {
return "不支持的音频格式,请上传 MP3、WAV 或 M4A 文件"
}
if (file.size > MAX_FILE_SIZE) {
return "文件大小超过 10MB,请压缩后重试"
}
return null
}
/** 格式化录制时间 mm:ss */
export const formatRecordTime = (seconds: number): string => {
const m = Math.floor(seconds / 60)
const s = seconds % 60
return `${m.toString().padStart(2, "0")}:${s.toString().padStart(2, "0")}`
}
@@ -0,0 +1,134 @@
import { useState, useRef, useCallback, useEffect } from "react"
import type { ModalPhase } from "../types/cloneModal"
import { MIN_VOICE_NAME_LENGTH, MAX_VOICE_NAME_LENGTH } from "../constants/cloneModal"
import useAudioRecorder from "./useAudioRecorder"
/**
* 克隆弹窗表单状态 Hook
* 管理表单字段、录音、文件选择、验证逻辑
*/
export function useCloneFormState({ open, onClose }: { open: boolean; onClose: () => void }) {
const [phase, setPhase] = useState<ModalPhase>("input")
const [voiceName, setVoiceName] = useState("")
const [voiceDescription, setVoiceDescription] = useState("")
const [selectedFile, setSelectedFile] = useState<File | null>(null)
const [dragActive, setDragActive] = useState(false)
const [errorMessage, setErrorMessage] = useState("")
const { isRecording, recordTime, recordedBlob, toggleRecording, resetRecording } =
useAudioRecorder()
/** 默认音色名称计数器 */
const cloneCounterRef = useRef(1)
const getNextDefaultName = useCallback((): string => {
const name = `我的声音 ${cloneCounterRef.current}`
cloneCounterRef.current += 1
return name
}, [])
const hasAudio = selectedFile !== null || recordedBlob !== null
const canSubmit =
voiceName.trim().length >= MIN_VOICE_NAME_LENGTH &&
voiceName.trim().length <= MAX_VOICE_NAME_LENGTH &&
hasAudio
const isProcessing = phase === "uploading" || phase === "cloning"
/** 重置弹窗状态 */
const resetState = useCallback(() => {
setPhase("input")
setVoiceName(getNextDefaultName())
setVoiceDescription("")
setSelectedFile(null)
setDragActive(false)
setErrorMessage("")
resetRecording()
}, [getNextDefaultName, resetRecording])
/** 关闭弹窗 */
const handleClose = useCallback(() => {
resetState()
onClose()
}, [resetState, onClose])
/** 弹窗打开时重置状态 */
useEffect(() => {
if (open) {
resetState()
}
}, [open, resetState])
/** 选择文件(来自上传或拖拽) */
const handleFileSelect = useCallback(
(file: File | null, error: string) => {
if (error) {
setErrorMessage(error)
setSelectedFile(null)
} else {
setErrorMessage("")
setSelectedFile(file)
// 清除录音
resetRecording()
}
},
[resetRecording],
)
/** 录音切换 */
const handleRecordToggle = useCallback(() => {
setErrorMessage("")
if (isRecording) {
toggleRecording()
} else {
// 开始录制前清除已选文件
setSelectedFile(null)
toggleRecording()
}
}, [isRecording, toggleRecording])
/** 表单验证 */
const validateForm = useCallback((): string | null => {
const name = voiceName.trim()
if (!name) {
return "请输入音色名称"
}
if (name.length < MIN_VOICE_NAME_LENGTH || name.length > MAX_VOICE_NAME_LENGTH) {
return `音色名称需在 ${MIN_VOICE_NAME_LENGTH}-${MAX_VOICE_NAME_LENGTH} 个字符之间`
}
if (!hasAudio) {
return "请上传音频文件或录制一段声音"
}
return null
}, [voiceName, hasAudio])
return {
// 状态
phase,
setPhase,
voiceName,
setVoiceName,
voiceDescription,
setVoiceDescription,
selectedFile,
dragActive,
setDragActive,
errorMessage,
setErrorMessage,
// 录音
isRecording,
recordTime,
recordedBlob,
// 计算属性
hasAudio,
canSubmit,
isProcessing,
// handlers
handleFileSelect,
handleRecordToggle,
handleClose,
validateForm,
resetState,
}
}
+33 -202
View File
@@ -1,213 +1,44 @@
import { useState, useRef, useCallback, useEffect } from "react"
import { createVoiceClone, toVoiceClone } from "@/api/voice-clone"
import { uploadAsset } from "@/api/assets"
import type { ModalPhase, CloneModalProps } from "../types/cloneModal"
import { MIN_VOICE_NAME_LENGTH, MAX_VOICE_NAME_LENGTH } from "../constants/cloneModal"
import useAudioRecorder from "./useAudioRecorder"
interface UseCloneModalReturn {
phase: ModalPhase
voiceName: string
voiceDescription: string
selectedFile: File | null
dragActive: boolean
errorMessage: string
isRecording: boolean
recordTime: number
recordedBlob: Blob | null
canSubmit: boolean
isProcessing: boolean
setVoiceName: (value: string) => void
setVoiceDescription: (value: string) => void
setDragActive: (active: boolean) => void
handleFileSelect: (file: File | null, error: string) => void
handleRecordToggle: () => void
handleClose: () => void
handleSubmit: () => void
}
import type { CloneModalProps } from "../types/cloneModal"
import { useCloneFormState } from "./useCloneFormState"
import { useCloneSubmit } from "./useCloneSubmit"
/**
* 音色克隆弹窗主业务 Hook
* 组合表单状态 + 提交流程两个子 Hook
*/
const useCloneModal = ({ open, onClose, onSuccess }: CloneModalProps): UseCloneModalReturn => {
const [phase, setPhase] = useState<ModalPhase>("input")
const [voiceName, setVoiceName] = useState("")
const [voiceDescription, setVoiceDescription] = useState("")
const [selectedFile, setSelectedFile] = useState<File | null>(null)
const [dragActive, setDragActive] = useState(false)
const [errorMessage, setErrorMessage] = useState("")
const useCloneModal = ({ open, onClose, onSuccess }: CloneModalProps) => {
const formState = useCloneFormState({ open, onClose })
const { isRecording, recordTime, recordedBlob, toggleRecording, resetRecording } =
useAudioRecorder()
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
/** 默认音色名称计数器 */
const cloneCounterRef = useRef(1)
const getNextDefaultName = useCallback((): string => {
const name = `我的声音 ${cloneCounterRef.current}`
cloneCounterRef.current += 1
return name
}, [])
const hasAudio = selectedFile !== null || recordedBlob !== null
const canSubmit =
voiceName.trim().length >= MIN_VOICE_NAME_LENGTH &&
voiceName.trim().length <= MAX_VOICE_NAME_LENGTH &&
hasAudio
const isProcessing = phase === "uploading" || phase === "cloning"
/** 重置弹窗状态 */
const resetState = useCallback(() => {
setPhase("input")
setVoiceName(getNextDefaultName())
setVoiceDescription("")
setSelectedFile(null)
setDragActive(false)
setErrorMessage("")
resetRecording()
}, [getNextDefaultName, resetRecording])
/** 关闭弹窗 */
const handleClose = useCallback(() => {
resetState()
onClose()
}, [resetState, onClose])
/** 弹窗打开时重置状态 */
useEffect(() => {
if (open) {
resetState()
}
}, [open, resetState])
/** 组件卸载时清理定时器 */
useEffect(() => {
return () => {
if (timerRef.current) clearTimeout(timerRef.current)
}
}, [])
/** 选择文件(来自上传或拖拽) */
const handleFileSelect = useCallback(
(file: File | null, error: string) => {
if (error) {
setErrorMessage(error)
setSelectedFile(null)
} else {
setErrorMessage("")
setSelectedFile(file)
// 清除录音
resetRecording()
}
},
[resetRecording],
)
/** 录音切换 */
const handleRecordToggle = useCallback(() => {
setErrorMessage("")
if (isRecording) {
toggleRecording()
} else {
// 开始录制前清除已选文件
setSelectedFile(null)
toggleRecording()
}
}, [isRecording, toggleRecording])
/** 表单验证 */
const validateForm = useCallback((): string | null => {
const name = voiceName.trim()
if (!name) {
return "请输入音色名称"
}
if (name.length < MIN_VOICE_NAME_LENGTH || name.length > MAX_VOICE_NAME_LENGTH) {
return `音色名称需在 ${MIN_VOICE_NAME_LENGTH}-${MAX_VOICE_NAME_LENGTH} 个字符之间`
}
if (!hasAudio) {
return "请上传音频文件或录制一段声音"
}
return null
}, [voiceName, hasAudio])
/** 提交克隆 */
const handleSubmit = useCallback(async () => {
const formError = validateForm()
if (formError) {
setErrorMessage(formError)
return
}
setErrorMessage("")
try {
// 阶段 1:上传音频
setPhase("uploading")
let fileToUpload: File
if (selectedFile) {
fileToUpload = selectedFile
} else {
fileToUpload = new File([recordedBlob!], `recorded-${Date.now()}.webm`, {
type: "audio/webm",
})
}
const formData = new FormData()
formData.append("file", fileToUpload)
const uploadResult = await uploadAsset(formData)
// 阶段 2:克隆
setPhase("cloning")
const result = await createVoiceClone({
name: voiceName.trim(),
description: voiceDescription.trim() || undefined,
audio_url: uploadResult.url,
})
// 阶段 3:完成
setPhase("done")
// 2秒后自动关闭
timerRef.current = setTimeout(() => {
onSuccess?.(toVoiceClone(result))
handleClose()
}, 2000)
} catch (err) {
setPhase("input")
setErrorMessage(err instanceof Error ? err.message : "克隆失败,请重试")
}
}, [
validateForm,
selectedFile,
recordedBlob,
voiceName,
voiceDescription,
const { handleSubmit } = useCloneSubmit({
voiceName: formState.voiceName,
voiceDescription: formState.voiceDescription,
selectedFile: formState.selectedFile,
recordedBlob: formState.recordedBlob,
setPhase: formState.setPhase,
setErrorMessage: formState.setErrorMessage,
validateForm: formState.validateForm,
onSuccess,
handleClose,
])
onClose: formState.handleClose,
})
return {
phase,
voiceName,
voiceDescription,
selectedFile,
dragActive,
errorMessage,
isRecording,
recordTime,
recordedBlob,
canSubmit,
isProcessing,
setVoiceName,
setVoiceDescription,
setDragActive,
handleFileSelect,
handleRecordToggle,
handleClose,
phase: formState.phase,
voiceName: formState.voiceName,
voiceDescription: formState.voiceDescription,
selectedFile: formState.selectedFile,
dragActive: formState.dragActive,
errorMessage: formState.errorMessage,
isRecording: formState.isRecording,
recordTime: formState.recordTime,
recordedBlob: formState.recordedBlob,
canSubmit: formState.canSubmit,
isProcessing: formState.isProcessing,
setVoiceName: formState.setVoiceName,
setVoiceDescription: formState.setVoiceDescription,
setDragActive: formState.setDragActive,
handleFileSelect: formState.handleFileSelect,
handleRecordToggle: formState.handleRecordToggle,
handleClose: formState.handleClose,
handleSubmit,
}
}
+108
View File
@@ -0,0 +1,108 @@
import { useRef, useCallback, useEffect } from "react"
import { createVoiceClone, toVoiceClone } from "@/api/voice-clone"
import { uploadAsset, ensureDefaultLibrary } from "@/api/assets"
import { getOrCreateDefaultProject } from "@/api/projects"
import type { VoiceClone } from "@/api/voice-clone"
interface UseCloneSubmitOptions {
voiceName: string
voiceDescription: string
selectedFile: File | null
recordedBlob: Blob | null
setPhase: (phase: "input" | "uploading" | "cloning" | "done") => void
setErrorMessage: (msg: string) => void
validateForm: () => string | null
onSuccess?: (clone: VoiceClone) => void
onClose: () => void
}
/**
* 克隆提交流程 Hook
* 封装上传 + 克隆 + 完成的三阶段流程
*/
export function useCloneSubmit({
voiceName,
voiceDescription,
selectedFile,
recordedBlob,
setPhase,
setErrorMessage,
validateForm,
onSuccess,
onClose,
}: UseCloneSubmitOptions) {
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
/** 组件卸载时清理定时器 */
useEffect(() => {
return () => {
if (timerRef.current) clearTimeout(timerRef.current)
}
}, [])
const handleSubmit = useCallback(async () => {
const formError = validateForm()
if (formError) {
setErrorMessage(formError)
return
}
setErrorMessage("")
try {
// 阶段 1:上传音频
setPhase("uploading")
let fileToUpload: File
if (selectedFile) {
fileToUpload = selectedFile
} else {
fileToUpload = new File([recordedBlob!], `recorded-${Date.now()}.webm`, {
type: "audio/webm",
})
}
// 获取默认项目和素材库(后端 /upload 接口必填)
const project = await getOrCreateDefaultProject()
const library = await ensureDefaultLibrary({ project_id: project.id, kind: "voice" })
const formData = new FormData()
formData.append("file", fileToUpload)
formData.append("project_id", project.id)
formData.append("library_id", library.id)
const uploadResult = await uploadAsset(formData)
// 阶段 2:克隆
setPhase("cloning")
const result = await createVoiceClone({
name: voiceName.trim(),
description: voiceDescription.trim() || undefined,
audio_url: uploadResult.url,
})
// 阶段 3:完成
setPhase("done")
// 2秒后自动关闭
timerRef.current = setTimeout(() => {
onSuccess?.(toVoiceClone(result))
onClose()
}, 2000)
} catch (err) {
setPhase("input")
setErrorMessage(err instanceof Error ? err.message : "克隆失败,请重试")
}
}, [
validateForm,
selectedFile,
recordedBlob,
voiceName,
voiceDescription,
setPhase,
setErrorMessage,
onSuccess,
onClose,
])
return { handleSubmit }
}
+2 -2
View File
@@ -31,8 +31,8 @@ export const useLogin = () => {
const user = await authApi.getCurrentUser()
setAuth(user, data.access_token, refreshToken)
// 跳转到登录前页面或首页
const redirect = localStorage.getItem("login_redirect") || "/"
// 跳转到登录前页面或仪表盘(与 Login.tsx onFinish 保持一致)
const redirect = localStorage.getItem("login_redirect") || "/app/dashboard"
localStorage.removeItem("login_redirect")
navigate(redirect, { replace: true })
return data
@@ -0,0 +1,4 @@
export { useBatchDelete } from "./useBatchDelete"
export { useBatchTag } from "./useBatchTag"
export { useBatchClassify } from "./useBatchClassify"
export { useBatchMark } from "./useBatchMark"
@@ -0,0 +1,58 @@
import { useState, useCallback } from "react"
import { message } from "antd"
import { batchClassifyAssets, type BatchOperationResult } from "@/api/assets"
interface UseBatchClassifyOptions {
selectedIds: Set<string>
queryClient: ReturnType<typeof import("@tanstack/react-query").useQueryClient>
showResult: (result: BatchOperationResult, title: string, clear?: boolean) => void
}
export const useBatchClassify = ({
selectedIds,
queryClient,
showResult,
}: UseBatchClassifyOptions) => {
const [classifyModalOpen, setClassifyModalOpen] = useState(false)
const [batchCategory, setBatchCategory] = useState("")
const [batchLoading, setBatchLoading] = useState(false)
const handleBatchClassify = useCallback(async () => {
if (!batchCategory) {
message.warning("请选择分类")
return
}
const ids = Array.from(selectedIds)
setBatchLoading(true)
try {
const result = await batchClassifyAssets({
asset_ids: ids,
category: batchCategory,
})
queryClient.invalidateQueries({ queryKey: ["assets"] })
showResult(result, "批量改分类")
setClassifyModalOpen(false)
setBatchCategory("")
if (result.failure_count === 0) {
message.success(`成功将 ${result.success_count} 个素材改为「${batchCategory}`)
} else {
message.warning(
`改分类完成:成功 ${result.success_count} 个,失败 ${result.failure_count}`,
)
}
} catch {
message.error("批量改分类失败,请重试")
} finally {
setBatchLoading(false)
}
}, [batchCategory, selectedIds, queryClient, showResult])
return {
classifyModalOpen,
setClassifyModalOpen,
batchCategory,
setBatchCategory,
batchLoading,
handleBatchClassify,
}
}
@@ -0,0 +1,40 @@
import { useState, useCallback } from "react"
import { message } from "antd"
import { batchDeleteAssets, type BatchOperationResult } from "@/api/assets"
interface UseBatchDeleteOptions {
selectedIds: Set<string>
invalidateAssets: () => void
showResult: (result: BatchOperationResult, title: string, clear?: boolean) => void
}
export const useBatchDelete = ({
selectedIds,
invalidateAssets,
showResult,
}: UseBatchDeleteOptions) => {
const [batchLoading, setBatchLoading] = useState(false)
const handleBatchDelete = useCallback(async () => {
const ids = Array.from(selectedIds)
setBatchLoading(true)
try {
const result = await batchDeleteAssets(ids)
invalidateAssets()
showResult(result, "批量删除")
if (result.failure_count === 0) {
message.success(`成功删除 ${result.success_count} 个素材`)
} else {
message.warning(
`删除完成:成功 ${result.success_count} 个,失败 ${result.failure_count}`,
)
}
} catch {
message.error("批量删除失败,请重试")
} finally {
setBatchLoading(false)
}
}, [selectedIds, invalidateAssets, showResult])
return { batchLoading, handleBatchDelete }
}
@@ -0,0 +1,53 @@
import { useState, useCallback } from "react"
import { message } from "antd"
import { batchMarkAssets, type BatchOperationResult } from "@/api/assets"
import type { SmartViewType } from "../../../components/BatchMarkModal"
import { SMART_VIEW_LABELS } from "../constants"
interface UseBatchMarkOptions {
selectedIds: Set<string>
queryClient: ReturnType<typeof import("@tanstack/react-query").useQueryClient>
showResult: (result: BatchOperationResult, title: string, clear?: boolean) => void
}
export const useBatchMark = ({ selectedIds, queryClient, showResult }: UseBatchMarkOptions) => {
const [markModalOpen, setMarkModalOpen] = useState(false)
const [batchSmartView, setBatchSmartView] = useState<SmartViewType>("recommended")
const [batchLoading, setBatchLoading] = useState(false)
const handleBatchMark = useCallback(async () => {
const ids = Array.from(selectedIds)
setBatchLoading(true)
try {
const result = await batchMarkAssets({
asset_ids: ids,
smart_view: batchSmartView,
})
queryClient.invalidateQueries({ queryKey: ["assets"] })
showResult(result, "批量智能标记")
setMarkModalOpen(false)
if (result.failure_count === 0) {
message.success(
`成功将 ${result.success_count} 个素材标记为「${SMART_VIEW_LABELS[batchSmartView]}`,
)
} else {
message.warning(
`智能标记完成:成功 ${result.success_count} 个,失败 ${result.failure_count}`,
)
}
} catch {
message.error("批量智能标记失败,请重试")
} finally {
setBatchLoading(false)
}
}, [batchSmartView, selectedIds, queryClient, showResult])
return {
markModalOpen,
setMarkModalOpen,
batchSmartView,
setBatchSmartView,
batchLoading,
handleBatchMark,
}
}
@@ -0,0 +1,86 @@
import { useState, useCallback } from "react"
import { message } from "antd"
import { batchTagAssets, type BatchOperationResult } from "@/api/assets"
interface UseBatchTagOptions {
selectedIds: Set<string>
queryClient: ReturnType<typeof import("@tanstack/react-query").useQueryClient>
showResult: (result: BatchOperationResult, title: string, clear?: boolean) => void
}
export const useBatchTag = ({ selectedIds, queryClient, showResult }: UseBatchTagOptions) => {
const [tagModalOpen, setTagModalOpen] = useState(false)
const [batchTagInput, setBatchTagInput] = useState("")
const [batchTags, setBatchTags] = useState<string[]>([])
const [tagMode, setTagMode] = useState<"add" | "replace">("add")
const [batchLoading, setBatchLoading] = useState(false)
const handleBatchTag = useCallback(async () => {
if (batchTags.length === 0) {
message.warning("请至少输入一个标签")
return
}
const ids = Array.from(selectedIds)
setBatchLoading(true)
try {
const result = await batchTagAssets({
asset_ids: ids,
tags: batchTags,
mode: tagMode,
})
queryClient.invalidateQueries({ queryKey: ["assets"] })
showResult(result, "批量打标签")
setTagModalOpen(false)
setBatchTags([])
setBatchTagInput("")
setTagMode("add")
if (result.failure_count === 0) {
message.success(`成功为 ${result.success_count} 个素材打标签`)
} else {
message.warning(
`打标签完成:成功 ${result.success_count} 个,失败 ${result.failure_count}`,
)
}
} catch {
message.error("批量打标签失败,请重试")
} finally {
setBatchLoading(false)
}
}, [batchTags, selectedIds, tagMode, queryClient, showResult])
const handleTagInputKeyDown = useCallback(
(e: React.KeyboardEvent) => {
if (e.key === "Enter" && batchTagInput.trim()) {
e.preventDefault()
const tag = batchTagInput.trim()
if (!batchTags.includes(tag)) {
setBatchTags([...batchTags, tag])
}
setBatchTagInput("")
}
},
[batchTagInput, batchTags],
)
const removeBatchTag = useCallback(
(tag: string) => {
setBatchTags(batchTags.filter((t) => t !== tag))
},
[batchTags],
)
return {
tagModalOpen,
setTagModalOpen,
batchTagInput,
setBatchTagInput,
batchTags,
setBatchTags,
tagMode,
setTagMode,
batchLoading,
handleBatchTag,
handleTagInputKeyDown,
removeBatchTag,
}
}
@@ -0,0 +1,4 @@
export { useBatchDelete } from "./useBatchDelete"
export { useBatchTag } from "./useBatchTag"
export { useBatchClassify } from "./useBatchClassify"
export { useBatchMark } from "./useBatchMark"
@@ -0,0 +1,58 @@
import { useState, useCallback } from "react"
import { message } from "antd"
import { batchClassifyAssets, type BatchOperationResult } from "@/api/assets"
interface UseBatchClassifyOptions {
selectedIds: Set<string>
queryClient: ReturnType<typeof import("@tanstack/react-query").useQueryClient>
showResult: (result: BatchOperationResult, title: string, clear?: boolean) => void
}
export const useBatchClassify = ({
selectedIds,
queryClient,
showResult,
}: UseBatchClassifyOptions) => {
const [classifyModalOpen, setClassifyModalOpen] = useState(false)
const [batchCategory, setBatchCategory] = useState("")
const [batchLoading, setBatchLoading] = useState(false)
const handleBatchClassify = useCallback(async () => {
if (!batchCategory) {
message.warning("请选择分类")
return
}
const ids = Array.from(selectedIds)
setBatchLoading(true)
try {
const result = await batchClassifyAssets({
asset_ids: ids,
category: batchCategory,
})
queryClient.invalidateQueries({ queryKey: ["assets"] })
showResult(result, "批量改分类")
setClassifyModalOpen(false)
setBatchCategory("")
if (result.failure_count === 0) {
message.success(`成功将 ${result.success_count} 个素材改为「${batchCategory}`)
} else {
message.warning(
`改分类完成:成功 ${result.success_count} 个,失败 ${result.failure_count}`,
)
}
} catch {
message.error("批量改分类失败,请重试")
} finally {
setBatchLoading(false)
}
}, [batchCategory, selectedIds, queryClient, showResult])
return {
classifyModalOpen,
setClassifyModalOpen,
batchCategory,
setBatchCategory,
batchLoading,
handleBatchClassify,
}
}
@@ -0,0 +1,40 @@
import { useState, useCallback } from "react"
import { message } from "antd"
import { batchDeleteAssets, type BatchOperationResult } from "@/api/assets"
interface UseBatchDeleteOptions {
selectedIds: Set<string>
invalidateAssets: () => void
showResult: (result: BatchOperationResult, title: string, clear?: boolean) => void
}
export const useBatchDelete = ({
selectedIds,
invalidateAssets,
showResult,
}: UseBatchDeleteOptions) => {
const [batchLoading, setBatchLoading] = useState(false)
const handleBatchDelete = useCallback(async () => {
const ids = Array.from(selectedIds)
setBatchLoading(true)
try {
const result = await batchDeleteAssets(ids)
invalidateAssets()
showResult(result, "批量删除")
if (result.failure_count === 0) {
message.success(`成功删除 ${result.success_count} 个素材`)
} else {
message.warning(
`删除完成:成功 ${result.success_count} 个,失败 ${result.failure_count}`,
)
}
} catch {
message.error("批量删除失败,请重试")
} finally {
setBatchLoading(false)
}
}, [selectedIds, invalidateAssets, showResult])
return { batchLoading, handleBatchDelete }
}
@@ -0,0 +1,53 @@
import { useState, useCallback } from "react"
import { message } from "antd"
import { batchMarkAssets, type BatchOperationResult } from "@/api/assets"
import type { SmartViewType } from "../../../components/BatchMarkModal"
import { SMART_VIEW_LABELS } from "../constants"
interface UseBatchMarkOptions {
selectedIds: Set<string>
queryClient: ReturnType<typeof import("@tanstack/react-query").useQueryClient>
showResult: (result: BatchOperationResult, title: string, clear?: boolean) => void
}
export const useBatchMark = ({ selectedIds, queryClient, showResult }: UseBatchMarkOptions) => {
const [markModalOpen, setMarkModalOpen] = useState(false)
const [batchSmartView, setBatchSmartView] = useState<SmartViewType>("recommended")
const [batchLoading, setBatchLoading] = useState(false)
const handleBatchMark = useCallback(async () => {
const ids = Array.from(selectedIds)
setBatchLoading(true)
try {
const result = await batchMarkAssets({
asset_ids: ids,
smart_view: batchSmartView,
})
queryClient.invalidateQueries({ queryKey: ["assets"] })
showResult(result, "批量智能标记")
setMarkModalOpen(false)
if (result.failure_count === 0) {
message.success(
`成功将 ${result.success_count} 个素材标记为「${SMART_VIEW_LABELS[batchSmartView]}`,
)
} else {
message.warning(
`智能标记完成:成功 ${result.success_count} 个,失败 ${result.failure_count}`,
)
}
} catch {
message.error("批量智能标记失败,请重试")
} finally {
setBatchLoading(false)
}
}, [batchSmartView, selectedIds, queryClient, showResult])
return {
markModalOpen,
setMarkModalOpen,
batchSmartView,
setBatchSmartView,
batchLoading,
handleBatchMark,
}
}
@@ -0,0 +1,86 @@
import { useState, useCallback } from "react"
import { message } from "antd"
import { batchTagAssets, type BatchOperationResult } from "@/api/assets"
interface UseBatchTagOptions {
selectedIds: Set<string>
queryClient: ReturnType<typeof import("@tanstack/react-query").useQueryClient>
showResult: (result: BatchOperationResult, title: string, clear?: boolean) => void
}
export const useBatchTag = ({ selectedIds, queryClient, showResult }: UseBatchTagOptions) => {
const [tagModalOpen, setTagModalOpen] = useState(false)
const [batchTagInput, setBatchTagInput] = useState("")
const [batchTags, setBatchTags] = useState<string[]>([])
const [tagMode, setTagMode] = useState<"add" | "replace">("add")
const [batchLoading, setBatchLoading] = useState(false)
const handleBatchTag = useCallback(async () => {
if (batchTags.length === 0) {
message.warning("请至少输入一个标签")
return
}
const ids = Array.from(selectedIds)
setBatchLoading(true)
try {
const result = await batchTagAssets({
asset_ids: ids,
tags: batchTags,
mode: tagMode,
})
queryClient.invalidateQueries({ queryKey: ["assets"] })
showResult(result, "批量打标签")
setTagModalOpen(false)
setBatchTags([])
setBatchTagInput("")
setTagMode("add")
if (result.failure_count === 0) {
message.success(`成功为 ${result.success_count} 个素材打标签`)
} else {
message.warning(
`打标签完成:成功 ${result.success_count} 个,失败 ${result.failure_count}`,
)
}
} catch {
message.error("批量打标签失败,请重试")
} finally {
setBatchLoading(false)
}
}, [batchTags, selectedIds, tagMode, queryClient, showResult])
const handleTagInputKeyDown = useCallback(
(e: React.KeyboardEvent) => {
if (e.key === "Enter" && batchTagInput.trim()) {
e.preventDefault()
const tag = batchTagInput.trim()
if (!batchTags.includes(tag)) {
setBatchTags([...batchTags, tag])
}
setBatchTagInput("")
}
},
[batchTagInput, batchTags],
)
const removeBatchTag = useCallback(
(tag: string) => {
setBatchTags(batchTags.filter((t) => t !== tag))
},
[batchTags],
)
return {
tagModalOpen,
setTagModalOpen,
batchTagInput,
setBatchTagInput,
batchTags,
setBatchTags,
tagMode,
setTagMode,
batchLoading,
handleBatchTag,
handleTagInputKeyDown,
removeBatchTag,
}
}
@@ -1,238 +1,8 @@
import { useState, useCallback } from "react"
import { message } from "antd"
import {
batchDeleteAssets,
batchTagAssets,
batchClassifyAssets,
batchMarkAssets,
type BatchOperationResult,
} from "@/api/assets"
import type { SmartViewType } from "../../components/BatchMarkModal"
import { SMART_VIEW_LABELS } from "./constants"
/* ── 批量删除 ── */
interface UseBatchDeleteOptions {
selectedIds: Set<string>
invalidateAssets: () => void
showResult: (result: BatchOperationResult, title: string, clear?: boolean) => void
}
export const useBatchDelete = ({
selectedIds,
invalidateAssets,
showResult,
}: UseBatchDeleteOptions) => {
const [batchLoading, setBatchLoading] = useState(false)
const handleBatchDelete = useCallback(async () => {
const ids = Array.from(selectedIds)
setBatchLoading(true)
try {
const result = await batchDeleteAssets(ids)
invalidateAssets()
showResult(result, "批量删除")
if (result.failure_count === 0) {
message.success(`成功删除 ${result.success_count} 个素材`)
} else {
message.warning(
`删除完成:成功 ${result.success_count} 个,失败 ${result.failure_count}`,
)
}
} catch {
message.error("批量删除失败,请重试")
} finally {
setBatchLoading(false)
}
}, [selectedIds, invalidateAssets, showResult])
return { batchLoading, handleBatchDelete }
}
/* ── 批量打标签 ── */
interface UseBatchTagOptions {
selectedIds: Set<string>
queryClient: ReturnType<typeof import("@tanstack/react-query").useQueryClient>
showResult: (result: BatchOperationResult, title: string, clear?: boolean) => void
}
export const useBatchTag = ({ selectedIds, queryClient, showResult }: UseBatchTagOptions) => {
const [tagModalOpen, setTagModalOpen] = useState(false)
const [batchTagInput, setBatchTagInput] = useState("")
const [batchTags, setBatchTags] = useState<string[]>([])
const [tagMode, setTagMode] = useState<"add" | "replace">("add")
const [batchLoading, setBatchLoading] = useState(false)
const handleBatchTag = useCallback(async () => {
if (batchTags.length === 0) {
message.warning("请至少输入一个标签")
return
}
const ids = Array.from(selectedIds)
setBatchLoading(true)
try {
const result = await batchTagAssets({
asset_ids: ids,
tags: batchTags,
mode: tagMode,
})
queryClient.invalidateQueries({ queryKey: ["assets"] })
showResult(result, "批量打标签")
setTagModalOpen(false)
setBatchTags([])
setBatchTagInput("")
setTagMode("add")
if (result.failure_count === 0) {
message.success(`成功为 ${result.success_count} 个素材打标签`)
} else {
message.warning(
`打标签完成:成功 ${result.success_count} 个,失败 ${result.failure_count}`,
)
}
} catch {
message.error("批量打标签失败,请重试")
} finally {
setBatchLoading(false)
}
}, [batchTags, selectedIds, tagMode, queryClient, showResult])
const handleTagInputKeyDown = useCallback(
(e: React.KeyboardEvent) => {
if (e.key === "Enter" && batchTagInput.trim()) {
e.preventDefault()
const tag = batchTagInput.trim()
if (!batchTags.includes(tag)) {
setBatchTags([...batchTags, tag])
}
setBatchTagInput("")
}
},
[batchTagInput, batchTags],
)
const removeBatchTag = useCallback(
(tag: string) => {
setBatchTags(batchTags.filter((t) => t !== tag))
},
[batchTags],
)
return {
tagModalOpen,
setTagModalOpen,
batchTagInput,
setBatchTagInput,
batchTags,
setBatchTags,
tagMode,
setTagMode,
batchLoading,
handleBatchTag,
handleTagInputKeyDown,
removeBatchTag,
}
}
/* ── 批量改分类 ── */
interface UseBatchClassifyOptions {
selectedIds: Set<string>
queryClient: ReturnType<typeof import("@tanstack/react-query").useQueryClient>
showResult: (result: BatchOperationResult, title: string, clear?: boolean) => void
}
export const useBatchClassify = ({
selectedIds,
queryClient,
showResult,
}: UseBatchClassifyOptions) => {
const [classifyModalOpen, setClassifyModalOpen] = useState(false)
const [batchCategory, setBatchCategory] = useState("")
const [batchLoading, setBatchLoading] = useState(false)
const handleBatchClassify = useCallback(async () => {
if (!batchCategory) {
message.warning("请选择分类")
return
}
const ids = Array.from(selectedIds)
setBatchLoading(true)
try {
const result = await batchClassifyAssets({
asset_ids: ids,
category: batchCategory,
})
queryClient.invalidateQueries({ queryKey: ["assets"] })
showResult(result, "批量改分类")
setClassifyModalOpen(false)
setBatchCategory("")
if (result.failure_count === 0) {
message.success(`成功将 ${result.success_count} 个素材改为「${batchCategory}`)
} else {
message.warning(
`改分类完成:成功 ${result.success_count} 个,失败 ${result.failure_count}`,
)
}
} catch {
message.error("批量改分类失败,请重试")
} finally {
setBatchLoading(false)
}
}, [batchCategory, selectedIds, queryClient, showResult])
return {
classifyModalOpen,
setClassifyModalOpen,
batchCategory,
setBatchCategory,
batchLoading,
handleBatchClassify,
}
}
/* ── 批量智能标记 ── */
interface UseBatchMarkOptions {
selectedIds: Set<string>
queryClient: ReturnType<typeof import("@tanstack/react-query").useQueryClient>
showResult: (result: BatchOperationResult, title: string, clear?: boolean) => void
}
export const useBatchMark = ({ selectedIds, queryClient, showResult }: UseBatchMarkOptions) => {
const [markModalOpen, setMarkModalOpen] = useState(false)
const [batchSmartView, setBatchSmartView] = useState<SmartViewType>("recommended")
const [batchLoading, setBatchLoading] = useState(false)
const handleBatchMark = useCallback(async () => {
const ids = Array.from(selectedIds)
setBatchLoading(true)
try {
const result = await batchMarkAssets({
asset_ids: ids,
smart_view: batchSmartView,
})
queryClient.invalidateQueries({ queryKey: ["assets"] })
showResult(result, "批量智能标记")
setMarkModalOpen(false)
if (result.failure_count === 0) {
message.success(
`成功将 ${result.success_count} 个素材标记为「${SMART_VIEW_LABELS[batchSmartView]}`,
)
} else {
message.warning(
`智能标记完成:成功 ${result.success_count} 个,失败 ${result.failure_count}`,
)
}
} catch {
message.error("批量智能标记失败,请重试")
} finally {
setBatchLoading(false)
}
}, [batchSmartView, selectedIds, queryClient, showResult])
return {
markModalOpen,
setMarkModalOpen,
batchSmartView,
setBatchSmartView,
batchLoading,
handleBatchMark,
}
}
/**
* @deprecated 请从 ./batch/ 目录导入子模块
* 保持向后兼容,re-export 所有批量操作 Hook
*/
export { useBatchDelete } from "./batch/useBatchDelete"
export { useBatchTag } from "./batch/useBatchTag"
export { useBatchClassify } from "./batch/useBatchClassify"
export { useBatchMark } from "./batch/useBatchMark"
@@ -12,7 +12,7 @@ import {
useBatchTag,
useBatchClassify,
useBatchMark,
} from "./asset-operations/batchOperations"
} from "./asset-operations/batch-operations"
import type { BatchOperationResult } from "@/api/assets"
import type { SmartViewType } from "../components/BatchMarkModal"
+36 -184
View File
@@ -1,116 +1,35 @@
/**
* 查重上传页面 — V21 设计系统
* 左右分栏:拖拽上传区 + 格式说明
* 零 antd 依赖
*/
import React, { useState, useRef, useCallback } from "react"
import { useMutation } from "@tanstack/react-query"
import { Button, Card, Tag } from "@/components/ui"
import { uploadForDuplication } from "@/api/duplication"
import React from "react"
import { useNavigate } from "react-router-dom"
import "./duplication.css"
import { Card } from "@/components/ui"
import PageHead from "@/components/layout/PageHead"
/** 支持的视频格式 */
const ACCEPT_FORMATS = ".mp4,.avi,.mov,.mkv,.wmv,.flv,.webm"
const FORMAT_LIST = ["MP4", "AVI", "MOV", "MKV", "WMV", "FLV", "WebM"]
/** 最大文件大小:2GB */
const MAX_FILE_SIZE = 2 * 1024 * 1024 * 1024
/** 简易 toast */
interface ToastState {
message: string
type: "success" | "error" | "warning"
}
import UploadZone from "./duplication-upload/UploadZone"
import UploadActions from "./duplication-upload/UploadActions"
import UploadProgress from "./duplication-upload/UploadProgress"
import UploadResultPanel from "./duplication-upload/UploadResultPanel"
import InfoSidebar from "./duplication-upload/InfoSidebar"
import { useDuplicationUpload } from "./duplication-upload/useDuplicationUpload"
import { ACCEPT_FORMATS } from "./duplication-upload/constants"
import "./duplication.css"
const DuplicationUpload: React.FC = () => {
const navigate = useNavigate()
const fileInputRef = useRef<HTMLInputElement>(null)
const [dragging, setDragging] = useState(false)
const [uploading, setUploading] = useState(false)
const [uploadResult, setUploadResult] = useState<{
id: string
message: string
} | null>(null)
const [toast, setToast] = useState<ToastState | null>(null)
/** 显示 toast */
const showToast = useCallback((message: string, type: "success" | "error" | "warning") => {
setToast({ message, type })
setTimeout(() => setToast(null), 3000)
}, [])
// 上传查重 mutation
const uploadMutation = useMutation({
mutationFn: (file: File) => uploadForDuplication(file),
onSuccess: (data) => {
setUploading(false)
setUploadResult({ id: data.id, message: data.message })
showToast("查重任务已提交", "success")
},
onError: () => {
setUploading(false)
showToast("上传失败,请重试", "error")
},
})
/** 校验并上传文件 */
const handleFile = useCallback(
(file: File) => {
if (file.size > MAX_FILE_SIZE) {
showToast("文件大小不能超过 2GB", "error")
return
}
const ext = file.name.toLowerCase().split(".").pop()
const allowedExts = ACCEPT_FORMATS.replace(/\./g, "").split(",")
if (!allowedExts.includes(ext || "")) {
showToast(`不支持的文件格式,支持:${FORMAT_LIST.join("、")}`, "error")
return
}
setUploading(true)
setUploadResult(null)
uploadMutation.mutate(file)
},
[uploadMutation, showToast],
)
/** 拖拽事件 */
const handleDragOver = useCallback((e: React.DragEvent) => {
e.preventDefault()
setDragging(true)
}, [])
const handleDragLeave = useCallback(() => {
setDragging(false)
}, [])
const handleDrop = useCallback(
(e: React.DragEvent) => {
e.preventDefault()
setDragging(false)
const file = e.dataTransfer.files[0]
if (file) handleFile(file)
},
[handleFile],
)
/** 点击选择文件 */
const handleSelectFile = () => {
fileInputRef.current?.click()
}
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0]
if (file) handleFile(file)
// 重置 input 以便重复选择同一文件
e.target.value = ""
}
/** 重置状态 */
const handleReset = () => {
setUploadResult(null)
setUploading(false)
}
const {
fileInputRef,
dragging,
uploading,
uploadResult,
toast,
handleDragOver,
handleDragLeave,
handleDrop,
handleSelectFile,
handleFileChange,
handleReset,
} = useDuplicationUpload()
return (
<div className="dup-page">
@@ -125,25 +44,14 @@ const DuplicationUpload: React.FC = () => {
<div className="dup-upload-grid">
{/* 左侧:上传区域 */}
<Card>
{/* 拖拽上传区 */}
<div
className={`dup-upload-zone ${dragging ? "dragging" : ""} ${uploading ? "disabled" : ""}`}
<UploadZone
dragging={dragging}
uploading={uploading}
onDragOver={handleDragOver}
onDragLeave={handleDragLeave}
onDrop={handleDrop}
onClick={uploading ? undefined : handleSelectFile}
>
<div className="dup-upload-icon">{uploading ? "⏳" : "📁"}</div>
<h3>{uploading ? "正在上传并查重..." : "点击或拖拽视频文件到此区域"}</h3>
<p> MP4AVIMOVMKV 2GB</p>
<div className="dup-upload-formats">
{FORMAT_LIST.map((fmt) => (
<Tag key={fmt} variant="info">
{fmt}
</Tag>
))}
</div>
</div>
onClick={handleSelectFile}
/>
{/* 隐藏的文件 input */}
<input
@@ -154,77 +62,21 @@ const DuplicationUpload: React.FC = () => {
onChange={handleFileChange}
/>
{/* 上传按钮 */}
<div className="dup-upload-actions">
<Button
buttonType="primary"
buttonSize="md"
onClick={handleSelectFile}
disabled={uploading}
>
📂
</Button>
</div>
<UploadActions uploading={uploading} onSelectFile={handleSelectFile} />
{/* 上传进度 */}
{uploading && (
<div className="dup-progress">
<div className="dup-progress-circle">
<span className="dup-progress-icon"></span>
<span className="dup-progress-text">...</span>
</div>
<p>...</p>
</div>
)}
{uploading && <UploadProgress />}
{/* 上传结果 */}
{uploadResult && !uploading && (
<div className="dup-result">
<div className="dup-result-icon"></div>
<h3></h3>
<p>{uploadResult.message}</p>
<div className="dup-result-actions">
<Button
buttonType="primary"
buttonSize="md"
onClick={() => navigate("/app/duplication/results")}
>
</Button>
<Button buttonType="secondary" buttonSize="md" onClick={handleReset}>
</Button>
</div>
</div>
<UploadResultPanel
result={uploadResult}
onViewResult={() => navigate("/app/duplication/results")}
onReset={handleReset}
/>
)}
</Card>
{/* 右侧:格式说明 + 提示 */}
<div className="dup-info-card">
<h3>📋 </h3>
<ul className="dup-info-list">
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
<h3 style={{ marginTop: 24 }}>🎬 </h3>
<div className="dup-format-tags">
{FORMAT_LIST.map((fmt) => (
<Tag key={fmt} variant="info">
{fmt}
</Tag>
))}
</div>
<h3 style={{ marginTop: 24 }}>💡 </h3>
<ul className="dup-info-list">
<li> 2GB</li>
<li> 60 </li>
<li></li>
</ul>
</div>
<InfoSidebar />
</div>
</div>
)
+7 -1
View File
@@ -51,7 +51,13 @@ export const FILTER_OPTIONS: { key: RiskFilter; label: string }[] = [
{ key: "high", label: "高风险" },
]
/** Toast 类型 */
/** 支持的视频格式 */
export const ACCEPT_FORMATS = ".mp4,.avi,.mov,.mkv,.wmv,.flv,.webm"
export const FORMAT_LIST = ["MP4", "AVI", "MOV", "MKV", "WMV", "FLV", "WebM"]
/** 最大文件大小:2GB */
export const MAX_FILE_SIZE = 2 * 1024 * 1024 * 1024
/** 简易 toast */
export interface ToastState {
message: string
type: "success" | "error" | "warning"
@@ -0,0 +1,36 @@
import React from "react"
import { Tag } from "@/components/ui"
import { FORMAT_LIST } from "./constants"
/** 右侧说明卡 */
const InfoSidebar: React.FC = () => {
return (
<div className="dup-info-card">
<h3>📋 </h3>
<ul className="dup-info-list">
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
<h3 style={{ marginTop: 24 }}>🎬 </h3>
<div className="dup-format-tags">
{FORMAT_LIST.map((fmt) => (
<Tag key={fmt} variant="info">
{fmt}
</Tag>
))}
</div>
<h3 style={{ marginTop: 24 }}>💡 </h3>
<ul className="dup-info-list">
<li> 2GB</li>
<li> 60 </li>
<li></li>
</ul>
</div>
)
}
export default InfoSidebar
@@ -0,0 +1,20 @@
import React from "react"
import { Button } from "@/components/ui"
interface UploadActionsProps {
uploading: boolean
onSelectFile: () => void
}
/** 上传按钮区 */
const UploadActions: React.FC<UploadActionsProps> = ({ uploading, onSelectFile }) => {
return (
<div className="dup-upload-actions">
<Button buttonType="primary" buttonSize="md" onClick={onSelectFile} disabled={uploading}>
📂
</Button>
</div>
)
}
export default UploadActions
@@ -0,0 +1,16 @@
import React from "react"
/** 上传进度展示 */
const UploadProgress: React.FC = () => {
return (
<div className="dup-progress">
<div className="dup-progress-circle">
<span className="dup-progress-icon"></span>
<span className="dup-progress-text">...</span>
</div>
<p>...</p>
</div>
)
}
export default UploadProgress
@@ -0,0 +1,30 @@
import React from "react"
import { Button } from "@/components/ui"
import type { UploadResult } from "./constants"
interface UploadResultPanelProps {
result: UploadResult
onViewResult: () => void
onReset: () => void
}
/** 上传结果展示 */
const UploadResultPanel: React.FC<UploadResultPanelProps> = ({ result, onViewResult, onReset }) => {
return (
<div className="dup-result">
<div className="dup-result-icon"></div>
<h3></h3>
<p>{result.message}</p>
<div className="dup-result-actions">
<Button buttonType="primary" buttonSize="md" onClick={onViewResult}>
</Button>
<Button buttonType="secondary" buttonSize="md" onClick={onReset}>
</Button>
</div>
</div>
)
}
export default UploadResultPanel
@@ -0,0 +1,45 @@
import React from "react"
import { Tag } from "@/components/ui"
import { FORMAT_LIST } from "./constants"
interface UploadZoneProps {
dragging: boolean
uploading: boolean
onDragOver: (e: React.DragEvent) => void
onDragLeave: () => void
onDrop: (e: React.DragEvent) => void
onClick: () => void
}
/** 拖拽上传区 */
const UploadZone: React.FC<UploadZoneProps> = ({
dragging,
uploading,
onDragOver,
onDragLeave,
onDrop,
onClick,
}) => {
return (
<div
className={`dup-upload-zone ${dragging ? "dragging" : ""} ${uploading ? "disabled" : ""}`}
onDragOver={onDragOver}
onDragLeave={onDragLeave}
onDrop={onDrop}
onClick={uploading ? undefined : onClick}
>
<div className="dup-upload-icon">{uploading ? "⏳" : "📁"}</div>
<h3>{uploading ? "正在上传并查重..." : "点击或拖拽视频文件到此区域"}</h3>
<p> MP4AVIMOVMKV 2GB</p>
<div className="dup-upload-formats">
{FORMAT_LIST.map((fmt) => (
<Tag key={fmt} variant="info">
{fmt}
</Tag>
))}
</div>
</div>
)
}
export default UploadZone
@@ -0,0 +1,17 @@
/** 支持的视频格式 */
export const ACCEPT_FORMATS = ".mp4,.avi,.mov,.mkv,.wmv,.flv,.webm"
export const FORMAT_LIST = ["MP4", "AVI", "MOV", "MKV", "WMV", "FLV", "WebM"]
/** 最大文件大小:2GB */
export const MAX_FILE_SIZE = 2 * 1024 * 1024 * 1024
/** 简易 toast */
export interface ToastState {
message: string
type: "success" | "error" | "warning"
}
/** 上传结果 */
export interface UploadResult {
id: string
message: string
}
@@ -0,0 +1,115 @@
import { useState, useRef, useCallback } from "react"
import { useMutation } from "@tanstack/react-query"
import { uploadForDuplication } from "@/api/duplication"
import { ACCEPT_FORMATS, FORMAT_LIST, MAX_FILE_SIZE } from "./constants"
import type { ToastState, UploadResult } from "./constants"
/**
* 查重上传逻辑 Hook
* 封装文件校验、上传 mutation、toast 提示
*/
export function useDuplicationUpload() {
const fileInputRef = useRef<HTMLInputElement>(null)
const [dragging, setDragging] = useState(false)
const [uploading, setUploading] = useState(false)
const [uploadResult, setUploadResult] = useState<UploadResult | null>(null)
const [toast, setToast] = useState<ToastState | null>(null)
/** 显示 toast */
const showToast = useCallback((message: string, type: "success" | "error" | "warning") => {
setToast({ message, type })
setTimeout(() => setToast(null), 3000)
}, [])
// 上传查重 mutation
const uploadMutation = useMutation({
mutationFn: (file: File) => uploadForDuplication(file),
onSuccess: (data) => {
setUploading(false)
setUploadResult({ id: data.id, message: data.message })
showToast("查重任务已提交", "success")
},
onError: () => {
setUploading(false)
showToast("上传失败,请重试", "error")
},
})
/** 校验并上传文件 */
const handleFile = useCallback(
(file: File) => {
if (file.size > MAX_FILE_SIZE) {
showToast("文件大小不能超过 2GB", "error")
return
}
const ext = file.name.toLowerCase().split(".").pop()
const allowedExts = ACCEPT_FORMATS.replace(/\./g, "").split(",")
if (!allowedExts.includes(ext || "")) {
showToast(`不支持的文件格式,支持:${FORMAT_LIST.join("、")}`, "error")
return
}
setUploading(true)
setUploadResult(null)
uploadMutation.mutate(file)
},
[uploadMutation, showToast],
)
/** 拖拽事件 */
const handleDragOver = useCallback((e: React.DragEvent) => {
e.preventDefault()
setDragging(true)
}, [])
const handleDragLeave = useCallback(() => {
setDragging(false)
}, [])
const handleDrop = useCallback(
(e: React.DragEvent) => {
e.preventDefault()
setDragging(false)
const file = e.dataTransfer.files[0]
if (file) handleFile(file)
},
[handleFile],
)
/** 点击选择文件 */
const handleSelectFile = useCallback(() => {
fileInputRef.current?.click()
}, [])
const handleFileChange = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0]
if (file) handleFile(file)
// 重置 input 以便重复选择同一文件
e.target.value = ""
},
[handleFile],
)
/** 重置状态 */
const handleReset = useCallback(() => {
setUploadResult(null)
setUploading(false)
}, [])
return {
// refs
fileInputRef,
// 状态
dragging,
uploading,
uploadResult,
toast,
// 事件
handleDragOver,
handleDragLeave,
handleDrop,
handleSelectFile,
handleFileChange,
handleReset,
}
}
@@ -3531,52 +3531,143 @@
color: var(--text-tertiary, #9ca3af);
}
.tts-voice-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 8px;
.tts-voice-tabs {
display: flex;
gap: 4px;
background: var(--bg-secondary, #f3f4f6);
border-radius: var(--radius-md, 8px);
padding: 3px;
}
.tts-voice-card {
.tts-voice-tab {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
padding: 10px 6px;
border: 1px solid var(--border-primary, #e5e7eb);
border-radius: var(--radius-md, 8px);
background: var(--bg-surface, #fff);
justify-content: center;
gap: 6px;
padding: 6px 12px;
border: none;
background: transparent;
border-radius: 6px;
font-size: 12px;
font-weight: 500;
color: var(--text-secondary, #6b7280);
cursor: pointer;
transition: all 0.2s ease;
font-size: 12px;
}
.tts-voice-card:hover:not(:disabled) {
border-color: var(--primary-300, #93c5fd);
color: var(--primary-600, #2563eb);
background: var(--primary-50, #eff6ff);
.tts-voice-tab:hover {
color: var(--text-primary, #1f2937);
}
.tts-voice-card.active {
border-color: var(--primary-500, #3b82f6);
.tts-voice-tab.active {
background: var(--bg-surface, #fff);
color: var(--primary-600, #2563eb);
background: var(--primary-50, #eff6ff);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
}
.tts-voice-tab-count {
font-size: 10px;
padding: 1px 6px;
background: var(--primary-100, #dbeafe);
color: var(--primary-600, #2563eb);
border-radius: 10px;
font-weight: 600;
}
.tts-voice-card:disabled {
opacity: 0.5;
cursor: not-allowed;
.tts-voice-list {
display: flex;
flex-direction: column;
gap: 6px;
max-height: 200px;
overflow-y: auto;
}
.tts-voice-card-icon {
font-size: 22px;
.tts-voice-item {
display: flex;
align-items: center;
gap: 10px;
padding: 8px 10px;
border: 1px solid var(--border-primary, #e5e7eb);
border-radius: var(--radius-md, 8px);
background: var(--bg-surface, #fff);
cursor: pointer;
transition: all 0.2s ease;
}
.tts-voice-card-name {
.tts-voice-item:hover {
border-color: var(--primary-300, #93c5fd);
background: var(--primary-50, #eff6ff);
}
.tts-voice-item.selected {
border-color: var(--primary-500, #3b82f6);
background: var(--primary-50, #eff6ff);
}
.tts-voice-item-icon {
font-size: 20px;
flex-shrink: 0;
width: 28px;
height: 28px;
display: flex;
align-items: center;
justify-content: center;
background: var(--bg-secondary, #f3f4f6);
border-radius: 50%;
}
.tts-voice-item-info {
flex: 1;
min-width: 0;
}
.tts-voice-item-name {
font-size: 13px;
font-weight: 500;
color: var(--text-primary, #1f2937);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.tts-voice-item-desc {
font-size: 11px;
color: var(--text-tertiary, #9ca3af);
margin-top: 2px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.tts-voice-item-play {
flex-shrink: 0;
width: 26px;
height: 26px;
display: flex;
align-items: center;
justify-content: center;
border: none;
background: var(--primary-500, #3b82f6);
color: #fff;
border-radius: 50%;
font-size: 11px;
cursor: pointer;
transition: all 0.2s ease;
}
.tts-voice-item-play:hover {
background: var(--primary-600, #2563eb);
transform: scale(1.08);
}
.tts-voice-empty {
padding: 20px;
text-align: center;
font-size: 12px;
color: var(--text-tertiary, #9ca3af);
background: var(--bg-secondary, #f3f4f6);
border-radius: var(--radius-md, 8px);
}
/* ── 滑块区域 ── */
+21 -130
View File
@@ -1,15 +1,11 @@
/**
* 绿幕抠像配置面板
* 5 种颜色预设 + 自定义颜色 + 相似度/边缘平滑/溢色抑制
*/
import React, { useCallback } from "react"
import { Drawer, Switch } from "antd"
import type { ChromaKeyConfig, ChromaKeyColorPreset } from "../types"
import {
DEFAULT_CHROMA_KEY_CONFIG,
CHROMA_KEY_PRESET_LABELS,
CHROMA_KEY_PRESET_COLORS,
} from "../types"
import { DEFAULT_CHROMA_KEY_CONFIG, CHROMA_KEY_PRESET_COLORS } from "../types"
import { GreenScreenPresets } from "./green-screen/GreenScreenPresets"
import { GreenScreenCustomColor } from "./green-screen/GreenScreenCustomColor"
import { GreenScreenSliders } from "./green-screen/GreenScreenSliders"
import { GreenScreenPreview } from "./green-screen/GreenScreenPreview"
interface GreenScreenPanelProps {
open: boolean
@@ -18,9 +14,6 @@ interface GreenScreenPanelProps {
onChange: (config: ChromaKeyConfig) => void
}
/** 预设列表 */
const PRESET_LIST: ChromaKeyColorPreset[] = ["green", "blue", "red", "pure_green", "soft_green"]
const GreenScreenPanel: React.FC<GreenScreenPanelProps> = ({ open, onClose, config, onChange }) => {
const update = useCallback(
(partial: Partial<ChromaKeyConfig>) => {
@@ -33,7 +26,6 @@ const GreenScreenPanel: React.FC<GreenScreenPanelProps> = ({ open, onClose, conf
onChange({ ...DEFAULT_CHROMA_KEY_CONFIG, enabled: config.enabled })
}, [config.enabled, onChange])
/** 选择颜色预设时同步更新 color 字段 */
const handlePresetSelect = useCallback(
(preset: ChromaKeyColorPreset) => {
update({
@@ -44,10 +36,9 @@ const GreenScreenPanel: React.FC<GreenScreenPanelProps> = ({ open, onClose, conf
[update],
)
/** 自定义颜色变化时清除预设标记 */
const handleColorChange = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
update({ color: e.target.value })
(color: string) => {
update({ color })
},
[update],
)
@@ -61,7 +52,6 @@ const GreenScreenPanel: React.FC<GreenScreenPanelProps> = ({ open, onClose, conf
onClose={onClose}
className="green-screen-panel-drawer"
>
{/* 顶部开关 */}
<div className="green-header">
<span className="green-header-label">绿</span>
<Switch
@@ -71,123 +61,24 @@ const GreenScreenPanel: React.FC<GreenScreenPanelProps> = ({ open, onClose, conf
/>
</div>
{/* 颜色预设 */}
<div className="green-section">
<div className="green-section-title"></div>
<div className="green-presets">
{PRESET_LIST.map((p) => (
<button
key={p}
className={`green-preset-btn${config.color_preset === p ? " active" : ""}`}
onClick={() => handlePresetSelect(p)}
>
<span
className="green-preset-dot"
style={{ background: CHROMA_KEY_PRESET_COLORS[p] }}
/>
<span className="green-preset-label">{CHROMA_KEY_PRESET_LABELS[p]}</span>
</button>
))}
</div>
</div>
<GreenScreenPresets
selectedPreset={config.color_preset}
onPresetSelect={handlePresetSelect}
/>
{/* 自定义颜色 */}
<div className="green-section">
<div className="green-section-title"></div>
<div className="green-color-row">
<input
type="color"
className="green-color-picker"
value={config.color}
onChange={handleColorChange}
/>
<input
type="text"
className="green-color-hex"
value={config.color}
onChange={handleColorChange}
placeholder="#00FF00"
/>
<div className="green-color-swatch" style={{ background: config.color }} />
</div>
</div>
<GreenScreenCustomColor color={config.color} onColorChange={handleColorChange} />
{/* 参数调节 */}
<div className="green-section">
<div className="green-section-title"></div>
<GreenScreenSliders
similarity={config.similarity}
blend={config.blend}
spill={config.spill}
onSimilarityChange={(v) => update({ similarity: v })}
onBlendChange={(v) => update({ blend: v })}
onSpillChange={(v) => update({ spill: v })}
/>
{/* 相似度 */}
<div className="green-slider-row">
<div className="green-slider-header">
<span className="green-slider-label"></span>
<span className="green-slider-value">{config.similarity}%</span>
</div>
<input
type="range"
className="green-slider"
min={0}
max={100}
value={config.similarity}
onChange={(e) => update({ similarity: Number(e.target.value) })}
/>
<div className="green-slider-desc">广</div>
</div>
<GreenScreenPreview color={config.color} blend={config.blend} />
{/* 边缘平滑 */}
<div className="green-slider-row">
<div className="green-slider-header">
<span className="green-slider-label"></span>
<span className="green-slider-value">{config.blend}%</span>
</div>
<input
type="range"
className="green-slider"
min={0}
max={100}
value={config.blend}
onChange={(e) => update({ blend: Number(e.target.value) })}
/>
<div className="green-slider-desc"></div>
</div>
{/* 溢色抑制 */}
<div className="green-slider-row">
<div className="green-slider-header">
<span className="green-slider-label"></span>
<span className="green-slider-value">{config.spill}%</span>
</div>
<input
type="range"
className="green-slider"
min={0}
max={100}
value={config.spill}
onChange={(e) => update({ spill: Number(e.target.value) })}
/>
<div className="green-slider-desc"></div>
</div>
</div>
{/* 预览 */}
<div className="green-section">
<div className="green-section-title"></div>
<div className="green-preview-box">
<div className="green-preview-bg" style={{ background: config.color, opacity: 0.3 }} />
<div className="green-preview-subject">
<div className="green-preview-circle" />
<div className="green-preview-text"></div>
</div>
<div
className="green-preview-edge"
style={{
borderColor: config.color,
filter: `blur(${config.blend / 10}px)`,
}}
/>
</div>
</div>
{/* 底部重置 */}
<div className="green-footer">
<button className="green-reset-btn" onClick={handleReset}>
@@ -0,0 +1,268 @@
import React from "react"
import type { PipLayer, PipGridPosition, PipAnimType, PipSlideDirection } from "../../../types"
import { GRID_POSITIONS, ANIM_OPTIONS, SLIDE_DIR_OPTIONS } from "../constants"
export interface LayerConfigProps {
layer: PipLayer
totalDuration: number
onUpdate: (id: string, partial: Partial<PipLayer>) => void
onGridClick: (pos: PipGridPosition) => void
onWidthChange: (val: number) => void
onHeightChange: (val: number) => void
}
export const LayerConfig: React.FC<LayerConfigProps> = ({
layer,
totalDuration,
onUpdate,
onGridClick,
onWidthChange,
onHeightChange,
}) => {
return (
<div className="pip-config-area">
{/* ── 素材类型 ── */}
<div className="pip-field">
<label className="pip-field-label"></label>
<div className="pip-type-btns">
<button
className={`pip-type-btn${layer.material_type === "image" ? " active" : ""}`}
onClick={() => onUpdate(layer.id, { material_type: "image" })}
>
🖼
</button>
<button
className={`pip-type-btn${layer.material_type === "video" ? " active" : ""}`}
onClick={() => onUpdate(layer.id, { material_type: "video" })}
>
🎬
</button>
</div>
</div>
{/* ── 素材 URL ── */}
<div className="pip-field">
<label className="pip-field-label">
{layer.material_type === "image" ? "图片" : "视频"} URL
</label>
<input
className="pip-input"
type="text"
placeholder={
layer.material_type === "image"
? "https://example.com/image.png"
: "https://example.com/video.mp4"
}
value={layer.material_url}
onChange={(e) => onUpdate(layer.id, { material_url: e.target.value })}
/>
</div>
{/* ── 位置:九宫格 + 坐标 ── */}
<div className="pip-field">
<label className="pip-field-label"></label>
<div style={{ display: "flex", gap: 16, alignItems: "flex-start" }}>
<div className="pip-grid">
{GRID_POSITIONS.map((pos) => (
<button
key={pos}
className={`pip-grid-btn${layer.grid_position === pos ? " active" : ""}`}
onClick={() => onGridClick(pos)}
>
<span className="pip-grid-dot" />
</button>
))}
</div>
<div className="pip-field-row" style={{ flex: 1 }}>
<div>
<label className="pip-field-label">X (%)</label>
<input
className="pip-number"
type="number"
min={0}
max={100}
value={layer.x}
onChange={(e) => {
const val = Number(e.target.value)
if (!isNaN(val) && val >= 0 && val <= 100) onUpdate(layer.id, { x: val })
}}
/>
</div>
<div>
<label className="pip-field-label">Y (%)</label>
<input
className="pip-number"
type="number"
min={0}
max={100}
value={layer.y}
onChange={(e) => {
const val = Number(e.target.value)
if (!isNaN(val) && val >= 0 && val <= 100) onUpdate(layer.id, { y: val })
}}
/>
</div>
</div>
</div>
</div>
{/* ── 尺寸 ── */}
<div className="pip-field">
<label className="pip-field-label"></label>
<div className="pip-slider-row">
<span style={{ fontSize: 12, color: "#999", width: 20 }}></span>
<input
className="pip-slider"
type="range"
min={10}
max={80}
value={layer.width}
onChange={(e) => {
const val = Number(e.target.value)
if (!isNaN(val)) onWidthChange(val)
}}
/>
<span className="pip-slider-value">{layer.width}%</span>
</div>
<div className="pip-slider-row" style={{ marginTop: 6 }}>
<span style={{ fontSize: 12, color: "#999", width: 20 }}></span>
<input
className="pip-slider"
type="range"
min={10}
max={80}
value={layer.height}
onChange={(e) => {
const val = Number(e.target.value)
if (!isNaN(val)) onHeightChange(val)
}}
/>
<span className="pip-slider-value">{layer.height}%</span>
</div>
<div
className="pip-lock-row"
style={{ marginTop: 6 }}
onClick={() => onUpdate(layer.id, { aspect_lock: !layer.aspect_lock })}
>
<span className="pip-lock-icon">{layer.aspect_lock ? "🔒" : "🔓"}</span>
<span>{layer.aspect_lock ? "已锁定比例" : "锁定宽高比"}</span>
</div>
</div>
{/* ── 圆角 ── */}
<div className="pip-field">
<label className="pip-field-label"></label>
<div className="pip-slider-row">
<input
className="pip-slider"
type="range"
min={0}
max={50}
value={layer.border_radius}
onChange={(e) => {
const val = Number(e.target.value)
if (!isNaN(val)) onUpdate(layer.id, { border_radius: val })
}}
/>
<span className="pip-slider-value">{layer.border_radius}%</span>
</div>
</div>
{/* ── 透明度 ── */}
<div className="pip-field">
<label className="pip-field-label"></label>
<div className="pip-slider-row">
<input
className="pip-slider"
type="range"
min={0}
max={100}
value={layer.opacity}
onChange={(e) => {
const val = Number(e.target.value)
if (!isNaN(val)) onUpdate(layer.id, { opacity: val })
}}
/>
<span className="pip-slider-value">{layer.opacity}%</span>
</div>
</div>
{/* ── 时间 ── */}
<div className="pip-field">
<label className="pip-field-label"></label>
<div className="pip-field-row">
<div>
<label className="pip-field-label"> (s)</label>
<input
className="pip-number"
type="number"
min={0}
max={totalDuration || 999}
step={0.1}
value={layer.start_time}
onChange={(e) => {
const val = Number(e.target.value)
if (!isNaN(val) && val >= 0 && val <= totalDuration)
onUpdate(layer.id, { start_time: val })
}}
/>
</div>
<div>
<label className="pip-field-label"> (s)</label>
<input
className="pip-number"
type="number"
min={0.1}
max={totalDuration || 999}
step={0.1}
value={layer.duration}
onChange={(e) => {
const val = Number(e.target.value)
if (!isNaN(val) && val > 0 && val <= totalDuration)
onUpdate(layer.id, { duration: val })
}}
/>
</div>
</div>
</div>
{/* ── 入场动画 ── */}
<div className="pip-field">
<label className="pip-field-label"></label>
<select
className="pip-select"
value={layer.animation}
onChange={(e) => onUpdate(layer.id, { animation: e.target.value as PipAnimType })}
>
{ANIM_OPTIONS.map((opt) => (
<option key={opt.value} value={opt.value}>
{opt.label}
</option>
))}
</select>
</div>
{/* 滑入方向(仅 slide_in 时显示) */}
{layer.animation === "slide_in" && (
<div className="pip-field">
<label className="pip-field-label"></label>
<select
className="pip-select"
value={layer.slide_direction}
onChange={(e) =>
onUpdate(layer.id, {
slide_direction: e.target.value as PipSlideDirection,
})
}
>
{SLIDE_DIR_OPTIONS.map((opt) => (
<option key={opt.value} value={opt.value}>
{opt.label}
</option>
))}
</select>
</div>
)}
</div>
)
}
@@ -0,0 +1,58 @@
import React from "react"
import type { PipLayer } from "../../../types"
import { LAYER_COLORS } from "../constants"
export interface LayerListProps {
layers: PipLayer[]
selectedId: string
onSelect: (id: string) => void
onDelete: (id: string) => void
}
export const LayerList: React.FC<LayerListProps> = ({ layers, selectedId, onSelect, onDelete }) => {
if (layers.length === 0) {
return (
<div className="pip-layer-list">
<div className="pip-layer-empty"></div>
</div>
)
}
return (
<div className="pip-layer-list">
{layers.map((layer, idx) => (
<div
key={layer.id}
className={`pip-layer-item${selectedId === layer.id ? " active" : ""}`}
onClick={() => onSelect(layer.id)}
>
{layer.thumbnail_url || layer.material_url ? (
<img
className="pip-layer-thumb"
src={layer.thumbnail_url || layer.material_url || undefined}
alt={layer.name}
/>
) : (
<div
className="pip-layer-thumb"
style={{
background: LAYER_COLORS[idx % LAYER_COLORS.length],
}}
/>
)}
<span className="pip-layer-name">{layer.name}</span>
<button
className="pip-layer-delete"
onClick={(e) => {
e.stopPropagation()
onDelete(layer.id)
}}
title="删除图层"
>
</button>
</div>
))}
</div>
)
}
@@ -0,0 +1,33 @@
import React from "react"
import type { PipLayer } from "../../../types"
import { LAYER_COLORS } from "../constants"
export interface PipPreviewProps {
layers: PipLayer[]
selectedId: string
}
export const PipPreview: React.FC<PipPreviewProps> = ({ layers, selectedId }) => {
return (
<div className="pip-preview-box">
{layers.map((layer, idx) => (
<div
key={layer.id}
className={`pip-preview-layer${selectedId === layer.id ? " selected" : ""}`}
style={{
left: `${layer.x}%`,
top: `${layer.y}%`,
width: `${layer.width}%`,
height: `${layer.height}%`,
background: LAYER_COLORS[idx % LAYER_COLORS.length],
opacity: layer.opacity / 100,
borderRadius: `${layer.border_radius}%`,
zIndex: layer.z_index,
}}
>
<span className="pip-preview-label">{layer.name}</span>
</div>
))}
</div>
)
}
@@ -0,0 +1,55 @@
import type { PipGridPosition, PipAnimType, PipSlideDirection } from "../../types"
/** 九宫格位置 → 百分比坐标映射 */
export const GRID_POSITION_MAP: Record<PipGridPosition, { x: number; y: number }> = {
top_left: { x: 5, y: 5 },
top_center: { x: 37.5, y: 5 },
top_right: { x: 70, y: 5 },
center_left: { x: 5, y: 37.5 },
center: { x: 37.5, y: 37.5 },
center_right: { x: 70, y: 37.5 },
bottom_left: { x: 5, y: 70 },
bottom_center: { x: 37.5, y: 70 },
bottom_right: { x: 70, y: 70 },
}
/** 九宫格位置选项 */
export const GRID_POSITIONS: PipGridPosition[] = [
"top_left",
"top_center",
"top_right",
"center_left",
"center",
"center_right",
"bottom_left",
"bottom_center",
"bottom_right",
]
/** 入场动画选项 */
export const ANIM_OPTIONS: { value: PipAnimType; label: string }[] = [
{ value: "none", label: "无" },
{ value: "fade_in", label: "淡入" },
{ value: "slide_in", label: "滑入" },
]
/** 滑入方向选项 */
export const SLIDE_DIR_OPTIONS: { value: PipSlideDirection; label: string }[] = [
{ value: "left", label: "← 左" },
{ value: "right", label: "→ 右" },
{ value: "up", label: "↑ 上" },
{ value: "down", label: "↓ 下" },
]
/** 预览图层颜色池 */
export const LAYER_COLORS = [
"rgba(22,119,255,0.5)",
"rgba(82,196,26,0.5)",
"rgba(250,173,20,0.5)",
"rgba(255,77,79,0.5)",
"rgba(114,46,209,0.5)",
"rgba(19,194,194,0.5)",
]
let layerIdCounter = 0
export const genLayerId = () => `pip_layer_${Date.now()}_${++layerIdCounter}`
@@ -0,0 +1,130 @@
import { useState, useCallback, useMemo } from "react"
import type { PipConfig, PipLayer, PipGridPosition } from "../../../types"
import { DEFAULT_PIP_LAYER, DEFAULT_PIP_CONFIG } from "../../../types"
import { GRID_POSITION_MAP, genLayerId } from "../constants"
const MIN_DIMENSION = 10
const MAX_DIMENSION = 80
interface UsePipConfigPanelOptions {
config: PipConfig
onChange: (config: PipConfig) => void
}
export const usePipConfigPanel = ({ config, onChange }: UsePipConfigPanelOptions) => {
const [selectedId, setSelectedId] = useState<string>("")
const selectedLayer = useMemo(
() => config.layers.find((l) => l.id === selectedId) ?? null,
[config.layers, selectedId],
)
/* ── 添加图层 ── */
const handleAddLayer = useCallback(() => {
const newLayer: PipLayer = {
...DEFAULT_PIP_LAYER,
id: genLayerId(),
name: `图层 ${config.layers.length + 1}`,
z_index: config.layers.length + 1,
}
onChange({
...config,
layers: [...config.layers, newLayer],
})
setSelectedId(newLayer.id)
}, [config, onChange])
/* ── 删除图层 ── */
const handleDeleteLayer = useCallback(
(id: string) => {
const newLayers = config.layers.filter((l) => l.id !== id)
onChange({ ...config, layers: newLayers })
if (selectedId === id) {
setSelectedId(newLayers.length > 0 ? newLayers[0].id : "")
}
},
[config, onChange, selectedId],
)
/* ── 更新图层 ── */
const updateLayer = useCallback(
(id: string, partial: Partial<PipLayer>) => {
onChange({
...config,
layers: config.layers.map((l) => (l.id === id ? { ...l, ...partial } : l)),
})
},
[config, onChange],
)
/* ── 切换启用 ── */
const handleEnableToggle = useCallback(
(checked: boolean) => {
onChange({ ...config, enabled: checked })
},
[config, onChange],
)
/* ── 重置 ── */
const handleReset = useCallback(() => {
onChange({ ...DEFAULT_PIP_CONFIG })
setSelectedId("")
}, [onChange])
/* ── 九宫格点击 ── */
const handleGridClick = useCallback(
(pos: PipGridPosition) => {
if (!selectedLayer) return
const coords = GRID_POSITION_MAP[pos]
updateLayer(selectedLayer.id, {
grid_position: pos,
x: coords.x,
y: coords.y,
})
},
[selectedLayer, updateLayer],
)
/* ── 宽高变化 ── */
const handleWidthChange = useCallback(
(val: number) => {
if (!selectedLayer) return
const partial: Partial<PipLayer> = { width: val }
if (selectedLayer.aspect_lock && selectedLayer.height > 0) {
const ratio = selectedLayer.width / selectedLayer.height
const newHeight = Math.round(val / ratio)
partial.height = Math.max(MIN_DIMENSION, Math.min(MAX_DIMENSION, newHeight))
}
updateLayer(selectedLayer.id, partial)
},
[selectedLayer, updateLayer],
)
const handleHeightChange = useCallback(
(val: number) => {
if (!selectedLayer) return
const partial: Partial<PipLayer> = { height: val }
if (selectedLayer.aspect_lock && selectedLayer.width > 0 && selectedLayer.height > 0) {
const ratio = selectedLayer.width / selectedLayer.height
const newWidth = Math.round(val * ratio)
partial.width = Math.max(MIN_DIMENSION, Math.min(MAX_DIMENSION, newWidth))
}
updateLayer(selectedLayer.id, partial)
},
[selectedLayer, updateLayer],
)
return {
selectedId,
setSelectedId,
selectedLayer,
handleAddLayer,
handleDeleteLayer,
updateLayer,
handleEnableToggle,
handleReset,
handleGridClick,
handleWidthChange,
handleHeightChange,
}
}
@@ -4,12 +4,14 @@
*/
import React from "react"
import { Drawer, Switch } from "antd"
import type { PipConfig } from "@/pages/editing-planner/types"
import LayerList from "./pip-config/LayerList"
import LayerConfig from "./pip-config/LayerConfig"
import { usePipLayers } from "@/pages/editing-planner/hooks/usePipLayers"
import type { PipConfig } from "../../types"
interface PipConfigPanelProps {
import { usePipConfigPanel } from "./hooks/usePipConfigPanel"
import { LayerList } from "./components/LayerList"
import { PipPreview } from "./components/PipPreview"
import { LayerConfig } from "./components/LayerConfig"
export interface PipConfigPanelProps {
open: boolean
onClose: () => void
config: PipConfig
@@ -36,7 +38,7 @@ const PipConfigPanel: React.FC<PipConfigPanelProps> = ({
handleGridClick,
handleWidthChange,
handleHeightChange,
} = usePipLayers({ config, onChange })
} = usePipConfigPanel({ config, onChange })
return (
<Drawer
@@ -50,7 +52,9 @@ const PipConfigPanel: React.FC<PipConfigPanelProps> = ({
{/* ═══ 顶部工具栏 ═══ */}
<div className="pip-toolbar">
<div className="pip-toolbar-left">
<span style={{ fontSize: 13, color: "#666" }}> {config.layers.length} </span>
<button className="pip-add-btn" onClick={handleAddLayer}>
+
</button>
</div>
<div className="pip-enable-switch">
<span></span>
@@ -60,22 +64,32 @@ const PipConfigPanel: React.FC<PipConfigPanelProps> = ({
{/* ═══ 主体:图层列表 + 配置区 ═══ */}
<div className="pip-body">
{/* 左侧图层列表 */}
<LayerList
layers={config.layers}
selectedId={selectedId}
onSelect={setSelectedId}
onAdd={handleAddLayer}
onDelete={handleDeleteLayer}
/>
<LayerConfig
layer={selectedLayer}
layers={config.layers}
totalDuration={totalDuration}
onUpdate={updateLayer}
onGridClick={handleGridClick}
onWidthChange={handleWidthChange}
onHeightChange={handleHeightChange}
/>
{/* 右侧配置区 */}
{!selectedLayer ? (
<div className="pip-config-area">
<div className="pip-config-empty"></div>
</div>
) : (
<>
<PipPreview layers={config.layers} selectedId={selectedId} />
<LayerConfig
layer={selectedLayer}
totalDuration={totalDuration}
onUpdate={updateLayer}
onGridClick={handleGridClick}
onWidthChange={handleWidthChange}
onHeightChange={handleHeightChange}
/>
</>
)}
</div>
{/* ── 底部操作 ── */}
@@ -0,0 +1,57 @@
import React from "react"
import type { StickerItem } from "../../../types"
export interface StickerListProps {
items: StickerItem[]
selectedId: string | null
onSelect: (id: string) => void
onRemove: (id: string) => void
}
export const StickerList: React.FC<StickerListProps> = ({
items,
selectedId,
onSelect,
onRemove,
}) => {
if (items.length === 0) return null
const getStickerDisplay = (item: StickerItem) => {
if (item.type === "emoji") return item.content
if (item.type === "text") return item.content.slice(0, 10)
return "🖼"
}
const getStickerName = (item: StickerItem) => {
if (item.type === "text") return item.content.slice(0, 10)
if (item.type === "emoji") return "表情贴纸"
return "图片贴纸"
}
return (
<div className="sticker-list-section">
<div className="sticker-section-title"> ({items.length})</div>
<div className="sticker-list">
{items.map((item) => (
<div
key={item.id}
className={`sticker-list-item${selectedId === item.id ? " active" : ""}`}
onClick={() => onSelect(item.id)}
>
<span className="sticker-list-icon">{getStickerDisplay(item)}</span>
<span className="sticker-list-name">{getStickerName(item)}</span>
<button
className="sticker-list-delete"
onClick={(e) => {
e.stopPropagation()
onRemove(item.id)
}}
>
</button>
</div>
))}
</div>
</div>
)
}
@@ -0,0 +1,203 @@
import React from "react"
import type { StickerItem, TextStickerPreset } from "../../../types"
import { TEXT_STICKER_PRESET_LABELS } from "../../../types"
import { TEXT_PRESET_STYLES } from "../constants"
export interface StickerPropsEditorProps {
sticker: StickerItem
totalDuration: number
onChange: (id: string, partial: Partial<StickerItem>) => void
}
export const StickerPropsEditor: React.FC<StickerPropsEditorProps> = ({
sticker,
totalDuration,
onChange,
}) => {
const update = (partial: Partial<StickerItem>) => {
onChange(sticker.id, partial)
}
return (
<div className="sticker-props-section">
<div className="sticker-section-title"></div>
{/* 位置 X */}
<div className="sticker-prop-row">
<span className="sticker-prop-label"> X</span>
<input
type="range"
className="sticker-prop-slider"
min={0}
max={100}
value={sticker.x}
onChange={(e) => update({ x: Number(e.target.value) })}
/>
<span className="sticker-prop-value">{sticker.x}%</span>
</div>
{/* 位置 Y */}
<div className="sticker-prop-row">
<span className="sticker-prop-label"> Y</span>
<input
type="range"
className="sticker-prop-slider"
min={0}
max={100}
value={sticker.y}
onChange={(e) => update({ y: Number(e.target.value) })}
/>
<span className="sticker-prop-value">{sticker.y}%</span>
</div>
{/* 尺寸 */}
<div className="sticker-prop-row">
<span className="sticker-prop-label"></span>
<input
type="range"
className="sticker-prop-slider"
min={5}
max={50}
value={sticker.width}
onChange={(e) =>
update({
width: Number(e.target.value),
height: Number(e.target.value),
})
}
/>
<span className="sticker-prop-value">{sticker.width}%</span>
</div>
{/* 旋转 */}
<div className="sticker-prop-row">
<span className="sticker-prop-label"></span>
<input
type="range"
className="sticker-prop-slider"
min={-180}
max={180}
value={sticker.rotation}
onChange={(e) => update({ rotation: Number(e.target.value) })}
/>
<span className="sticker-prop-value">{sticker.rotation}°</span>
</div>
{/* 透明度 */}
<div className="sticker-prop-row">
<span className="sticker-prop-label"></span>
<input
type="range"
className="sticker-prop-slider"
min={0}
max={100}
value={sticker.opacity}
onChange={(e) => update({ opacity: Number(e.target.value) })}
/>
<span className="sticker-prop-value">{sticker.opacity}%</span>
</div>
{/* 时间 */}
<div className="sticker-prop-row">
<span className="sticker-prop-label"></span>
<input
type="number"
className="sticker-prop-number"
min={0}
max={totalDuration}
step={0.1}
value={sticker.start_time}
onChange={(e) => {
const val = Number(e.target.value)
if (!isNaN(val) && val >= 0 && val <= totalDuration) update({ start_time: val })
}}
/>
<span className="sticker-prop-label"></span>
<input
type="number"
className="sticker-prop-number"
min={0}
max={totalDuration}
step={0.1}
value={sticker.duration}
onChange={(e) => {
const val = Number(e.target.value)
if (!isNaN(val) && val > 0 && val <= totalDuration) update({ duration: val })
}}
/>
</div>
{/* 文字贴纸特有属性 */}
{sticker.type === "text" && (
<>
<div className="sticker-prop-row">
<span className="sticker-prop-label"></span>
<select
className="sticker-prop-select"
value={sticker.text_preset}
onChange={(e) => update({ text_preset: e.target.value as TextStickerPreset })}
>
{(Object.keys(TEXT_STICKER_PRESET_LABELS) as TextStickerPreset[]).map((p) => (
<option key={p} value={p}>
{TEXT_STICKER_PRESET_LABELS[p]}
</option>
))}
</select>
</div>
<div className="sticker-prop-row">
<span className="sticker-prop-label"></span>
<input
type="range"
className="sticker-prop-slider"
min={12}
max={72}
value={sticker.font_size}
onChange={(e) => update({ font_size: Number(e.target.value) })}
/>
<span className="sticker-prop-value">{sticker.font_size}px</span>
</div>
<div className="sticker-prop-row">
<span className="sticker-prop-label"></span>
<input
type="color"
className="sticker-prop-color"
value={sticker.text_color}
onChange={(e) => update({ text_color: e.target.value })}
/>
</div>
</>
)}
{/* 预览 */}
<div className="sticker-preview-box">
<div
className="sticker-preview-item"
style={{
left: `${sticker.x}%`,
top: `${sticker.y}%`,
width: `${sticker.width}%`,
height: `${sticker.width}%`,
transform: `translate(-50%, -50%) rotate(${sticker.rotation}deg)`,
opacity: sticker.opacity / 100,
fontSize: sticker.type === "text" ? `${sticker.font_size}px` : undefined,
...TEXT_PRESET_STYLES[sticker.text_preset],
}}
>
{sticker.type === "emoji" && sticker.content}
{sticker.type === "text" && sticker.content}
{sticker.type === "image" && (
<img
src={sticker.content}
alt="sticker"
style={{
width: "100%",
height: "100%",
objectFit: "contain",
}}
/>
)}
</div>
</div>
</div>
)
}
@@ -0,0 +1,136 @@
import React, { useState } from "react"
import type { StickerType, TextStickerPreset } from "../../../types"
import { TEXT_STICKER_PRESET_LABELS } from "../../../types"
import { EMOJI_LIST, TEXT_PRESET_STYLES } from "../constants"
export interface StickerTabsProps {
activeTab: StickerType
onTabChange: (tab: StickerType) => void
textInput: string
onTextInputChange: (val: string) => void
onAddEmoji: (emoji: string) => void
onAddImage: (url: string) => void
onAddText: (text: string) => void
}
export const StickerTabs: React.FC<StickerTabsProps> = ({
activeTab,
onTabChange,
textInput,
onTextInputChange,
onAddEmoji,
onAddImage,
onAddText,
}) => {
const [imageUrl, setImageUrl] = useState("")
const handleImageAdd = () => {
if (imageUrl.trim()) {
onAddImage(imageUrl.trim())
setImageUrl("")
}
}
const handleTextAdd = () => {
if (textInput.trim()) {
onAddText(textInput.trim())
}
}
return (
<>
{/* 类型 Tab */}
<div className="sticker-tabs">
{(["emoji", "image", "text"] as StickerType[]).map((t) => (
<button
key={t}
className={`sticker-tab${activeTab === t ? " active" : ""}`}
onClick={() => onTabChange(t)}
>
{t === "emoji" ? "表情贴纸" : t === "image" ? "图片贴纸" : "文字花字"}
</button>
))}
</div>
{/* Tab 内容区 */}
<div className="sticker-tab-content">
{/* Emoji 素材库 */}
{activeTab === "emoji" && (
<div className="sticker-emoji-grid">
{EMOJI_LIST.map((emoji) => (
<button key={emoji} className="sticker-emoji-btn" onClick={() => onAddEmoji(emoji)}>
{emoji}
</button>
))}
</div>
)}
{/* 图片贴纸 */}
{activeTab === "image" && (
<div className="sticker-image-input">
<input
type="text"
className="sticker-url-input"
placeholder="输入图片 URL 添加贴纸..."
value={imageUrl}
onChange={(e) => setImageUrl(e.target.value)}
onKeyDown={(e) => {
if (e.key === "Enter" && imageUrl.trim()) {
onAddImage(imageUrl.trim())
setImageUrl("")
}
}}
/>
<button className="sticker-url-add-btn" onClick={handleImageAdd}>
</button>
</div>
)}
{/* 文字花字 */}
{activeTab === "text" && (
<div className="sticker-text-section">
<div className="sticker-text-input-row">
<input
type="text"
className="sticker-text-input"
placeholder="输入文字内容..."
value={textInput}
onChange={(e) => onTextInputChange(e.target.value)}
/>
<button
className="sticker-text-add-btn"
disabled={!textInput.trim()}
onClick={handleTextAdd}
>
</button>
</div>
<div className="sticker-text-presets">
<div className="sticker-preset-title"></div>
<div className="sticker-preset-grid">
{(Object.keys(TEXT_STICKER_PRESET_LABELS) as TextStickerPreset[]).map((p) => (
<div
key={p}
className="sticker-preset-preview"
style={{
background:
p === "bubble"
? "rgba(0,0,0,0.5)"
: p === "gradient"
? "linear-gradient(90deg,#f093fb,#f5576c)"
: "#1a1a2e",
}}
>
<span style={TEXT_PRESET_STYLES[p]}></span>
<div className="sticker-preset-name">{TEXT_STICKER_PRESET_LABELS[p]}</div>
</div>
))}
</div>
</div>
</div>
)}
</div>
</>
)
}
@@ -0,0 +1,54 @@
import type { CSSProperties } from "react"
import type { TextStickerPreset } from "../../types"
/** 常用 emoji 素材 */
export const EMOJI_LIST = [
"😀",
"😂",
"🥰",
"😎",
"🤩",
"😱",
"🤔",
"😴",
"🥳",
"😍",
"❤️",
"🔥",
"⭐",
"✨",
"💯",
"👍",
"👏",
"🎉",
"🎵",
"💪",
"📌",
"💡",
"🎯",
"✅",
"❌",
"⬆️",
"⬇️",
"➡️",
"⭕",
"🔔",
]
/** 文字花字预设对应的 CSS 样式预览 */
export const TEXT_PRESET_STYLES: Record<TextStickerPreset, CSSProperties> = {
normal: { color: "#fff", textShadow: "none" },
highlight: { color: "#FFD700", textShadow: "0 0 8px rgba(255,215,0,0.6)" },
bubble: { color: "#fff", background: "rgba(0,0,0,0.5)", borderRadius: 8 },
neon: { color: "#0ff", textShadow: "0 0 6px #0ff, 0 0 12px #0ff" },
shadow: { color: "#fff", textShadow: "2px 2px 4px rgba(0,0,0,0.8)" },
outline: { color: "#fff", WebkitTextStroke: "1px #000" },
gradient: {
color: "transparent",
background: "linear-gradient(90deg,#f093fb,#f5576c)",
WebkitBackgroundClip: "text",
},
handwrite: { color: "#333", fontStyle: "italic", fontFamily: "cursive" },
}
export const genStickerId = () => `sticker_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`
@@ -0,0 +1,85 @@
import { useState, useCallback, useMemo } from "react"
import type { StickerConfig, StickerItem, StickerType } from "../../../types"
import { DEFAULT_STICKER_CONFIG, DEFAULT_STICKER_ITEM } from "../../../types"
import { genStickerId } from "../constants"
interface UseStickerPanelOptions {
config: StickerConfig
onChange: (config: StickerConfig) => void
totalDuration: number
}
export const useStickerPanel = ({ config, onChange, totalDuration }: UseStickerPanelOptions) => {
const [selectedId, setSelectedId] = useState<string | null>(null)
const [activeTab, setActiveTab] = useState<StickerType>("emoji")
const [textInput, setTextInput] = useState("")
const selectedSticker = useMemo(
() => config.items.find((s) => s.id === selectedId) ?? null,
[config.items, selectedId],
)
/** 更新单个贴纸 */
const updateItem = useCallback(
(id: string, partial: Partial<StickerItem>) => {
onChange({
...config,
items: config.items.map((s) => (s.id === id ? { ...s, ...partial } : s)),
})
},
[config, onChange],
)
/** 添加贴纸 */
const addSticker = useCallback(
(type: StickerType, content: string) => {
const newItem: StickerItem = {
...DEFAULT_STICKER_ITEM,
id: genStickerId(),
type,
content,
duration: totalDuration > 0 ? totalDuration : 5,
z_index: config.items.length > 0 ? Math.max(...config.items.map((i) => i.z_index)) + 1 : 1,
}
onChange({
...config,
enabled: true,
items: [...config.items, newItem],
})
setSelectedId(newItem.id)
},
[config, onChange, totalDuration],
)
/** 删除贴纸 */
const removeSticker = useCallback(
(id: string) => {
onChange({
...config,
items: config.items.filter((s) => s.id !== id),
})
if (selectedId === id) setSelectedId(null)
},
[config, onChange, selectedId],
)
/** 重置所有 */
const handleReset = useCallback(() => {
onChange({ ...DEFAULT_STICKER_CONFIG, enabled: config.enabled })
setSelectedId(null)
}, [config.enabled, onChange])
return {
selectedId,
setSelectedId,
selectedSticker,
activeTab,
setActiveTab,
textInput,
setTextInput,
updateItem,
addSticker,
removeSticker,
handleReset,
}
}
@@ -1,16 +1,13 @@
/**
*
* emoji / + +
*/
import React from "react"
import { Drawer, Switch } from "antd"
import type { StickerConfig } from "@/pages/editing-planner/types"
import StickerLibrary from "./sticker/StickerLibrary"
import StickerList from "./sticker/StickerList"
import StickerPropsEditor from "./sticker/StickerPropsEditor"
import { useStickerItems } from "@/pages/editing-planner/hooks/useStickerItems"
import type { StickerConfig } from "../../types"
interface StickerPanelProps {
import { useStickerPanel } from "./hooks/useStickerPanel"
import { StickerTabs } from "./components/StickerTabs"
import { StickerList } from "./components/StickerList"
import { StickerPropsEditor } from "./components/StickerPropsEditor"
export interface StickerPanelProps {
open: boolean
onClose: () => void
config: StickerConfig
@@ -28,14 +25,28 @@ const StickerPanel: React.FC<StickerPanelProps> = ({
const {
selectedId,
setSelectedId,
selectedSticker,
activeTab,
setActiveTab,
selectedSticker,
textInput,
setTextInput,
updateItem,
addSticker,
removeSticker,
handleReset,
} = useStickerItems({ config, onChange, totalDuration })
} = useStickerPanel({ config, onChange, totalDuration })
const handleEmojiAdd = (emoji: string) => {
addSticker("emoji", emoji)
}
const handleImageAdd = (url: string) => {
addSticker("image", url)
}
const handleTextAdd = (text: string) => {
addSticker("text", text)
setTextInput("")
}
return (
<Drawer
@@ -56,15 +67,23 @@ const StickerPanel: React.FC<StickerPanelProps> = ({
/>
</div>
{/* 素材库 + 类型Tab */}
<StickerLibrary activeTab={activeTab} onTabChange={setActiveTab} onAddSticker={addSticker} />
{/* Tab 选择器 + 内容 */}
<StickerTabs
activeTab={activeTab}
onTabChange={setActiveTab}
textInput={textInput}
onTextInputChange={setTextInput}
onAddEmoji={handleEmojiAdd}
onAddImage={handleImageAdd}
onAddText={handleTextAdd}
/>
{/* 已添加贴纸列表 */}
<StickerList
items={config.items}
selectedId={selectedId}
onSelect={setSelectedId}
onDelete={removeSticker}
onRemove={removeSticker}
/>
{/* 选中贴纸的属性编辑 */}
@@ -72,16 +91,9 @@ const StickerPanel: React.FC<StickerPanelProps> = ({
<StickerPropsEditor
sticker={selectedSticker}
totalDuration={totalDuration}
onUpdate={updateItem}
onChange={updateItem}
/>
)}
{/* 底部重置 */}
<div className="sticker-footer">
<button className="sticker-reset-btn" onClick={handleReset}>
</button>
</div>
</Drawer>
)
}
+14 -58
View File
@@ -1,18 +1,16 @@
/**
* 字幕样式配置面板 — Drawer 形式
* 字幕开关(手动 / ASR 自动识别)、字体大小、颜色、描边/阴影、位置、ASR 语言
*/
import React from "react"
import { Drawer, Slider, ColorPicker, Select } from "antd"
import type { Color } from "antd/es/color-picker"
import type { SubtitleStyleConfig } from "@/pages/editing-planner/types/subtitle"
import {
POSITION_OPTIONS,
FONT_OPTIONS,
ANIMATION_OPTIONS,
ASR_LANGUAGE_OPTIONS,
} from "@/pages/editing-planner/constants/subtitleStyle"
import SubtitlePreview from "./subtitle-style/SubtitlePreview"
import { SubtitleModeSwitch } from "./subtitle-style/SubtitleModeSwitch"
import { SubtitlePositionSelector } from "./subtitle-style/SubtitlePositionSelector"
import { SubtitleEffectButtons } from "./subtitle-style/SubtitleEffectButtons"
interface SubtitleStylePanelProps {
open: boolean
@@ -40,7 +38,6 @@ const SubtitleStylePanel: React.FC<SubtitleStylePanelProps> = ({
onClose={onClose}
className="subtitle-style-drawer"
>
{/* ── 字幕开关 ── */}
<div className="sub-field">
<div className="sub-toggle-row">
<span className="sub-label"></span>
@@ -55,26 +52,11 @@ const SubtitleStylePanel: React.FC<SubtitleStylePanelProps> = ({
{config.enabled && (
<>
{/* ── 模式切换 ── */}
<div className="sub-field">
<label className="sub-label"></label>
<div className="sub-mode-switch">
<button
className={`sub-mode-btn${config.mode === "manual" ? " active" : ""}`}
onClick={() => update({ mode: "manual" })}
>
</button>
<button
className={`sub-mode-btn${config.mode === "asr" ? " active" : ""}`}
onClick={() => update({ mode: "asr" })}
>
🤖 ASR
</button>
</div>
<SubtitleModeSwitch mode={config.mode} onModeChange={(mode) => update({ mode })} />
</div>
{/* ── ASR 语言(仅 ASR 模式) ── */}
{config.mode === "asr" && (
<div className="sub-field">
<label className="sub-label"></label>
@@ -88,7 +70,6 @@ const SubtitleStylePanel: React.FC<SubtitleStylePanelProps> = ({
</div>
)}
{/* ── 字体大小 ── */}
<div className="sub-field">
<label className="sub-label">
<span className="sub-value">{config.fontSize}px</span>
@@ -101,7 +82,6 @@ const SubtitleStylePanel: React.FC<SubtitleStylePanelProps> = ({
/>
</div>
{/* ── 字体颜色 ── */}
<div className="sub-field">
<label className="sub-label"></label>
<div className="sub-color-row">
@@ -113,7 +93,6 @@ const SubtitleStylePanel: React.FC<SubtitleStylePanelProps> = ({
</div>
</div>
{/* ── 字体 ── */}
<div className="sub-field">
<label className="sub-label"></label>
<Select
@@ -125,46 +104,24 @@ const SubtitleStylePanel: React.FC<SubtitleStylePanelProps> = ({
/>
</div>
{/* ── 字幕位置 ── */}
<div className="sub-field">
<label className="sub-label"></label>
<div className="sub-position-group">
{POSITION_OPTIONS.map((opt) => (
<button
key={opt.value}
className={`sub-position-btn${config.position === opt.value ? " active" : ""}`}
onClick={() =>
update({
position: opt.value as SubtitleStyleConfig["position"],
})
}
>
{opt.label}
</button>
))}
</div>
<SubtitlePositionSelector
position={config.position}
onPositionChange={(position) => update({ position })}
/>
</div>
{/* ── 描边 / 阴影 ── */}
<div className="sub-field">
<label className="sub-label"></label>
<div className="sub-effect-btns">
<button
className={`sub-effect-btn${config.stroke ? " active" : ""}`}
onClick={() => update({ stroke: !config.stroke })}
>
S
</button>
<button
className={`sub-effect-btn${config.shadow ? " active" : ""}`}
onClick={() => update({ shadow: !config.shadow })}
>
</button>
</div>
<SubtitleEffectButtons
stroke={config.stroke}
shadow={config.shadow}
onStrokeChange={(stroke) => update({ stroke })}
onShadowChange={(shadow) => update({ shadow })}
/>
</div>
{/* ── 动画 ── */}
<div className="sub-field">
<label className="sub-label"></label>
<Select
@@ -179,7 +136,6 @@ const SubtitleStylePanel: React.FC<SubtitleStylePanelProps> = ({
/>
</div>
{/* ── 预览 ── */}
<SubtitlePreview config={config} />
</>
)}
@@ -1,22 +1,32 @@
/**
* 水平轨道时间线 — 支持裁剪手柄、分割、右键菜单
* 时间标尺(20px) + 水平片段卡片轨道(100x100) + HTML5拖拽排序
* "+" 卡片 → 类型+时长选择器
* 时间标尺 + 片段轨道 + HTML5拖拽排序
*
* 裁剪交互
* - 鼠标悬停片段两端显示拖拽手柄,拖动调整入点/出点
* - 拖动时实时显示裁剪预览(入点/出点/时长)
* - 右键片段弹出菜单:分割 / 恢复原始长度 / 删除
* 子组件(timeline/
* - TimeRuler - 时间标尺
* - ClipCard - 片段卡片
* - ClipTrack - 片段轨道(播放头+片段列表+添加卡片)
* - TimelineHeader - 时间线头部(标题+缩放+操作按钮)
* - AddClipPicker - 添加片段选择器
* - TrimPreview - 裁剪预览 tooltip
* - ContextMenu - 右键菜单
*
* Hooks
* - useClipDrag - 片段拖拽排序
* - useTrimDrag - 裁剪拖拽
* - useTimelineMenus - 菜单 & 面板
* - usePlayheadDrag - 播放头拖拽
*/
import React, { useState, useRef, useCallback, useEffect } from "react"
import React from "react"
import type { ClipData, ClipType, TrimConfig } from "../types"
import { DEFAULT_PIXELS_PER_SECOND } from "../constants/timeline"
import { formatTime } from "../utils/timeline"
import { useClipDrag } from "../hooks/useClipDrag"
import { useTrimDrag } from "../hooks/useTrimDrag"
import { useTimelineMenus } from "../hooks/useTimelineMenus"
import { ClipCard } from "./timeline/ClipCard"
import { usePlayheadDrag } from "./timeline/hooks/usePlayheadDrag"
import { TimeRuler } from "./timeline/TimeRuler"
import { ClipTrack } from "./timeline/ClipTrack"
import { TimelineHeader } from "./timeline/TimelineHeader"
import { AddClipPicker } from "./timeline/AddClipPicker"
import { TrimPreview } from "./timeline/TrimPreview"
import { ContextMenu } from "./timeline/ContextMenu"
@@ -105,106 +115,32 @@ const TimelinePanel: React.FC<TimelinePanelProps> = ({
setHoveredClipId,
} = useTimelineMenus(clips, currentMode, onAddClip, onClipSplit, onClipResetTrim, onClipRemove)
/* ── 播放头拖拽状态 ── */
const [playheadDragging, setPlayheadDragging] = useState(false)
const trackRef = useRef<HTMLDivElement>(null)
/* ── 播放头拖拽 ── */
const { trackRef, handlePlayheadMouseDown, handleRulerClick } = usePlayheadDrag({
currentTime,
pps,
totalDuration,
onSeek,
})
/* ── 播放头拖拽全局 mousemove/mouseup ── */
useEffect(() => {
if (!playheadDragging) return
const handleMouseMove = (e: MouseEvent) => {
const trackEl = trackRef.current
if (!trackEl) return
const rect = trackEl.getBoundingClientRect()
const x = e.clientX - rect.left + trackEl.scrollLeft
const time = Math.max(0, Math.min(x / pps, totalDuration))
onSeek?.(Math.round(time * 10) / 10)
/* ── 撤销最后一个片段 ── */
const handleUndoClip = () => {
if (clips.length > 1) {
const last = clips[clips.length - 1]
onClipRemove(last.id)
}
const handleMouseUp = () => {
setPlayheadDragging(false)
}
document.addEventListener("mousemove", handleMouseMove)
document.addEventListener("mouseup", handleMouseUp)
return () => {
document.removeEventListener("mousemove", handleMouseMove)
document.removeEventListener("mouseup", handleMouseUp)
}
}, [playheadDragging, pps, totalDuration, onSeek])
/* ── 标尺点击跳转播放头 ── */
const handleRulerClick = useCallback(
(e: React.MouseEvent<HTMLDivElement>) => {
const rect = e.currentTarget.getBoundingClientRect()
const x = e.clientX - rect.left
const time = Math.max(0, Math.min(x / pps, totalDuration))
onSeek?.(Math.round(time * 10) / 10)
},
[pps, totalDuration, onSeek],
)
/* ── 播放头拖拽开始 ── */
const handlePlayheadMouseDown = useCallback((e: React.MouseEvent) => {
e.preventDefault()
e.stopPropagation()
setPlayheadDragging(true)
}, [])
}
return (
<div className="ep-timeline-area">
{/* 时间线头部 */}
<div className="ep-timeline-header">
<div className="ep-timeline-title">
<span>🎬 线</span>
<span className="ep-timeline-duration">: {formatTime(totalDuration)}</span>
</div>
<div className="ep-timeline-actions">
{/* 缩放控件 */}
<div className="ep-timeline-zoom">
<button
className="ep-zoom-btn"
onClick={() => onZoomChange?.(Math.max(10, pps - 10))}
title="缩小"
>
</button>
<input
type="range"
className="ep-zoom-slider"
min={10}
max={120}
step={5}
value={pps}
onChange={(e) => onZoomChange?.(Number(e.target.value))}
/>
<button
className="ep-zoom-btn"
onClick={() => onZoomChange?.(Math.min(120, pps + 10))}
title="放大"
>
+
</button>
<span className="ep-zoom-label">{pps}px/s</span>
</div>
<button
className="ep-timeline-action-btn"
onClick={() => {
if (clips.length > 1) {
const last = clips[clips.length - 1]
onClipRemove(last.id)
}
}}
title="删除最后一个片段"
>
</button>
<button className="ep-timeline-action-btn" title="重做">
</button>
</div>
</div>
<TimelineHeader
totalDuration={totalDuration}
pps={pps}
onZoomChange={onZoomChange}
onUndoClip={handleUndoClip}
clipsCount={clips.length}
/>
{/* 一镜到底提示 */}
{currentMode === "one_take" && <div className="ep-one-take-hint">🎥 </div>}
@@ -216,61 +152,33 @@ const TimelinePanel: React.FC<TimelinePanelProps> = ({
{/* 水平片段轨道 */}
{currentMode !== "one_take" && (
<div className="ep-clip-track" ref={trackRef} onDragOver={handleEmptyDragOver}>
{/* 播放头 */}
{currentMode !== "one_take" && totalDuration > 0 && (
<div
className="ep-playhead"
style={{ left: currentTime * pps }}
onMouseDown={handlePlayheadMouseDown}
>
<div className="ep-playhead-handle" />
</div>
)}
{clips.length === 0 ? (
<div className="ep-track-empty">
<div className="ep-track-empty-icon">🎬</div>
<div className="ep-track-empty-text"> + </div>
</div>
) : (
clips.map((clip, idx) => (
<ClipCard
key={clip.id}
clip={clip}
idx={idx}
isSelected={selectedClipId === clip.id}
isDragging={dragIdx === idx}
isDragOver={dragOverIdx === idx}
isHovered={hoveredClipId === clip.id}
pps={pps}
trimDragActive={!!trimDrag}
showTrimHandles={!!onClipTrim}
onDragStart={handleDragStart}
onDragOver={handleDragOver}
onDragEnd={handleDragEnd}
onDrop={handleDrop}
onSelect={onClipSelect}
onContextMenu={handleContextMenu}
onMouseEnter={() => setHoveredClipId(clip.id)}
onMouseLeave={() => setHoveredClipId(null)}
onTrimHandleMouseDown={handleTrimHandleMouseDown}
onRemove={onClipRemove}
/>
))
)}
{/* ── 轨道末尾 "+" 添加卡片 ── */}
<div className="ep-track-add-card-wrapper">
<div
ref={addCardRef}
className="ep-track-add-card"
onClick={handleTogglePicker}
title="添加片段"
>
+
</div>
</div>
</div>
<ClipTrack
clips={clips}
selectedClipId={selectedClipId}
pps={pps}
currentTime={currentTime}
totalDuration={totalDuration}
dragIdx={dragIdx}
dragOverIdx={dragOverIdx}
hoveredClipId={hoveredClipId}
trimDragActive={!!trimDrag}
showTrimHandles={!!onClipTrim}
trackRef={trackRef}
addCardRef={addCardRef}
onPlayheadMouseDown={handlePlayheadMouseDown}
onClipSelect={onClipSelect}
onDragStart={handleDragStart}
onDragOver={handleDragOver}
onDragEnd={handleDragEnd}
onDrop={handleDrop}
onEmptyDragOver={handleEmptyDragOver}
onContextMenu={handleContextMenu}
onClipMouseEnter={setHoveredClipId}
onClipMouseLeave={() => setHoveredClipId(null)}
onTrimHandleMouseDown={handleTrimHandleMouseDown}
onClipRemove={onClipRemove}
onTogglePicker={handleTogglePicker}
/>
)}
{/* 裁剪预览 tooltip */}
@@ -0,0 +1,48 @@
import React from "react"
import { Slider } from "antd"
export interface TtsSliderProps {
label: string
value: number
min: number
max: number
step: number
onChange: (val: number) => void
formatter: (val: number) => string
marks?: string[]
}
export const TtsSlider: React.FC<TtsSliderProps> = ({
label,
value,
min,
max,
step,
onChange,
formatter,
marks,
}) => {
return (
<div className="tts-slider-section">
<div className="tts-slider-header">
<span className="tts-slider-label">{label}</span>
<span className="tts-slider-value">{formatter(value)}</span>
</div>
<Slider
min={min}
max={max}
step={step}
value={value}
onChange={onChange}
tooltip={{ formatter: (v) => (v != null ? formatter(v as number) : "") }}
/>
{marks && (
<div className="tts-slider-marks">
{marks.map((m, i) => (
<span key={i}>{m}</span>
))}
</div>
)}
</div>
)
}
@@ -0,0 +1,107 @@
import React, { useState } from "react"
import type { UnifiedVoiceItem } from "@/api/voices"
import { LoadingOutlined } from "@ant-design/icons"
export interface VoiceSelectorProps {
presetVoices: UnifiedVoiceItem[]
cloneVoices: UnifiedVoiceItem[]
selectedVoiceId: string
loading: boolean
playingVoiceId: string | null
loadingSampleVoiceId: string | null
onSelect: (voiceId: string) => void
onPlaySample: (voice: UnifiedVoiceItem) => void
}
type VoiceTab = "preset" | "clone"
const GENDER_ICON: Record<string, string> = {
male: "👨",
female: "👩",
young: "🧑",
}
export const VoiceSelector: React.FC<VoiceSelectorProps> = ({
presetVoices,
cloneVoices,
selectedVoiceId,
loading,
playingVoiceId,
loadingSampleVoiceId,
onSelect,
onPlaySample,
}) => {
const [activeTab, setActiveTab] = useState<VoiceTab>("preset")
const currentList = activeTab === "preset" ? presetVoices : cloneVoices
return (
<div className="tts-voice-section">
<div className="tts-voice-label">
{loading && <span className="tts-voice-loading">...</span>}
</div>
{/* Tab 切换 */}
<div className="tts-voice-tabs">
<button
className={`tts-voice-tab${activeTab === "preset" ? " active" : ""}`}
onClick={() => setActiveTab("preset")}
>
</button>
<button
className={`tts-voice-tab${activeTab === "clone" ? " active" : ""}`}
onClick={() => setActiveTab("clone")}
>
{cloneVoices.length > 0 && (
<span className="tts-voice-tab-count">{cloneVoices.length}</span>
)}
</button>
</div>
{/* 音色列表 */}
{loading ? (
<div className="tts-voice-empty"> ...</div>
) : currentList.length === 0 ? (
<div className="tts-voice-empty">
{activeTab === "preset" ? "暂无预设音色" : "暂无克隆音色,快去克隆一个吧"}
</div>
) : (
<div className="tts-voice-list">
{currentList.map((voice) => {
const isSelected = selectedVoiceId === voice.voice_id
const isPlaying = playingVoiceId === voice.voice_id
const isLoadingSample = loadingSampleVoiceId === voice.voice_id
const icon = GENDER_ICON[voice.gender] ?? "✨"
return (
<div
key={voice.voice_id}
className={`tts-voice-item${isSelected ? " selected" : ""}`}
onClick={() => onSelect(voice.voice_id)}
>
<div className="tts-voice-item-icon">{icon}</div>
<div className="tts-voice-item-info">
<div className="tts-voice-item-name">{voice.name}</div>
<div className="tts-voice-item-desc">{voice.description || voice.language}</div>
</div>
<button
className="tts-voice-item-play"
onClick={(e) => {
e.stopPropagation()
onPlaySample(voice)
}}
disabled={isLoadingSample}
title={isPlaying ? "暂停" : isLoadingSample ? "合成中" : "试听"}
>
{isLoadingSample ? <LoadingOutlined /> : isPlaying ? "⏸" : "▶"}
</button>
</div>
)
})}
</div>
)}
</div>
)
}
@@ -0,0 +1,18 @@
import type { TtsMode } from "../../types"
/** 音色卡片分类图标 */
export const VOICE_CATEGORY_MAP: Record<string, { icon: string; label: string }> = {
male: { icon: "👨", label: "男声" },
female: { icon: "👩", label: "女声" },
young: { icon: "🧑", label: "少年" },
service: { icon: "🎧", label: "客服" },
news: { icon: "📰", label: "新闻" },
emotion: { icon: "🎭", label: "情感" },
}
/** 配音模式选项 */
export const TTS_MODE_OPTIONS: { mode: TtsMode; icon: string; label: string }[] = [
{ mode: "none", icon: "🔇", label: "无配音" },
{ mode: "upload", icon: "📁", label: "上传配音" },
{ mode: "tts", icon: "🤖", label: "TTS 合成" },
]
@@ -0,0 +1,272 @@
import { useState, useCallback, useEffect, useRef, type ChangeEvent } from "react"
import { message } from "antd"
import type { TtsConfig, TtsMode } from "../../../types"
import { DEFAULT_TTS_CONFIG } from "../../../types"
import { previewTts } from "@/api/tts"
import { getVoiceClonePreview } from "@/api/voice-clone"
import { fetchVoices, type UnifiedVoiceItem } from "@/api/voices"
interface UseTtsPanelOptions {
open: boolean
config: TtsConfig
onChange: (config: TtsConfig) => void
onClose: () => void
}
export const useTtsPanel = ({ open, config, onChange, onClose }: UseTtsPanelOptions) => {
/* ── 音色列表(统一音色:预设 + 克隆) ── */
const [presetVoices, setPresetVoices] = useState<UnifiedVoiceItem[]>([])
const [cloneVoices, setCloneVoices] = useState<UnifiedVoiceItem[]>([])
const [voicesLoading, setVoicesLoading] = useState(false)
// 用 ref 保存最新 config 和 onChange,避免 useEffect 闭包陷阱
const configRef = useRef(config)
const onChangeRef = useRef(onChange)
configRef.current = config
onChangeRef.current = onChange
/* ── 试听状态 ── */
const [previewLoading, setPreviewLoading] = useState(false)
const audioRef = useRef<HTMLAudioElement | null>(null)
const mountedRef = useRef(true)
/* ── 加载音色列表 + 重置挂载状态 ── */
useEffect(() => {
if (!open) return
mountedRef.current = true
let active = true
setVoicesLoading(true)
fetchVoices({ status: "ready", limit: 100 })
.then((res) => {
if (!active) return
const presets = res.items.filter((v) => v.type === "preset")
const clones = res.items.filter((v) => v.type === "clone")
setPresetVoices(presets)
setCloneVoices(clones)
// 如果当前没有选中音色,默认选第一个预设(用 ref 读最新值,避免闭包旧值覆盖用户选择)
if (!configRef.current.voice_id && presets.length > 0) {
onChangeRef.current({
...configRef.current,
voice_id: presets[0].voice_id,
})
}
})
.catch(() => {
if (active) message.error("加载音色列表失败")
})
.finally(() => {
if (active) setVoicesLoading(false)
})
return () => {
active = false
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [open])
/* ── 组件卸载时清理音频资源 ── */
useEffect(() => {
return () => {
audioRef.current?.pause()
audioRef.current = null
mountedRef.current = false
}
}, [])
/* ── 切换配音模式 ── */
const handleModeChange = useCallback(
(mode: TtsMode) => {
onChange({ ...config, mode })
},
[config, onChange],
)
/* ── 文本输入 ── */
const handleTextChange = useCallback(
(e: ChangeEvent<HTMLTextAreaElement>) => {
const text = e.target.value.slice(0, 5000)
onChange({ ...config, text })
},
[config, onChange],
)
/* ── 选择音色 ── */
const handleVoiceSelect = useCallback(
(voiceId: string) => {
onChange({ ...config, voice_id: voiceId })
},
[config, onChange],
)
/* ── 语速 ── */
const handleSpeedChange = useCallback(
(speed: number) => {
onChange({ ...config, speed })
},
[config, onChange],
)
/* ── 语调 ── */
const handlePitchChange = useCallback(
(pitch: number) => {
onChange({ ...config, pitch })
},
[config, onChange],
)
/* ── 音量 ── */
const handleVolumeChange = useCallback(
(volume: number) => {
onChange({ ...config, volume })
},
[config, onChange],
)
/* ── 字幕联动 ── */
const handleSubtitleSyncToggle = useCallback(() => {
onChange({ ...config, subtitle_sync: !config.subtitle_sync })
}, [config, onChange])
/* ── 试听 ── */
const handlePreview = useCallback(async () => {
if (!config.text.trim()) {
message.warning("请先输入合成文本")
return
}
if (!config.voice_id) {
message.warning("请先选择音色")
return
}
setPreviewLoading(true)
try {
const res = await previewTts({
text: config.text.slice(0, 200),
voice_id: config.voice_id,
speed: config.speed,
pitch: config.pitch,
})
if (!mountedRef.current) return
audioRef.current?.pause()
const audio = new Audio(res.audio_url)
audioRef.current = audio
audio
.play()
.then(() => {
if (mountedRef.current) message.success("试听播放中")
})
.catch(() => {
if (mountedRef.current) message.error("播放失败")
})
audio.onended = () => {
if (mountedRef.current) audioRef.current = null
}
} catch {
if (mountedRef.current) message.error("试听生成失败")
} finally {
if (mountedRef.current) setPreviewLoading(false)
}
}, [config])
/* ── 单音色示例试听 ── */
const [playingVoiceId, setPlayingVoiceId] = useState<string | null>(null)
const [loadingSampleVoiceId, setLoadingSampleVoiceId] = useState<string | null>(null)
const handlePlayVoiceSample = useCallback(
async (voice: UnifiedVoiceItem) => {
const voiceId = voice.voice_id
if (playingVoiceId === voiceId) {
audioRef.current?.pause()
audioRef.current = null
setPlayingVoiceId(null)
return
}
// 暂停上一个音频并清除事件监听,避免竞态
if (audioRef.current) {
audioRef.current.onended = null
audioRef.current.pause()
}
let audioUrl: string | null = voice.preview_url ?? voice.audio_url
// 克隆音色:调用试听接口实时合成
if (voice.type === "clone" && voice.voice_clone_profile_id) {
setLoadingSampleVoiceId(voiceId)
try {
const preview = await getVoiceClonePreview(voice.voice_clone_profile_id)
audioUrl = preview.audio_url
} catch {
if (mountedRef.current) {
message.error("试听音频生成失败,请重试")
}
setLoadingSampleVoiceId(null)
return
} finally {
if (mountedRef.current) {
setLoadingSampleVoiceId(null)
}
}
}
if (!audioUrl) {
message.warning("该音色暂无示例音频")
return
}
const audio = new Audio(audioUrl)
audioRef.current = audio
audio
.play()
.then(() => {
// 只有当前活跃的音频实例才能更新状态,防止快速切换竞态
if (mountedRef.current && audioRef.current === audio) {
setPlayingVoiceId(voiceId)
}
})
.catch(() => {
if (mountedRef.current && audioRef.current === audio) {
message.error("播放失败")
audioRef.current = null
}
})
audio.onended = () => {
if (mountedRef.current && audioRef.current === audio) {
setPlayingVoiceId(null)
audioRef.current = null
}
}
},
[playingVoiceId],
)
/* ── 重置 ── */
const handleReset = useCallback(() => {
onChange({ ...DEFAULT_TTS_CONFIG })
}, [onChange])
/* ── 关闭时停止音频 ── */
const handleClose = useCallback(() => {
mountedRef.current = false
audioRef.current?.pause()
audioRef.current = null
setPlayingVoiceId(null)
onClose()
}, [onClose])
return {
presetVoices,
cloneVoices,
voicesLoading,
previewLoading,
playingVoiceId,
loadingSampleVoiceId,
handleModeChange,
handleTextChange,
handleVoiceSelect,
handlePlayVoiceSample,
handleSpeedChange,
handlePitchChange,
handleVolumeChange,
handleSubtitleSyncToggle,
handlePreview,
handleReset,
handleClose,
}
}
@@ -1,16 +1,17 @@
/**
* TTS Drawer
* + + + // + +
* + + + + // + +
*/
import React from "react"
import { Drawer } from "antd"
import type { TtsConfig } from "@/pages/editing-planner/types"
import { TTS_MODE_OPTIONS } from "@/pages/editing-planner/constants/tts"
import VoiceSelector from "./tts/VoiceSelector"
import TtsSlider from "./tts/TtsSlider"
import { useTtsPanel } from "@/pages/editing-planner/hooks/useTtsPanel"
import type { TtsConfig } from "../../types"
interface TtsPanelProps {
import { useTtsPanel } from "./hooks/useTtsPanel"
import { VoiceSelector } from "./components/VoiceSelector"
import { TtsSlider } from "./components/TtsSlider"
import { TTS_MODE_OPTIONS } from "./constants"
export interface TtsPanelProps {
open: boolean
onClose: () => void
/** 当前片段 TTS 配置 */
@@ -20,12 +21,16 @@ interface TtsPanelProps {
const TtsPanel: React.FC<TtsPanelProps> = ({ open, onClose, config, onChange }) => {
const {
voices,
presetVoices,
cloneVoices,
voicesLoading,
previewLoading,
playingVoiceId,
loadingSampleVoiceId,
handleModeChange,
handleTextChange,
handleVoiceSelect,
handlePlayVoiceSample,
handleSpeedChange,
handlePitchChange,
handleVolumeChange,
@@ -74,7 +79,7 @@ const TtsPanel: React.FC<TtsPanelProps> = ({ open, onClose, config, onChange })
className="tts-text-input"
placeholder="请输入需要合成的文本内容..."
value={config.text}
onChange={(e) => handleTextChange(e.target.value)}
onChange={handleTextChange}
maxLength={5000}
rows={5}
/>
@@ -82,48 +87,49 @@ const TtsPanel: React.FC<TtsPanelProps> = ({ open, onClose, config, onChange })
{/* 音色选择 */}
<VoiceSelector
voices={voices}
voicesLoading={voicesLoading}
presetVoices={presetVoices}
cloneVoices={cloneVoices}
selectedVoiceId={config.voice_id}
onVoiceSelect={handleVoiceSelect}
loading={voicesLoading}
playingVoiceId={playingVoiceId}
loadingSampleVoiceId={loadingSampleVoiceId}
onSelect={handleVoiceSelect}
onPlaySample={handlePlayVoiceSample}
/>
{/* 语速滑块 */}
{/* 语速 */}
<TtsSlider
label="语速"
value={config.speed}
min={0.5}
max={2.0}
step={0.05}
unit="x"
onChange={handleSpeedChange}
formatter={(v) => `${v.toFixed(2)}x`}
marks={["0.5x", "1.0x", "2.0x"]}
tooltipFormatter={(v) => `${v.toFixed(2)}x`}
/>
{/* 语调滑块 */}
{/* 语调 */}
<TtsSlider
label="语调"
value={config.pitch}
min={-12}
max={12}
step={1}
unit="半音"
onChange={handlePitchChange}
formatter={(v) => `${v > 0 ? "+" : ""}${v} 半音`}
marks={["-12", "0", "+12"]}
tooltipFormatter={(v) => `${v}半音`}
/>
{/* 音量滑块 */}
{/* 音量 */}
<TtsSlider
label="音量"
value={config.volume}
min={0}
max={100}
step={1}
unit="%"
onChange={handleVolumeChange}
tooltipFormatter={(v) => `${v}%`}
formatter={(v) => `${v}%`}
/>
{/* 试听按钮 */}
@@ -0,0 +1,37 @@
import React from "react"
interface GreenScreenCustomColorProps {
color: string
onColorChange: (color: string) => void
}
export const GreenScreenCustomColor: React.FC<GreenScreenCustomColorProps> = ({
color,
onColorChange,
}) => {
const handleColorInput = (e: React.ChangeEvent<HTMLInputElement>) => {
onColorChange(e.target.value)
}
return (
<div className="green-section">
<div className="green-section-title"></div>
<div className="green-color-row">
<input
type="color"
className="green-color-picker"
value={color}
onChange={handleColorInput}
/>
<input
type="text"
className="green-color-hex"
value={color}
onChange={handleColorInput}
placeholder="#00FF00"
/>
<div className="green-color-swatch" style={{ background: color }} />
</div>
</div>
)
}
@@ -0,0 +1,36 @@
import React from "react"
import type { ChromaKeyColorPreset } from "../../types"
import { CHROMA_KEY_PRESET_LABELS, CHROMA_KEY_PRESET_COLORS } from "../../types"
interface GreenScreenPresetsProps {
selectedPreset: ChromaKeyColorPreset | null
onPresetSelect: (preset: ChromaKeyColorPreset) => void
}
const PRESET_LIST: ChromaKeyColorPreset[] = ["green", "blue", "red", "pure_green", "soft_green"]
export const GreenScreenPresets: React.FC<GreenScreenPresetsProps> = ({
selectedPreset,
onPresetSelect,
}) => {
return (
<div className="green-section">
<div className="green-section-title"></div>
<div className="green-presets">
{PRESET_LIST.map((p) => (
<button
key={p}
className={`green-preset-btn${selectedPreset === p ? " active" : ""}`}
onClick={() => onPresetSelect(p)}
>
<span
className="green-preset-dot"
style={{ background: CHROMA_KEY_PRESET_COLORS[p] }}
/>
<span className="green-preset-label">{CHROMA_KEY_PRESET_LABELS[p]}</span>
</button>
))}
</div>
</div>
)
}
@@ -0,0 +1,28 @@
import React from "react"
interface GreenScreenPreviewProps {
color: string
blend: number
}
export const GreenScreenPreview: React.FC<GreenScreenPreviewProps> = ({ color, blend }) => {
return (
<div className="green-section">
<div className="green-section-title"></div>
<div className="green-preview-box">
<div className="green-preview-bg" style={{ background: color, opacity: 0.3 }} />
<div className="green-preview-subject">
<div className="green-preview-circle" />
<div className="green-preview-text"></div>
</div>
<div
className="green-preview-edge"
style={{
borderColor: color,
filter: `blur(${blend / 10}px)`,
}}
/>
</div>
</div>
)
}
@@ -0,0 +1,68 @@
import React from "react"
interface SliderConfig {
label: string
value: number
description: string
onChange: (value: number) => void
}
interface GreenScreenSlidersProps {
similarity: number
blend: number
spill: number
onSimilarityChange: (value: number) => void
onBlendChange: (value: number) => void
onSpillChange: (value: number) => void
}
const SliderRow: React.FC<SliderConfig> = ({ label, value, description, onChange }) => (
<div className="green-slider-row">
<div className="green-slider-header">
<span className="green-slider-label">{label}</span>
<span className="green-slider-value">{value}%</span>
</div>
<input
type="range"
className="green-slider"
min={0}
max={100}
value={value}
onChange={(e) => onChange(Number(e.target.value))}
/>
<div className="green-slider-desc">{description}</div>
</div>
)
export const GreenScreenSliders: React.FC<GreenScreenSlidersProps> = ({
similarity,
blend,
spill,
onSimilarityChange,
onBlendChange,
onSpillChange,
}) => {
return (
<div className="green-section">
<div className="green-section-title"></div>
<SliderRow
label="相似度"
value={similarity}
description="越大容忍的色差范围越广"
onChange={onSimilarityChange}
/>
<SliderRow
label="边缘平滑"
value={blend}
description="越大边缘越柔和自然"
onChange={onBlendChange}
/>
<SliderRow
label="溢色抑制"
value={spill}
description="去除边缘颜色溢出"
onChange={onSpillChange}
/>
</div>
)
}

Some files were not shown because too many files have changed in this diff Show More