# LocalMiniDrama 后端服务

**Node.js + Express + SQLite · 纯 JavaScript · 无 TypeScript**

→ [项目主页](../README.md) | [快速开始](../docs/quickstart.md) | [AI 配置](../docs/configuration.md) | [版本历史](../docs/changelog.md) | [作者故事](../docs/story.md) | [English](../docs/en.md)

**官方仓库：**
[![GitHub](https://img.shields.io/badge/GitHub-xuanyustudio%2FLocalMiniDrama-181717?logo=github)](https://github.com/xuanyustudio/LocalMiniDrama)
[![Gitee](https://img.shields.io/badge/Gitee-bi__shang__a%2Flocalminidrama-C71D23?logo=gitee)](https://gitee.com/bi_shang_a/localminidrama)

> 遇到问题或有功能建议，欢迎在 [GitHub Issues](https://github.com/xuanyustudio/LocalMiniDrama/issues) 或 [Gitee Issues](https://gitee.com/bi_shang_a/localminidrama/issues) 提交反馈。

> **本包版本：** `1.2.7`（与仓库根目录 [CHANGELOG](../CHANGELOG.md)、前端与桌面 `package.json` 对齐）

---

## 目录

- [环境要求](#环境要求)
- [安装与启动](#安装与启动)
- [目录结构](#目录结构)
- [配置文件](#配置文件)
- [API 接口总览](#api-接口总览)
- [数据库说明](#数据库说明)
- [各大平台中转站示例配置](#各大平台中转站示例配置)
- [AI 服务集成](#ai-服务集成)
- [开发说明](#开发说明)

---

## 环境要求

| 依赖 | 版本 |
|------|------|
| Node.js | >= 18 |
| npm | 随 Node.js 附带 |

---

## 安装与启动

```bash
cd backend-node

# 安装依赖
npm install

# 复制配置文件
cp configs/config.example.yaml configs/config.yaml
# Windows: copy configs\config.example.yaml configs\config.yaml

# 编辑 config.yaml，填入 AI API 配置（也可通过前端「AI 配置」页面管理）

# 首次运行：初始化数据库表
npm run migrate

# 生产启动（默认端口 5679）
npm start

# 开发模式（nodemon 热重载）
npm run dev
```

启动成功后终端输出：
```
Server started on port 5679
```

---

## 目录结构

```
backend-node/
├── configs/
│   ├── config.example.yaml     # 配置模板（提交到 Git）
│   └── config.yaml             # 实际配置（不提交，自行创建）
├── data/
│   ├── drama_generator.db      # SQLite 数据库
│   └── storage/                # 生成的图片/视频本地文件
│       ├── images/             # 分镜生成图
│       ├── characters/         # 角色图
│       ├── scenes/             # 场景图
│       ├── videos/             # 生成的视频片段
│       └── merged/             # 合成后的完整视频
├── migrations/
│   ├── 01_init.sql             # 初始建表
│   ├── 02_local_path.sql       # 本地路径字段
│   └── 03_async_tasks_frame_prompts.sql
├── src/
│   ├── app.js                  # Express 应用（路由注册、中间件）
│   ├── server.js               # HTTP 服务入口
│   ├── logger.js               # 日志（pino）
│   ├── response.js             # 统一响应格式工具
│   ├── config/
│   │   └── index.js            # YAML 配置加载
│   ├── db/
│   │   ├── index.js            # better-sqlite3 连接
│   │   └── migrate.js          # 启动时自动补列（ensureColumns）
│   ├── routes/
│   │   ├── index.js            # 路由总入口
│   │   ├── drama.js            # 剧本 / 导出 / 导入
│   │   ├── videos.js           # 视频生成任务
│   │   ├── images.js           # 图片生成任务
│   │   ├── tasks.js            # 异步任务查询
│   │   ├── aiConfig.js         # AI 服务商配置 CRUD
│   │   ├── settings.js         # 全局设置
│   │   └── static.js           # 静态文件服务（/static）
│   └── services/
│       ├── dramaService.js             # 剧本 CRUD 与数据组装
│       ├── episodeStoryboardService.js # 分镜生成核心逻辑
│       ├── imageService.js             # 图片生成任务处理
│       ├── videoService.js             # 视频生成任务处理
│       ├── videoMergeService.js        # 视频合并（ffmpeg）
│       ├── videoClient.js              # 视频 API 调用（Volcengine 等）
│       ├── imageClient.js              # 图片 API 调用（DashScope 等）
│       ├── characterGenerationService.js  # 角色提取与生成
│       ├── characterLibraryService.js     # 角色库管理
│       ├── backgroundExtractionService.js # 场景背景提取
│       ├── propExtractionService.js       # 道具提取
│       ├── propImageGenerationService.js  # 道具图片生成
│       ├── framePromptService.js          # 首/尾帧提示词生成
│       ├── dramaExportService.js          # 工程导出为 ZIP
│       ├── dramaImportService.js          # ZIP 工程导入
│       ├── promptI18n.js                  # 多语言提示词模板
│       └── uploadService.js              # 本地文件存储管理
└── tools/                      # 辅助脚本（数据迁移等）
```

---

## 配置文件

`configs/config.yaml` 主要配置项：

```yaml
server:
  port: 5679                      # HTTP 服务端口

database:
  path: ./data/drama_generator.db # SQLite 文件路径

storage:
  local_path: ./data/storage      # 图片/视频本地存储根目录

language: zh                      # 提示词语言（zh / en）

style:
  default_style: realistic         # 默认绘图风格
  default_image_ratio: "16:9"      # 默认图片比例
  default_video_ratio: "16:9"      # 默认视频比例
```

> AI 服务配置（API Key、模型名、端点 URL）通过前端「AI 配置」页面管理，存储于数据库 `ai_service_configs` 表，无需手动编辑 YAML。

---

## 各大平台中转站示例配置

仓库根目录（与 `backend-node` 同级）下的 [`各大平台中转站配置/`](https://github.com/xuanyustudio/LocalMiniDrama/tree/main/%E5%90%84%E5%A4%A7%E5%B9%B3%E5%8F%B0%E4%B8%AD%E8%BD%AC%E7%AB%99%E9%85%8D%E7%BD%AE) 提供多家常见中转站的 **完整 JSON 示例**，可直接对照导入前端「AI 配置」，再改为自己的 Key 与地址。

| 文件 | 说明 |
|------|------|
| `使用说明.txt` | 导入后需将 **每一条** 服务里的 `api_key` 换成自己的 |
| `302ai-302.json` | 302.AI 示例 |
| `云雾ai.json` | 云雾 AI 示例 |
| `向量.json` | 向量平台示例 |
| `飞儿api-ffir.cn.json` | 飞儿 API 示例 |

**示意图（与 JSON 互补）：**

- [官方即梦 2.0 配置](https://github.com/xuanyustudio/LocalMiniDrama/blob/main/%E5%90%84%E5%A4%A7%E5%B9%B3%E5%8F%B0%E4%B8%AD%E8%BD%AC%E7%AB%99%E9%85%8D%E7%BD%AE/%E5%AE%98%E6%96%B9%E5%8D%B3%E6%A2%A62.0%E9%85%8D%E7%BD%AE.png)
- [本地反向代理即梦 Free API 配置](https://github.com/xuanyustudio/LocalMiniDrama/blob/main/%E5%90%84%E5%A4%A7%E5%B9%B3%E5%8F%B0%E4%B8%AD%E8%BD%AC%E7%AB%99%E9%85%8D%E7%BD%AE/%E8%B0%83%E7%94%A8%E6%9C%AC%E5%9C%B0%E5%8F%8D%E5%90%91%E4%BB%A3%E7%90%86%E5%8D%B3%E6%A2%A6freeapi%E7%9A%84%E9%85%8D%E7%BD%AE.png)

若中转商控制台中的商品名（如「即梦 2.0」）与示例里的 **模型 ID**（如 `doubao-seedream-4-0-250828`）表述不一致，以对方实际开放的模型 ID 为准，并在本系统中与 JSON 保持一致。更多面向最终用户的说明见仓库根目录 [项目主页 `index.html`](../index.html) 中的「AI 与各大平台中转站」区块（发布页展示用）。

---

## API 接口总览

所有接口前缀：`/api/v1`

响应统一格式：
```json
{
  "success": true,
  "data": { ... },
  "timestamp": "2026-01-01T00:00:00.000Z"
}
```
错误响应：
```json
{
  "success": false,
  "error": { "code": "INTERNAL_ERROR", "message": "..." },
  "timestamp": "..."
}
```

### 剧集（Drama）

| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/dramas` | 获取剧集列表（分页） |
| POST | `/dramas` | 创建新剧集 |
| GET | `/dramas/:id` | 获取剧集详情（含集数、角色、场景等） |
| PUT | `/dramas/:id` | 更新剧集信息 |
| DELETE | `/dramas/:id` | 软删除剧集 |
| GET | `/dramas/:id/export` | 导出工程 ZIP |
| POST | `/dramas/import` | 导入工程 ZIP（multipart/form-data，字段名 `file`） |
| GET | `/dramas/stats` | 统计信息 |

### 集数（Episode）

| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/episodes/:id` | 获取集数详情 |
| PUT | `/episodes/:id` | 更新集数（剧本内容、标题等） |
| POST | `/dramas/:id/episodes` | 新增集数 |
| DELETE | `/episodes/:id` | 删除集数 |

### 分镜（Storyboard）

| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/episodes/:id/storyboards` | 获取集数所有分镜 |
| POST | `/episodes/:id/generate-storyboard` | 触发分镜生成任务 |
| PUT | `/storyboards/:id` | 更新分镜字段 |

### 图片生成（Image）

| 方法 | 路径 | 说明 |
|------|------|------|
| POST | `/images` | 创建图片生成任务 |
| GET | `/images/:id` | 查询任务状态 |
| GET | `/storyboards/:id/images` | 获取分镜所有图片 |
| POST | `/storyboards/:id/images/upload` | 手动上传图片 |

### 视频生成（Video）

| 方法 | 路径 | 说明 |
|------|------|------|
| POST | `/videos` | 创建视频生成任务 |
| GET | `/videos/:id` | 查询任务状态 |
| POST | `/episodes/:id/merge-video` | 触发视频合并 |
| GET | `/episodes/:id/merge-status` | 查询合并进度 |

### AI 配置（AI Config）

| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/ai-configs` | 获取所有 AI 配置 |
| POST | `/ai-configs` | 新增配置 |
| PUT | `/ai-configs/:id` | 修改配置 |
| DELETE | `/ai-configs/:id` | 删除配置 |
| POST | `/ai-configs/:id/test` | 测试连接 |
| POST | `/ai-configs/preset/dashscope` | 一键创建通义预设 |
| POST | `/ai-configs/preset/volcengine` | 一键创建火山预设 |

### 异步任务（Task）

| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/tasks/:id` | 查询任务状态与进度 |
| GET | `/tasks` | 获取任务列表 |

### 角色与素材库

| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/dramas/:id/characters` | 获取剧集角色 |
| POST | `/dramas/:id/characters/extract` | 从剧本提取角色（触发任务） |
| POST | `/characters/:id/generate-image` | 生成角色图片 |
| GET | `/dramas/:id/scenes` | 获取剧集场景 |
| POST | `/episodes/:id/extract-backgrounds` | 提取场景背景（触发任务） |
| GET | `/dramas/:id/props` | 获取剧集道具 |
| POST | `/episodes/:id/extract-props` | 从剧本提取道具（触发任务） |
| POST | `/props/:id/generate-image` | 生成道具图片 |

### 静态文件

| 路径 | 说明 |
|------|------|
| `GET /static/*` | 访问 `data/storage/` 下的图片/视频文件 |

---

## 数据库说明

使用 better-sqlite3（同步 API），数据库文件为单个 SQLite 文件。

**主要数据表：**

| 表名 | 说明 |
|------|------|
| `dramas` | 剧集基本信息（标题、类型、风格、metadata） |
| `episodes` | 集数（所属剧集、剧本内容） |
| `storyboards` | 分镜（所属集数、台词、动作、提示词等） |
| `characters` | 角色（所属剧集、外貌描述、图片路径） |
| `episode_characters` | 角色-集数关联表（多对多） |
| `scenes` | 场景（所属剧集/集数、位置、时间、提示词） |
| `props` | 道具（所属剧集/集数、类型、描述） |
| `image_generations` | 图片生成任务记录（状态、本地路径） |
| `video_generations` | 视频生成任务记录（状态、本地路径、参数） |
| `video_merges` | 视频合并任务记录 |
| `ai_service_configs` | AI 服务商配置（API Key、模型、端点） |
| `async_tasks` | 通用异步任务（分镜生成、角色提取等） |
| `character_libraries` | 全局/剧集角色素材库 |
| `scene_libraries` | 全局/剧集场景素材库 |
| `prop_libraries` | 全局/剧集道具素材库 |

**字段约定：**
- `deleted_at IS NULL` — 所有查询均过滤软删除记录
- `metadata TEXT` — JSON 字符串，存储扩展属性（如 `aspect_ratio`、`video_clip_duration`）
- `local_path TEXT` — 相对于 `storage/` 根目录的相对路径

**数据库迁移：**
- `npm run migrate` — 运行 `migrations/` 目录下的 SQL 文件
- 每次服务启动时自动执行 `ensureColumns()`，确保所有列存在（支持旧数据库升级）

---

## AI 服务集成

### 图片生成流程

1. 前端调用 `POST /images` → 创建 `image_generations` 记录（status=pending）
2. `imageService.js` 异步处理：调用配置好的图片 API → 下载图片到本地 → 更新记录（status=completed, local_path）
3. 前端轮询 `GET /images/:id` 直到 status=completed

**支持的图片 API：**
- DashScope（通义万象）：`POST /api/v1/services/aigc/text2image/image-synthesis`
- Volcengine（豆包）：`POST /api/v3/images/generations`（OpenAI 兼容格式）

### 即梦（Seedream）Volcengine 图生图与文生图

- 分镜 **图生图**（`storyboard_image`）与 **文生图**（`image`）中，凡使用 `doubao-seedream-*` 系列模型，均走 `imageClient.js` 的 **Volcengine 图片协议**分支；配置项 **`api_protocol`** 须为 `volcengine`，`base_url`、`endpoint` 需与所用中转站文档一致，勿与纯 OpenAI 兼容条目混用。
- 中转商界面若标注为「即梦 2.0」等名称，请以对方返回的 **模型 ID** 为准（示例中常见 `doubao-seedream-4-0-250828`、`doubao-seedream-4-5-251128` 等），与 [各大平台中转站示例配置](#各大平台中转站示例配置) 中的 JSON 对照修改即可。
- **Seedream 4.5+** 对输出像素有下限（约 1920×1920 等效面积）；`imageClient.js` 会在请求前对过小尺寸做等比放大（`fixSeedreamSize`）。若仍报错，请结合中转站错误信息检查模型与尺寸。

**图片尺寸：** 系统根据项目 `metadata.aspect_ratio` 自动计算符合服务商最低要求的分辨率（最低 3,686,400 像素）。

### 视频生成流程

1. 前端调用 `POST /videos` → 创建 `video_generations` 记录
2. `videoService.js` 异步处理：调用视频 API → 轮询任务状态 → 下载到本地
3. 前端轮询 `GET /videos/:id` 直到 status=completed

**视频参数（Volcengine 经典 Seedance 单链路，示例）：**
```json
{
  "model": "doubao-seedance-1-0-pro-250528",
  "content": [{ "type": "text", "text": "..." }],
  "ratio": "16:9",
  "duration": 5,
  "resolution": "720p",
  "seed": null,
  "camera_fixed": false,
  "watermark": false
}
```

**火山方舟 Seedance 2.0 · 全能 / 多参考图（`volcengine_omni`）：**

- 在前端「AI 配置 → 视频生成」选择接口规范 **`volcengine_omni`**，厂商仍为火山引擎；**Base URL** 一般为 `https://ark.cn-beijing.volces.com/api/v3`；**模型**填控制台接入点（如 `doubao-seedance-2-0-260128`、`doubao-seedance-2-0-fast-260128`）。
- 与制作页分镜 **「全能模式」** 配合：首条为文本提示，其余参考图为场景/角色/道具/分镜主图等，每张 **`role: reference_image`**；方舟侧最多取 **9** 张。
- **Seedance 2.x** 请求时长会在后端吸附到 **4–15 秒**；默认走 `POST /v1/videos/generations`（可用配置 **Endpoint** 覆盖）。实现见 `videoClient.js`（`volcengine_omni` 分支）。

**可灵 Omni（`kling_omni`）** 同样支持分镜全能模式的多图参考与片段描述-only 提交逻辑，配置方式见前端 AI 配置页说明。

### 提示词国际化

`promptI18n.js` 管理所有提示词模板，支持中文（zh）和英文（en）两套模板，通过 `config.yaml` 中的 `language` 字段切换。

---

## 开发说明

### 添加新的 AI 服务商

1. 在 `imageClient.js` 或 `videoClient.js` 中添加新的 `provider` 分支
2. 实现对应的 API 调用逻辑
3. 在前端「AI 配置」页面新增服务商选项（`AIConfigContent.vue`）

### Jimeng AI API（自建即梦 OpenAI 兼容服务）

若使用「视频 → 厂商 **Jimeng AI API（自建即梦免费 API）**」：

1. 自行克隆并启动第三方即梦逆向/兼容服务项目（如 `jimeng-free-api-all`），按对方 README 安装依赖与 Chromium，默认监听端口以对方文档为准（常见 `8000`）。
2. 在本系统 AI 配置中填写 **Base URL**（如 `http://127.0.0.1:8000`）、**API Key** 填即梦 **Session**（多个用英文逗号分隔）。
3. 后端会请求对方 `POST /v1/videos/generations`（可用配置项 **Endpoint** 覆盖路径），Seedance 多图场景需分镜带参考图；返回为同步 `data[0].url`，无需轮询。

字段级对照可参考仓库 [`各大平台中转站配置/调用本地反向代理即梦freeapi的配置.png`](https://github.com/xuanyustudio/LocalMiniDrama/blob/main/%E5%90%84%E5%A4%A7%E5%B9%B3%E5%8F%B0%E4%B8%AD%E8%BD%AC%E7%AB%99%E9%85%8D%E7%BD%AE/%E8%B0%83%E7%94%A8%E6%9C%AC%E5%9C%B0%E5%8F%8D%E5%90%91%E4%BB%A3%E7%90%86%E5%8D%B3%E6%A2%A6freeapi%E7%9A%84%E9%85%8D%E7%BD%AE.png)。该路径为 **OpenAI 兼容的本地即梦代理（视频为主）**，与上文「即梦（Seedream）Volcengine 图生图与文生图」所描述的 **`volcengine` 直连中转图 API** 不是同一套协议，请按实际接入分别配置、勿混用字段。

### 添加新的数据库字段

1. 在 `migrate.js` 的 `ensureColumns()` 中添加新字段定义（类型 + 默认值）
2. 更新对应的 Service 文件中的 INSERT/SELECT 语句

### 日志级别

通过环境变量 `LOG_LEVEL` 控制：
```bash
LOG_LEVEL=debug npm run dev   # 详细日志
LOG_LEVEL=info  npm start     # 生产日志（默认）
```

---

[← 返回项目主页](../README.md)
