Compare commits

..

2 Commits

Author SHA1 Message Date
xiaoxia 8ce48c1b02 Merge branch 'develop' into refactor/tasks-api
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 39s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 1m7s
CI/CD Pipeline / Validate - Code Quality (pull_request) Failing after 1m55s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 57s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 42s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 38s
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 1m41s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 52s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 3m37s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 47s
AI Code Review / AI Code Review (pull_request) Successful in 2m5s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 15m23s
CI/CD Pipeline / CI Gate (pull_request) 失败: CI/CD Pipeline / Validate - Code Quality (pull_request) [frontend-only]
CI/CD Pipeline / 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 / Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 19s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Has been cancelled
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
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 18s
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
2026-07-27 12:34:55 +08:00
xiaoxia f31408baee refactor(api): 拆分 tasks.ts 为目录结构(types/tasks/index)
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 28s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 33s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 1m6s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 1m11s
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 29s
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 - Code Quality (pull_request) Failing after 2m26s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 1m13s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 47s
AI Code Review / AI Code Review (pull_request) Successful in 1m38s
Preview Deploy / Deploy Preview Environment (pull_request) Failing after 29s
CI/CD Pipeline / PR Build Web Image (pull_request) Failing after 2m25s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 5m45s
CI/CD Pipeline / 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 / Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 20s
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 / Production Browser E2E (pull_request) Has been skipped
将 123 行的 tasks.ts 拆分为目录化结构:
- types.ts: 类型定义(TaskItem/TaskStatus/创建请求响应等)
- tasks.ts: 全部 5 个 API 函数
- index.ts: 统一入口 re-export,保持 @/api/tasks 路径向后兼容
2026-07-26 17:12:41 +08:00
22 changed files with 132 additions and 80 deletions
+1
View File
@@ -12,6 +12,7 @@
from __future__ import annotations
import json
import logging
from typing import Any, Dict, List, Optional
+19
View File
@@ -0,0 +1,19 @@
/**
* 任务相关 API — 目录化入口
* 保持与原 tasks.ts 相同导出,向后兼容
*/
// 类型
export type {
TaskStatus,
TaskType,
TaskErrorInfo,
TaskItem,
TaskListParams,
TaskListResponse,
CreateGenerationTaskRequest,
CreateGenerationTaskResponse,
} from "./types"
// API 函数
export { createGenerationTask, getTasks, getUserTasks, getTask, retryTask } from "./tasks"
+46
View File
@@ -0,0 +1,46 @@
/**
* 任务相关 API 函数
* 对接后端任务中心 API
*/
import apiClient from "../client"
import type {
CreateGenerationTaskRequest,
CreateGenerationTaskResponse,
TaskItem,
TaskListParams,
TaskListResponse,
} from "./types"
/** 创建生成任务(智能剪辑) */
export const createGenerationTask = async (
params: CreateGenerationTaskRequest,
): Promise<CreateGenerationTaskResponse> => {
const { data } = await apiClient.post<CreateGenerationTaskResponse>("/generation/tasks", params)
return data
}
/** 获取任务列表(支持分页和筛选) */
export const getTasks = async (params?: TaskListParams): Promise<TaskListResponse> => {
const { data } = await apiClient.get<TaskListResponse>("/tasks", {
params,
})
return data
}
/** 获取当前用户的所有任务(兼容旧接口,跨 project) */
export const getUserTasks = async (): Promise<TaskItem[]> => {
const { data } = await apiClient.get("/tasks")
return data.items || data || []
}
/** 获取单个任务详情(含 error_info */
export const getTask = async (taskId: string): Promise<TaskItem> => {
const { data } = await apiClient.get(`/tasks/${taskId}`)
return data
}
/** 重试失败的任务 */
export const retryTask = async (taskId: string): Promise<TaskItem> => {
const { data } = await apiClient.post(`/tasks/${taskId}/retry`)
return data
}
@@ -1,14 +1,6 @@
/**
* API
* API
* - POST /api/v1/generation/tasks
* - GET /api/v1/tasks /
* - GET /api/v1/tasks/{task_id} error_info
* - POST /api/v1/tasks/{task_id}/retry
*
*/
import apiClient from "./client"
/* ──────────── 类型定义 ──────────── */
/** 任务状态 */
export type TaskStatus = "pending" | "waiting" | "running" | "completed" | "failed" | "cancelled"
@@ -85,39 +77,3 @@ export interface CreateGenerationTaskResponse {
result_count: number
error_message: string
}
/* ──────────── API 函数 ──────────── */
/** 创建生成任务(智能剪辑) */
export const createGenerationTask = async (
params: CreateGenerationTaskRequest,
): Promise<CreateGenerationTaskResponse> => {
const { data } = await apiClient.post<CreateGenerationTaskResponse>("/generation/tasks", params)
return data
}
/** 获取任务列表(支持分页和筛选) */
export const getTasks = async (params?: TaskListParams): Promise<TaskListResponse> => {
const { data } = await apiClient.get<TaskListResponse>("/tasks", {
params,
})
return data
}
/** 获取当前用户的所有任务(兼容旧接口,跨 project) */
export const getUserTasks = async (): Promise<TaskItem[]> => {
const { data } = await apiClient.get("/tasks")
return data.items || data || []
}
/** 获取单个任务详情(含 error_info */
export const getTask = async (taskId: string): Promise<TaskItem> => {
const { data } = await apiClient.get(`/tasks/${taskId}`)
return data
}
/** 重试失败的任务 */
export const retryTask = async (taskId: string): Promise<TaskItem> => {
const { data } = await apiClient.post(`/tasks/${taskId}/retry`)
return data
}
@@ -9,11 +9,12 @@
from __future__ import annotations
import logging
from typing import Optional
from packages.domain.chroma_key_config import CHROMA_KEY_PRESETS # noqa: F401
from packages.domain.chroma_key_config import apply_chroma_key_if_needed # noqa: F401
from packages.domain.chroma_key_config import (
CHROMA_KEY_PRESETS,
ChromaKeyConfig,
apply_chroma_key_if_needed,
)
from packages.domain.chroma_key_config import ( # noqa: F401 — 向后兼容
build_chromakey_filter as _build_chromakey_filter_base,
@@ -195,3 +195,17 @@ class ColorGradeEngine:
# ── 便捷函数 ──────────────────────────────────────────────────────────────────
def get_preset_names() -> list[tuple[str, str]]:
"""获取所有预设名称列表.
Returns:
[(preset_key, display_name), ...]
"""
return [(key, PRESET_DISPLAY_NAMES.get(key, key)) for key in PRESET_PARAMS.keys()]
def get_preset_params(preset: str) -> dict[str, float] | None:
"""获取指定预设的参数."""
return PRESET_PARAMS.get(preset)
+5 -3
View File
@@ -22,9 +22,11 @@ from shared.ffmpeg_utils import ( # noqa: F401
# xfade 转场纯逻辑已抽离到 domain 层,这里 re-export 保持向后兼容
from packages.domain.xfade_builder import DEFAULT_TRANSITION_DURATION as _default_transition_duration_base # noqa: F401
from packages.domain.xfade_builder import SUPPORTED_TRANSITIONS # noqa: F401
from packages.domain.xfade_builder import XFADE_TRANSITION_MAP # noqa: F401
from packages.domain.xfade_builder import XFade_TRANSITION_NAMES # noqa: F401
from packages.domain.xfade_builder import (
SUPPORTED_TRANSITIONS,
XFADE_TRANSITION_MAP,
XFade_TRANSITION_NAMES,
)
from packages.domain.xfade_builder import build_xfade_filter_chain as _build_xfade_filter_chain_base
from packages.domain.xfade_builder import chain_filters as _chain_filters_base
from packages.domain.xfade_builder import resolve_xfade_transition as _resolve_xfade_transition_base
@@ -9,21 +9,16 @@ from __future__ import annotations
import logging
# isort: off
from packages.domain.noise_reduction_config import (
NoiseReductionConfig,
NoiseReductionLevel, # noqa: F401
NoiseReductionLevel,
)
from packages.domain.noise_reduction_config import (
apply_noise_reduction_if_needed as _apply_noise_reduction_if_needed_base,
)
from packages.domain.noise_reduction_config import (
build_afftdn_filter as _build_afftdn_filter_base,
) # noqa: F401 — 向后兼容
from packages.domain.noise_reduction_config import build_afftdn_filter as _build_afftdn_filter_base # noqa: F401 — 向后兼容
from packages.domain.noise_reduction_config import build_arnndn_filter as _build_arnndn_filter_base
# isort: on
logger = logging.getLogger(__name__)
+10 -1
View File
@@ -18,13 +18,22 @@ from pathlib import Path
# 向后兼容:POSITION_BOTTOM_CENTER 也从 pip_config 再导出
from packages.domain.pip_config import POSITION_BOTTOM_CENTER # noqa: E402, F401
from packages.domain.pip_config import PiPConfig # noqa: F401
from packages.domain.pip_config import (
ANIMATION_FADE,
ANIMATION_SCALE,
ANIMATION_SLIDE_BOTTOM,
ANIMATION_SLIDE_LEFT,
ANIMATION_SLIDE_RIGHT,
ANIMATION_SLIDE_TOP,
POSITION_BOTTOM_LEFT,
POSITION_BOTTOM_RIGHT,
POSITION_CENTER,
POSITION_CENTER_LEFT,
POSITION_CENTER_RIGHT,
POSITION_TOP_CENTER,
POSITION_TOP_LEFT,
POSITION_TOP_RIGHT,
PiPConfig,
PiPLayerConfig,
)
from packages.domain.pip_config import ( # noqa: F401 — 向后兼容:保留模块级导出
@@ -13,6 +13,9 @@ from pathlib import Path
from typing import Any
from packages.domain.ass_subtitle_builder import (
TITLE_MARGIN_BOTTOM,
TITLE_MARGIN_SIDE,
TITLE_MARGIN_TOP,
build_ass_content,
)
from packages.domain.ass_subtitle_builder import build_ass_style as _build_ass_style_base # noqa: F401 — 向后兼容
@@ -14,20 +14,16 @@ import logging
from pathlib import Path
from typing import Any
# isort: off
from packages.domain.sticker_config import (
POSITION_PRESETS, # noqa: F401
STICKER_CATEGORIES, # noqa: F401
POSITION_PRESETS,
STICKER_CATEGORIES,
ImageStickerConfig,
StickerOverlayResult,
TextStickerConfig,
)
from packages.domain.sticker_config import (
get_sticker_categories as _get_sticker_categories_base,
) # noqa: F401 向后兼容导出
from packages.domain.sticker_config import get_sticker_categories as _get_sticker_categories_base # noqa: F401 向后兼容导出
from packages.domain.sticker_config import parse_stickers_from_config as _parse_stickers_base
from packages.domain.sticker_config import (
# isort: on
resolve_sticker_position,
)
@@ -25,22 +25,28 @@ from __future__ import annotations
import logging
from pathlib import Path
from typing import Any
from video_processing.path_security import PathSecurityError, is_in_allowed_dirs, safe_resolve_path
from packages.domain.subtitle_style import (
ALLOWED_SUBTITLE_EXTENSIONS,
DEFAULT_COLOR,
DEFAULT_FONT,
DEFAULT_FONT_SIZE,
DEFAULT_MAX_CHARS_PER_LINE,
DEFAULT_POSITION,
DEFAULT_STROKE_COLOR,
DEFAULT_STROKE_WIDTH,
POSITION_ALIASES,
POSITION_ALIGNMENT,
SubtitleSegment,
SubtitleStyle,
)
from packages.domain.subtitle_style import escape_ass_text as _escape_ass_text # noqa: F401 向后兼容导出
from packages.domain.subtitle_style import format_ass_time as _format_ass_time
from packages.domain.subtitle_style import hex_to_ass_bgr as _hex_to_ass_bgr # noqa: F401
from packages.domain.subtitle_style import hex_to_ass_color as _hex_to_ass_color # noqa: F401
from packages.domain.subtitle_style import opacity_to_ass_alpha as _opacity_to_ass_alpha # noqa: F401
from packages.domain.subtitle_style import hex_to_ass_bgr as _hex_to_ass_bgr
from packages.domain.subtitle_style import hex_to_ass_color as _hex_to_ass_color
from packages.domain.subtitle_style import opacity_to_ass_alpha as _opacity_to_ass_alpha
from packages.domain.subtitle_style import wrap_text as _wrap_text
logger = logging.getLogger(__name__)
+4 -2
View File
@@ -15,14 +15,16 @@ from __future__ import annotations
import logging
from typing import Any
from packages.domain.trim_config import MIN_TRIM_DURATION # noqa: F401
from packages.domain.trim_config import extract_trim_from_clip_config # noqa: F401
from packages.domain.trim_config import (
MIN_TRIM_DURATION,
TrimConfig,
TrimSegment,
)
from packages.domain.trim_config import build_audio_trim_filter as _build_audio_trim_filter # noqa: F401 — 向后兼容
from packages.domain.trim_config import build_video_trim_filter as _build_video_trim_filter
from packages.domain.trim_config import (
extract_trim_from_clip_config,
)
from packages.domain.trim_config import parse_segments_from_config as _parse_segments_from_config
from packages.domain.trim_config import resolve_segments as _resolve_segments
@@ -53,6 +53,7 @@ from video_processing.tts_engine import TtsEngine
from video_processing.watermark_engine import WatermarkConfig, WatermarkEngine
from packages.domain.render_layer_utils import LAYER_Z_INDEX as _IMPORTED_LAYER_Z_INDEX
from packages.domain.render_layer_utils import can_pass_through as _can_pass_through_pure
from packages.domain.render_layer_utils import clip_adjusted_duration as _clip_adjusted_duration_pure
from packages.domain.render_layer_utils import clip_effective_duration as _clip_effective_duration_pure
from packages.domain.render_layer_utils import clip_playback_speed as _clip_playback_speed_pure
@@ -14,9 +14,10 @@
from __future__ import annotations
import logging
from typing import Any
from packages.domain.watermark_config import WATERMARK_POSITIONS # noqa: F401
from packages.domain.watermark_config import (
WATERMARK_POSITIONS,
WatermarkConfig,
)
from packages.domain.watermark_config import ( # noqa: F401 — 向后兼容
+1 -1
View File
@@ -186,7 +186,7 @@ class PiPConfig:
"""最大 z_index."""
if not self.layers:
return 0
return max(layer.z_index for layer in self.layers)
return max(l.z_index for l in self.layers)
# ── 纯逻辑工具函数 ───────────────────────────────────────────────────────────
+1 -1
View File
@@ -6,7 +6,7 @@
from __future__ import annotations
from dataclasses import dataclass
from dataclasses import dataclass, field
from typing import Any
# ── 常量 ──────────────────────────────────────────────────────────────────────
+1 -1
View File
@@ -11,7 +11,7 @@
from __future__ import annotations
import logging
from dataclasses import dataclass
from dataclasses import dataclass, field
from typing import Any
logger = logging.getLogger(__name__)
+1 -1
View File
@@ -10,7 +10,7 @@
from __future__ import annotations
import logging
from dataclasses import dataclass
from dataclasses import dataclass, field
from typing import Any
logger = logging.getLogger(__name__)
+2 -2
View File
@@ -39,7 +39,7 @@ class TestConstants:
def test_preset_params_complete(self):
assert set(PRESET_PARAMS.keys()) == VALID_PRESETS
for _preset, params in PRESET_PARAMS.items():
for preset, params in PRESET_PARAMS.items():
assert set(params.keys()) == set(ALL_PARAM_KEYS)
def test_default_params_keys(self):
@@ -54,7 +54,7 @@ class TestConstants:
assert min_val <= DEFAULT_PARAMS[key] <= max_val
def test_all_presets_within_ranges(self):
for _preset, params in PRESET_PARAMS.items():
for preset, params in PRESET_PARAMS.items():
for key in ALL_PARAM_KEYS:
min_val, max_val = PARAM_RANGES[key]
assert min_val <= params[key] <= max_val, f"{preset}.{key}={params[key]} out of range"
+1 -1
View File
@@ -208,7 +208,7 @@ class TestPiPConfigFromDict:
}
)
assert cfg.layer_count == 3
assert [layer.source for layer in cfg.layers] == ["bottom", "mid", "top"]
assert [l.source for l in cfg.layers] == ["bottom", "mid", "top"]
def test_invalid_layer_skipped(self):
cfg = PiPConfig.from_dict(
+1 -1
View File
@@ -23,7 +23,7 @@ class TestConstants:
assert len(POSITION_PRESETS) == 9
def test_position_presets_normalized(self):
for _name, (x, y) in POSITION_PRESETS.items():
for name, (x, y) in POSITION_PRESETS.items():
assert 0.0 <= x <= 1.0
assert 0.0 <= y <= 1.0