主要变更: - 修复 /tasks/script 路由 404(去掉重复 prefix) - 开发模式自动认证兜底(无需登录即可测试流程) - Docker 基础设施独立化(共用 db/redis) - 前端 API 端口改为 8081 - 新增 TTS/语音克隆、视频粗剪、音频混音等智剪功能 - 删除智影专属模块(avatar、model_usage、qiniu 上传等)
33 KiB
美家卡智剪 (Meijiaka Smart Cut) - AI 视频剪辑桌面应用
项目概述
美家卡智剪是一个 AI 驱动的短视频剪辑桌面应用,采用 Tauri + React + FastAPI 混合架构。本项目由「美家卡智影」Fork 而来,从"数字人生成"转向"AI 辅助长视频剪辑"方向:用户导入长视频素材,AI 根据脚本自动分镜切割,配合语音克隆/TTS 生成配音,最终合成带字幕的成品短视频。
核心功能流程(6 步)
- 脚本生成 (Step 1) - AI 生成或粘贴口播文案,自动拆分为带预估时长的分镜
- 视频粗剪 (Step 2) - 导入长视频素材,按脚本时长自动切割为片段
- 语音配音 (Step 3) - AI 声音克隆(KlingAI)+ TTS 合成,为每段生成配音音频
- 字幕压制 (Step 4) - 基于音频自动对齐时间轴,ASS 字幕渲染并压制到视频
- 封面制作 (Step 5) - 提取视频首帧并叠加标题样式生成封面
- 视频合成 (Step 6) - FFmpeg 拼接视频片段,替换原声为 TTS 音频,导出成品
技术架构
- 前端桌面壳: Tauri 2.x + React 19 + TypeScript 5.8 + Vite 7
- 后端服务: FastAPI (Python 3.13+) + PostgreSQL 15 + Redis 7
- 任务调度: 自定义 Async Engine(基于 Redis 槽位管理),非 Celery
- 本地处理: 嵌入式 FFmpeg,Rust 层封装视频/音频处理命令
- AI 服务: 火山方舟(LLM/字幕)、可灵 AI(TTS/声音克隆)、OpenAI 兼容接口
项目结构
meijiaka-zj/
├── python-api/ # FastAPI 后端服务(AI 代理 + 认证 + 任务调度)
│ ├── app/
│ │ ├── api/v1/ # API 路由: auth, system, caption, voice
│ │ ├── ai/ # AI 模型路由、Provider、提示词模板
│ │ ├── core/ # 安全、配置加载、Token管理器、Redis客户端、异常处理
│ │ ├── crud/ # 数据访问层(users, avatar)
│ │ ├── db/ # 数据库配置(PostgreSQL + asyncpg + SQLAlchemy 2.0)
│ │ ├── models/ # SQLAlchemy 模型(当前仅 users)
│ │ ├── schemas/ # Pydantic 校验模型
│ │ ├── services/ # AI 服务代理、字幕/视频/TTS/声音克隆服务
│ │ ├── scheduler/ # Async Engine 异步任务调度
│ │ ├── config.py # Pydantic Settings 配置管理
│ │ └── main.py # FastAPI 入口(含生命周期管理)
│ ├── config/ # AI 模型配置文件(ai_models.yaml),支持热重载
│ ├── alembic/ # 数据库迁移(当前无迁移文件)
│ ├── tests/ # 测试目录(当前仅 test_kling_tts.py)
│ ├── pyproject.toml # Python 依赖和工具配置
│ ├── requirements.lock # uv 锁定依赖版本(如缺失需执行 make update-lock)
│ ├── Makefile # 常用命令封装
│ ├── docker-compose.yml
│ ├── Dockerfile
│ └── .env.example # 环境变量模板
│
├── tauri-app/ # Tauri 桌面应用(业务数据本地存储)
│ ├── src/ # React 前端源码
│ │ ├── api/
│ │ │ ├── adapters/ # 数据转换层(前后端字段映射)
│ │ │ ├── generated/ # OpenAPI 自动生成类型(只读)
│ │ │ ├── modules/ # API 模块封装(HTTP + IPC)
│ │ │ ├── client.ts # HTTP 客户端(自动 camelCase↔snake_case)
│ │ │ ├── types.ts # 手写核心类型
│ │ │ └── ipc.ts # Tauri IPC 调用封装
│ │ ├── components/ # 可复用组件
│ │ ├── pages/ # 页面组件
│ │ │ ├── VideoCreation/ # 6 步视频创作流程
│ │ │ ├── ContentManagement/
│ │ │ ├── Settings/
│ │ │ ├── Profile/
│ │ │ └── Login/
│ │ ├── store/ # Zustand 状态管理(+ Immer + persist)
│ │ ├── hooks/ # 自定义 React Hooks
│ │ ├── styles/ # 全局 CSS 变量、主题
│ │ └── utils/ # 工具函数
│ ├── src-tauri/ # Rust 后端源码
│ │ ├── src/
│ │ │ ├── lib.rs # Tauri 应用入口,命令注册
│ │ │ ├── ffmpeg_cmd.rs # FFmpeg 命令封装
│ │ │ ├── video_processing.rs # 视频合成业务逻辑
│ │ │ ├── storage/ # 本地存储引擎(原子写入、文件锁、路径净化)
│ │ │ ├── commands/ # IPC 命令按领域拆分
│ │ │ ├── api_proxy.rs # Python API 代理转发
│ │ │ └── utils.rs # 通用工具函数
│ │ ├── Cargo.toml
│ │ ├── tauri.conf.json
│ │ └── binaries/ # 嵌入式 FFmpeg
│ ├── package.json
│ ├── vite.config.ts
│ ├── tsconfig.json
│ └── eslint.config.js
│
├── docs/ # 项目文档
├── scripts/ # 数据修复/迁移脚本(非部署脚本)
├── package.json # 根级 package.json(monorepo 占位)
└── AGENTS.md # 本文件
技术栈详解
后端 (python-api)
⚠️ Python 版本要求: 3.13+ (项目使用 | 类型注解语法)
| 组件 | 技术 | 版本 | 用途 |
|---|---|---|---|
| Python | - | 3.13+ | 运行环境 |
| Web 框架 | FastAPI | 0.116+ | REST API |
| 数据库 | PostgreSQL | 15+ | 用户认证 |
| ORM | SQLAlchemy | 2.0 (异步) | 数据模型 |
| 缓存/调度 | Redis + Async Engine | 5.2+ / 自定义 | 异步任务槽位调度 |
| AI SDK | OpenAI / volcengine | 1.58+ / 5.0+ | LLM / 字幕调用 |
| TTS/语音 | KlingAI API | - | 语音合成、声音克隆 |
| 认证 | python-jose + passlib | 3.4+ / 1.7+ | JWT 认证 |
| HTTP 客户端 | httpx + aiohttp | 0.28+ / 3.13+ | 异步 HTTP |
| 包管理/构建 | uv | - | 虚拟环境、依赖锁定、Docker 构建 |
后端架构说明:
- 后端为"轻量云账号 + 全本地业务数据"模式
- 云端仅存储:用户账户信息
- 业务数据(项目/脚本/媒体/成品)全部本地存储
- 任务调度使用自定义 Async Engine(基于 Redis 的槽位管理),非 Celery
- 当前活跃 API 模块仅 4 个:
auth,system,caption,voice - 调度器已注册 7 个 Handler:
Video,Avatar,Image,Subtitle,Copy,Script,TTS
前端 (tauri-app)
| 组件 | 技术 | 版本 | 用途 |
|---|---|---|---|
| 桌面框架 | Tauri | 2.x | 桌面应用壳 |
| UI 框架 | React | 19.1+ | 用户界面 |
| 路由 | 自定义 NavigationContext | - | 页面切换(react-router-dom 已安装但未用于主流程) |
| 状态管理 | Zustand | 5.x | 全局状态 + Immer 中间件 |
| 数据获取 | SWR | 2.x | 请求缓存 |
| 虚拟列表 | @tanstack/react-virtual | 3.x | 大数据列表渲染 |
| 构建工具 | Vite | 7.x | 构建、开发服务器 |
| 测试 | Vitest + @testing-library | 4.x | 单元测试 |
| 类型生成 | openapi-typescript | 7.x | 从 OpenAPI 生成 TS 类型 |
| 字幕渲染 | assjs | 0.1.x | ASS 字幕前端渲染 |
Rust 后端 (src-tauri/src)
| 模块 | 用途 |
|---|---|
| lib.rs | Tauri 应用入口,命令注册(含 FFmpeg / 音频处理 / 视频合成命令) |
| ffmpeg_cmd.rs | FFmpeg 命令封装(首帧提取、字幕压制、封面合成、音频替换/混音/标准化) |
| video_processing.rs | 视频合成业务逻辑 |
| storage/engine.rs | 本地存储引擎(原子写入、文件锁、路径净化) |
| storage/paths.rs | 集中化路径计算 |
| commands/project.rs | 项目本地存储 IPC 命令 |
| commands/asset.rs | 资源文件保存 IPC 命令 |
| commands/auth_state.rs | 认证状态文件持久化 |
| commands/voice.rs | 音频文件保存/列表/删除 IPC 命令 |
| commands/product.rs | 成品视频保存/列表/删除/重命名 IPC 命令 |
| api_proxy.rs | Python API 代理转发 |
| avatar_cache.rs | 头像视频缓存管理 |
开发环境搭建
1. 启动 Python 后端
cd python-api
# 方式一:Docker Compose(推荐)
cp .env.example .env
# 编辑 .env 填入实际的 AI API Keys
docker-compose up -d
# 方式二:本地开发(若 Docker 不可用)
# 启动 PostgreSQL 和 Redis
docker-compose up -d db redis
# 安装依赖(使用 uv)
uv pip install -e ".[dev]"
# 启动开发服务器(注意:Docker API 会占用 8080 端口)
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
# 另开终端启动 Async Engine Scheduler(必须同时启动,否则任务不会执行)
python -m app.scheduler.main
后端服务地址:
Docker Compose 服务组成(4 个服务):
db: PostgreSQL 15redis: Redis 7api: FastAPI 开发服务器(端口 8080→8000)scheduler: Async Engine 统一调度器,处理所有第三方异步任务
2. 启动 Tauri 前端
cd tauri-app
# 安装依赖
npm install
# 开发模式(自动启动 Vite + Tauri)
npm run tauri dev
前端窗口:
- Vite 开发服务器: http://localhost:1420
- 应用窗口: 1440×960(最小 960×640,可调整大小)
构建命令
Python 后端
cd python-api
# 使用 Makefile(推荐)
make dev # 安装开发依赖 + pre-commit 钩子
make lint # ruff + mypy
make format # black + ruff --fix
make format-check # 检查格式但不修改
make test # pytest
make test-cov # 覆盖率报告
make security # bandit + pip-audit
make lint-semantic # 语义层禁词检查
make ci # 运行所有 CI 检查(format-check + lint + lint-semantic + test + security)
make docker-run # Docker Compose 启动全部服务
make scheduler # 启动 Async Engine Scheduler
make clean # 清理缓存文件
# 手动命令
black app/
ruff check app/
mypy app/
bandit -c pyproject.toml -r app/
pip-audit
pytest
pytest --cov=app
# 导出 OpenAPI 文档到前端
python3 -c "
import logging
logging.disable(logging.WARNING)
from app.main import app
import json
print(json.dumps(app.openapi(), indent=2, ensure_ascii=False))
" > ../tauri-app/src/api/generated/openapi.json
# Docker 构建
make docker
Tauri 前端
cd tauri-app
# 开发
npm run dev # 纯 Vite 开发(不启动 Tauri)
npm run tauri dev # 完整 Tauri 开发模式
# 构建
npm run build # 前端生产构建(tsc + vite build)
npm run tauri build # 打包桌面应用
# 测试
npm run test # 运行 Vitest
npm run test:ui # UI 模式
npm run test:coverage # 覆盖率报告
# 代码质量
npm run lint # ESLint 检查
npm run lint:fix # ESLint 自动修复
npm run format # Prettier 格式化
npm run format:check # Prettier 格式检查
npm run stylelint # CSS 检查
npm run stylelint:fix # CSS 自动修复
# 类型生成
npm run gen:api # 从 OpenAPI 生成 TypeScript 类型
架构说明
混合路由架构
前端 API 调用采用 智能路由 策略:
- HTTP 直连 Python: 纯数据 API(脚本生成、模型管理、任务轮询等)
- Tauri IPC → Rust: 需要本地能力的 API(FFmpeg、文件系统)
路由决策在 tauri-app/src/api/client.ts 中实现。HTTP 客户端会自动处理 camelCase ↔ snake_case 字段名转换。需要走 Rust IPC 的 API 包括:
video_composite_synthesis// FFmpeg 视频合成burn_subtitle// 字幕压制extract_video_first_frame// 首帧提取generate_cover_image// 封面生成replace_audio_track// 音频替换mix_audio_tracks// 音频混音standardize_audio// 音频标准化save_project_meta*/load_project_meta*// 本地文件系统save_project_segments*/load_project_segments*save_project_asset/get_video_save_path/get_image_save_pathsave_final_product/list_local_products/delete_local_product/rename_local_productsave_audio/list_project_audios/delete_audio- 头像缓存相关 API
添加新 API 流程:
- Python 端实现端点
- 前端直接调用(默认 HTTP)
- 仅当需要本地能力时,在 Rust 中添加命令并在
lib.rs注册
AI Provider 架构
后端 AI 模块采用 多 Provider 路由 设计:
app/ai/
├── model_router.py # 模型路由器(自动降级)
├── providers/
│ ├── base.py # Provider 抽象基类
│ ├── generic_llm_provider.py # 通用 OpenAI 兼容 Provider
│ ├── volcengine_provider.py # 火山方舟官方 SDK
│ └── klingai_provider.py # KlingAI(可灵 AI)
└── prompts/ # 提示词模板(禁止硬编码)
支持的 AI 平台:
- 火山方舟 (字节跳动) - LLM、字幕服务
- OpenAI - GPT 系列(可选)
- 可灵 AI (快手) - TTS 语音合成、声音克隆
- 文心一言 (百度) - 可选
- 通义千问 (阿里云) - 可选
AI 模型配置位于 python-api/config/ai_models.yaml,支持热重载,无需重启服务即可更新模型配置。
Async Engine(异步任务调度)
⚠️ 重要:项目不使用 Celery,使用自定义 Async Engine
架构:
API (POST /tasks/{type}) → Redis JobRegistry → AsyncEngine tick loop → Handlers
组件:
AsyncEngine(app/scheduler/engine.py): 每 ~10s 执行tick(),加载运行中任务,按类型分组,并行分发给 Handler,通过 Pipeline 应用StateChange,清理已完成任务JobRegistry(app/scheduler/registry.py): Redis-based 任务 CRUD,使用job:{id}hash +scheduler:running_tasksSETSlotManager(app/scheduler/slot_manager.py): Redis Lua 原子脚本实现并发槽位抢占/释放JobRecord/StateChange(app/scheduler/models.py): 调度器内部类型
已注册的 Handler(app/scheduler/main.py):
| Handler | 槽位数 | Redis Key | 用途 |
|---|---|---|---|
| VideoHandler | 18 | kling:video_slots |
Kling 视频生成(omni + image2video) |
| ImageHandler | 9 | kling:image_slots |
Kling 图片生成 |
| ScriptHandler | 10 | script:slots |
LLM 脚本生成(含 AnyToCopy 视频文案提取) |
| SubtitleHandler | 5 | volc:subtitle_slots |
火山引擎字幕/自动对齐 |
| CopyHandler | 5 | anytocopy:slots |
AnyToCopy 视频文案提取 |
| TTSHandler | - | kling:tts_slots |
Kling TTS 语音合成 |
| AvatarHandler | 2 | kling:avatar_slots |
Kling 形象克隆 |
TokenManager(API 认证 Token 管理)
app/core/token_manager.py 提供通用的 API 认证 Token 缓存与自动刷新:
from app.core.token_manager import JWTTokenStrategy, TokenManager
class MyProvider:
def __init__(self, access_key: str, secret_key: str):
self._token_strategy = JWTTokenStrategy(
access_key=access_key,
secret_key=secret_key,
expires_in=1800, # 30分钟
)
async def _get_headers(self) -> dict[str, str]:
token_info = await TokenManager.get_instance().get_token(self._token_strategy)
return {"Authorization": f"Bearer {token_info.token}"}
特性:
- Token 缓存(避免重复生成)
- 自动刷新(Token 即将过期时自动刷新)
- 并发安全(双重检查锁定,确保并发请求只生成一次 Token)
- 后台预热(提前 10 分钟刷新,避免请求时等待)
- 支持 JWT、OAuth2 等多种策略
本地存储引擎(Rust)
Rust 层实现了 defense-in-depth 的本地存储系统:src-tauri/src/storage/
engine.rs: 核心原子操作sanitize_id()— 白名单[a-zA-Z0-9_-]+,防御路径遍历sanitize_filename()— 提取纯文件名,拒绝目录组件atomic_write_json()/atomic_write_bytes()— 先写.tmp再rename原子替换with_file_lock()— 通过fs2实现独占文件锁read_json<T>()— 安全读取,文件不存在返回None
paths.rs: 集中路径计算~/Documents/Meijiaka/projects/{id}/(meta.json, segments.json, assets/)~/Documents/Meijiaka/products/~/Documents/Meijiaka/avatars.json{app_config_dir}/auth.json
所有本地 JSON 读写必须经过 StorageEngine,禁止在命令处理器中直接调用 fs::write。
数据库模型
后端当前仅保留 1 个活跃模型:
users -- 用户账户信息(mobile, nickname, avatar_url)
注:
avatars和model_usage_logs相关模型及 CRUD 在最近的重构中已移除或清理。crud/avatar.py仍存在但可能处于未使用状态。
业务数据本地存储:
- 项目/脚本/分镜 → 前端本地 JSON 文件(
~/Documents/Meijiaka/projects/) - 音频/视频/图片文件 → 本地磁盘
- 成品视频 →
~/Documents/Meijiaka/products/ - 用户配置 → localStorage(少量 UI 状态)
数据流规范
用户输入主题 ──→ 后端 AI 生成脚本 ──→ 后端返回分镜列表 ──→ 前端保存到本地
│ │
└────────────────── 后端不存储脚本数据 ────────────────────────┘
用户导入长视频 ──→ Rust 本地切割 ──→ 生成分段视频 ──→ 前端保存到本地
本地存储结构
~/Documents/Meijiaka/ # 用户文档目录
├── config.json # 全局配置
├── projects/ # 项目数据
│ └── {project_id}/
│ ├── meta.json # 项目元数据
│ ├── segments.json # 分镜数据
│ ├── media/ # 导入的原始素材
│ ├── shots/ # 自动切割后的片段
│ ├── audio/ # TTS 生成的音频
│ └── assets/ # 资源文件(封面、成品等)
├── products/ # 成品视频目录
├── avatars.json # 形象列表(本地)
└── cache/ # 缓存目录
项目元数据 meta.json 关键字段:
id,title,topic,status(draft | published)currentStep: 1=脚本生成, 2=视频粗剪, 3=语音配音, 4=字幕压制, 5=封面制作, 6=视频合成createdAt,updatedAt,exportedAtcoverPath,finalVideoPathcoverConfig,scriptDuration,scriptType
分镜数据 segments.json 字段:
id,type(segment | empty_shot),scene,voiceover,durationvideoPath,videoUrl,elementId,voiceIdalignmentResult,burnedVideoPath,burnedAt
前端导航
主应用壳使用 自定义 NavigationContext(React Context)实现页面切换,映射 Record<PageType, ComponentType>。react-router-dom 已安装但主要用于未来扩展或特定路由场景,当前主流程不使用 BrowserRouter 进行导航。
页面结构:
video-creation- 6 步视频创作流程avatar-clone- 形象/声音克隆管理my-works- 我的作品profile- 个人中心settings-*- 设置相关页面
状态管理
七个专门的 Zustand store:
| Store | 职责 | 持久化 |
|---|---|---|
authStore |
JWT、UserInfo、登录/登出 | Tauri auth.json(或 localStorage fallback) |
projectStore |
分镜、currentStep、选题、封面配置 | 仅 UI 标志通过 persist;业务数据显式写入本地 JSON |
taskStore |
异步任务状态/进度/消息 | 无(内存 only,真相源在后端 Redis) |
uiStore |
Toast 通知队列 | 无 |
progressStore |
全局进度模态框 | 无 |
settingsStore |
主题模式、用户偏好 | localStorage |
voiceStore |
语音/形象选择状态 | 无 |
projectStore 不自动保存。数据在显式过渡点持久化到磁盘(如进入 step 2、调用 setFinalVideoPath 时触发 saveMetaToLocalFile)。saveMetaToLocalFile() 通过 Promise 链串行化写入,避免并发覆盖。
开发规范
核心原则
- 后端环境优先使用 Docker Compose: 开发时通过
docker-compose up -d启动后端。前端默认连接http://127.0.0.1:8080/api/v1。 - 接口契约优先: 后端承诺无论使用什么 AI 模型,输出永远符合同一个 Schema
- 类型单一来源: 后端 Schema 是权威,前端通过 OpenAPI 生成类型
- Adapter 层隔离: 前后端字段差异只允许在 Adapter 层处理
- 数据库分层: API → Service → CRUD → Model,禁止跨层调用
- 提示词文件化: 除前端输入外,后端不允许硬编码任何 Prompt
- 配置统一管理: 所有配置通过
get_settings()读取,禁止直接使用os.getenv() - 本地存储必须经过 StorageEngine: Rust 层所有文件操作使用
atomic_write_json+with_file_lock
配置管理规范
架构层级:
.env (Layer 1) ──→ Settings (Layer 2) ──→ 服务层 (Layer 3)
↑
唯一配置出口
强制规范:
- 所有服务必须使用
from app.config import get_settings读取配置 - 禁止在服务层使用
os.getenv()或os.environ.get() - 所有配置项必须在
app/config.py的Settings类中定义 - 敏感信息(API Keys、Secrets)必须通过环境变量注入
- 业务默认值可以硬编码在
Settings中
添加新配置流程:
- 在
app/config.py的Settings类中添加字段定义 - 在
.env中添加实际值(敏感信息)或使用默认值 - 在服务层通过
get_settings()读取 - 更新
.env.example文档
语义层防护网
项目强制执行语义分层,禁止供应商术语泄漏到业务层:
| 层级 | 职责 | 禁词示例 |
|---|---|---|
| Layer 6 (Presentation) | API Schema | element_id, kling_task_id |
| Layer 4 (Orchestration) | Scheduler | task_id(应使用 job_id) |
| Layer 3 (Domain) | Service | 供应商特定术语 |
| Layer 2 (Adapter) | Provider | 允许使用供应商原生术语 |
Makefile 提供 make lint-semantic 进行自动化检查:
- API 层(除
klingai.py)禁止使用element_id(应使用provider_element_id或human_id) - Scheduler 层禁止使用
task_id(应使用job_id) - 全局禁止
kling_task_id(应使用provider_task_id) - Scheduler Redis key 必须使用
job:而非task:
快速参考
| 场景 | 正确做法 |
|---|---|
| 后端换 AI 模型 | 修改 services/ai_response_utils.py 标准化层,不修改 Schema |
| 后端新增字段 | Optional[T] = Field(None),向后兼容 |
| 后端修改字段 | 保留旧字段,标记 deprecated,逐步迁移 |
| 前端需要新字段 | Store 中 extends 基础类型 |
| 数据清洗 | 只在 Adapter 层,禁止在组件层 |
| 新增数据库实体 | 创建 Model → CRUD → API(分层开发) |
| 数据库查询 | 在 CRUD 层封装,API 层调用 |
| 事务管理 | API 层控制,通过 get_db 依赖注入 |
| 新增提示词 | 创建 .txt 文件,使用 _load_prompt() 加载 |
| 新增本地文件操作 | 使用 storage::engine 原子写入 + 文件锁 |
后端分层架构
API Layer (api/v1/*.py)
↓ 调用
Service Layer (services/*.py) - 可选,复杂业务
↓ 调用
CRUD Layer (crud/*.py)
↓ 调用
Model Layer (models/*.py)
↓ 调用
Database Layer (db/*.py)
禁止:
- API 层直接操作 Model
- CRUD 层返回 Schema(应返回 Model)
- Service 层直接操作数据库(应通过 CRUD)
- 在业务代码中写 SQL
前端类型规范
| 层级 | 类型来源 | 说明 |
|---|---|---|
| 后端 Schema | python-api/app/schemas/*.py |
Pydantic 模型,OpenAPI 生成源 |
| 前端基础类型 | tauri-app/src/api/types.ts |
手写的核心类型,与后端对齐 |
| 前端完整类型 | tauri-app/src/api/generated/schema.ts |
OpenAPI 自动生成,只读 |
| Store 扩展 | tauri-app/src/store/*.ts |
extends 基础类型添加前端字段 |
间距规范
前端使用基于 4px 的网格系统,定义在 tauri-app/src/styles/variables.css:
| 变量 | 值 | 使用场景 |
|---|---|---|
--spacing-2xs |
2px | 微调控件、边框线 |
--spacing-xs |
4px | 紧凑间隙、图标边距 |
--spacing-sm |
8px | 小间隙、按钮内边距-y |
--spacing-md |
12px | 标准间隙、卡片内边距 |
--spacing-lg |
16px | 大间隙、区块间距 |
--spacing-xl |
24px | 页面区块、内容分隔 |
--spacing-2xl |
32px | 大区块间距、页面边距 |
--spacing-3xl |
48px | 页面级间距、Hero 区域 |
代码风格
Python
- 格式化: Black (line-length: 100, target-version: py313)
- 检查: Ruff (E, F, I, N, W, UP, B, C4, SIM)
- 类型: MyPy(非严格模式全局,但
app.schemas.*、app.crud.*、app.scheduler.handlers.*强制严格模式) - 文档: 中文注释,Google Style Docstrings
- 安全: Bandit + pip-audit
- Git Hooks: pre-commit(Black、Ruff、uv lock 同步检查)
- 忽略规则: Ruff 忽略
E501, E402, N802, N803, N806, N815, B008, B904
TypeScript/React
- 类型: 严格 TypeScript 模式(
strict: true,noUnusedLocals: true,noUnusedParameters: true) - 组件: 函数组件 + Hooks
- 状态: Zustand 管理全局状态(配合 Immer 处理不可变更新)
- 样式: 普通 CSS + CSS 变量(
tauri-app/src/styles/variables.css) - ESLint: 使用
eslint.config.js(Flat Config),含 React Hooks 和 React Refresh 规则 - Prettier: semi=true, singleQuote=true, tabWidth=2, printWidth=100, trailingComma=es5
- Stylelint:
stylelint-config-standard,禁止 magic px 用于border-radius和font-size - ESLint 规则:
no-console为 warn(允许 warn/error/info),eqeqeq为 warn,no-var为 error
Rust
- 格式化: rustfmt
- 检查: cargo clippy
- 注释: 中文文档注释
提交规范
feat: 新功能
fix: 修复
docs: 文档
refactor: 重构
test: 测试
chore: 构建/工具
测试策略
后端测试
cd python-api
# 运行所有测试
pytest -v
# 覆盖率报告
pytest --cov=app --cov-report=html --cov-report=term
测试配置 (pyproject.toml):
- asyncio_mode = "auto"
- 测试文件命名:
test_*.py - 测试路径:
tests/
注:当前后端测试覆盖非常有限,仅
tests/test_kling_tts.py一个测试文件,包含TTSService、VoiceCloneService和状态枚举的单元测试。大量模块暂无测试。
前端测试
cd tauri-app
# 运行 Vitest
npm run test
# UI 模式
npm run test:ui
# 覆盖率报告
npm run test:coverage
测试配置:
- 测试框架: Vitest 4.x + @testing-library/react 16.3.x + jsdom
- 测试文件:
src/**/*.test.ts(x) - Mock 配置:
src/__tests__/setup.ts - 自动 Mock: localStorage, Tauri API (
@tauri-apps/api/core) - 现有测试:
src/store/__tests__/authStore.test.tsxsrc/store/__tests__/settingsStore.test.tsx
安全注意事项
- SECRET_KEY: 生产环境必须修改为强随机密钥(
get_settings()会在生产环境校验) - CORS: 生产环境限制为实际前端域名,开发环境
DEBUG=true时允许所有来源 - API Keys: 不要提交到 Git,使用
.env文件注入 - FFmpeg: 嵌入的二进制文件需验证来源
- 文件上传: 限制文件类型和大小,防止攻击
- 路径遍历: Rust StorageEngine 的
sanitize_id()和sanitize_filename()防御路径遍历攻击 - 原子写入: 所有本地 JSON 使用
atomic_write_json(先写.tmp再rename) - 文件锁: 并发 RMW 操作使用
with_file_lock防止竞态 - 日志: 后端日志写入
~/Documents/Meijiaka-zj/logs/api_YYYYMMDD.log
配置说明
Python 后端 (.env)
关键环境变量:
# 数据库 (PostgreSQL)
DATABASE_URL=postgresql+asyncpg://postgres:postgres@localhost:5432/meijiaka
# Redis
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_DB=0
# JWT 密钥(生产环境必须修改)
SECRET_KEY=your-secret-key-here-change-in-production
ACCESS_TOKEN_EXPIRE_MINUTES=10080
# AI API Keys
VOLCENGINE_API_KEY=your-volcengine-key
VOLCENGINE_CAPTION_APPID=your-caption-appid
VOLCENGINE_CAPTION_TOKEN=your-caption-token
KLINGAI_ACCESS_KEY=your-kling-access-key
KLINGAI_SECRET_KEY=your-kling-secret-key
OPENAI_API_KEY=sk-your-openai-key
# 七牛云存储
QINIU_ACCESS_KEY=your-qiniu-access-key
QINIU_SECRET_KEY=your-qiniu-secret-key
QINIU_VIDEO_BUCKET=media-liche
QINIU_IMAGE_BUCKET=img-liche
# CORS 允许的前端地址
CORS_ORIGINS=http://localhost:1420,http://127.0.0.1:1420,http://localhost:8080
Tauri 配置 (tauri.conf.json)
{
"productName": "美家卡智剪",
"identifier": "cn.meijiaka.ai-jian",
"build": {
"devUrl": "http://localhost:1420",
"frontendDist": "../dist"
},
"bundle": {
"externalBin": ["binaries/ffmpeg"],
"resources": {
"fonts/*": "fonts/"
}
}
}
AI 模型配置 (config/ai_models.yaml)
模型配置文件支持热重载,无需重启服务即可更新模型配置。主要配置项:
- platforms: AI 平台配置(mock, volcengine, klingai)
- models: 可用模型列表及其能力标签 [script, polish, chat, image, video_generation, image2video, lip_sync, image_generation]
- task_defaults: 任务类型到模型的默认映射
视频创作流程
- 脚本生成 (Step 1) - AI 生成视频脚本和分镜,或用户直接粘贴文案
- 视频粗剪 (Step 2) - 导入长视频,按脚本时长自动切割为片段
- 语音配音 (Step 3) - 声音克隆 + TTS 生成每段配音
- 字幕压制 (Step 4) - 生成字幕并压制到视频中
- 封面制作 (Step 5) - 生成视频封面
- 视频合成 (Step 6) - FFmpeg 拼接视频片段,替换原声为 TTS 音频,导出最终视频
常见问题
Q: 火山方舟如何配置?
- 注册火山引擎账号并实名认证
- 创建 API Key
- 开通模型并创建推理接入点
- 在
.env中设置VOLCENGINE_API_KEY
Q: 可灵 AI 如何配置?
- 前往可灵 AI 开发者平台 https://klingai.com/document-api
- 获取 Access Key 和 Secret Key
- 在
.env中设置KLINGAI_ACCESS_KEY和KLINGAI_SECRET_KEY
Q: FFmpeg 在哪里?
Tauri 应用已嵌入 FFmpeg 二进制文件:
- 位置:
tauri-app/src-tauri/binaries/ffmpeg-* - 使用: Rust 层通过
ffmpeg_cmd模块调用 - 打包时会作为
externalBin资源嵌入
Q: 后端换了 AI 模型,输出格式变了怎么办?
修改 services/ai_response_utils.py 中的标准化函数,增加新的字段映射,不要修改 API Schema。
Q: 如何新增/修改提示词?
- 创建文件:
app/ai/prompts/my_prompt.txt - 加载使用:
prompt = self._load_prompt("my_prompt") - 禁止: 在 Python 代码中直接写
"""你是一位..."""
Q: 项目数据是如何持久化的?
- 项目元数据(
meta.json)和分镜数据(segments.json)保存在~/Documents/Meijiaka/projects/{project_id}/ - 不通过 Zustand
persist保存项目数据,而是通过localProjectApi显式调用 Tauri IPC 写入文件 projectStore的persist中间件仅保存少量 UI 状态
Q: Async Engine 和 Celery 有什么区别?
本项目使用自定义 Async Engine 替代 Celery:
- 基于 Redis 的槽位管理(SlotManager),限制各类型任务的并发数
- 独立的
scheduler进程(python -m app.scheduler.main) - 每个 Handler 实现
AsyncHandler接口,状态机驱动任务生命周期 - 优势:更细粒度的并发控制、统一状态机、无 Celery 依赖
Q: 为什么后端某些 API 模块不存在?
当前项目处于从「美家卡智影」向「美家卡智剪」的重构过程中。app/api/v1/router.py 当前仅注册了 4 个模块(auth, system, caption, voice),历史上存在的 script, video, klingai, qiniu, ai_models, tasks 等路由当前未激活。如需使用相关功能,需在路由中重新引入。
最后更新: 2026-04-21 架构模式: 单机版(轻量云账号 + 全本地业务数据) 项目状态: 从 ai-meijiaka Fork 后的活跃重构中