Merge remote-tracking branch 'upstream/master'
This commit is contained in:
@@ -26,6 +26,7 @@ backend
|
||||
|
||||
# 依赖
|
||||
node_modules/
|
||||
**/node_modules/
|
||||
vendor/
|
||||
|
||||
# 数据文件
|
||||
|
||||
67
.env.example
Normal file
67
.env.example
Normal file
@@ -0,0 +1,67 @@
|
||||
# ========================================
|
||||
# Docker 镜像源配置示例
|
||||
# ========================================
|
||||
#
|
||||
# 【使用方法】
|
||||
# 1. 复制此文件:cp .env.example .env
|
||||
# 2. 编辑 .env 文件,删掉下面 4 个配置行前面的 # 号:
|
||||
# - DOCKER_REGISTRY
|
||||
# - NPM_REGISTRY
|
||||
# - GO_PROXY
|
||||
# - ALPINE_MIRROR
|
||||
# 3. 保存后运行:docker-compose build
|
||||
#
|
||||
# 【注意】
|
||||
# - 只删除配置行前面的 #,不要删除注释说明的 #
|
||||
# - 国外用户请勿配置,使用默认官方源即可
|
||||
#
|
||||
# ========================================
|
||||
|
||||
# -----------------------------
|
||||
# Docker Hub 镜像源(末尾必须有斜杠 /)
|
||||
# -----------------------------
|
||||
#
|
||||
# 推荐镜像源:
|
||||
# docker.1ms.run/ - 速度快,推荐
|
||||
# dockerpull.org/ - 备用
|
||||
# dockerproxy.cn/ - 备用
|
||||
#
|
||||
# 删除下面行首的 # 来启用:
|
||||
# DOCKER_REGISTRY=docker.1ms.run/
|
||||
|
||||
|
||||
# -----------------------------
|
||||
# npm 镜像源(末尾斜杠可选)
|
||||
# -----------------------------
|
||||
#
|
||||
# 推荐镜像源:
|
||||
# https://registry.npmmirror.com/ - 淘宝镜像,推荐
|
||||
#
|
||||
# 删除下面行首的 # 来启用:
|
||||
# NPM_REGISTRY=https://registry.npmmirror.com/
|
||||
|
||||
|
||||
# -----------------------------
|
||||
# Go 模块代理(注意末尾不要斜杠)
|
||||
# -----------------------------
|
||||
#
|
||||
# 推荐镜像源:
|
||||
# https://goproxy.cn,direct - 七牛云,推荐
|
||||
# https://goproxy.io,direct - 备用
|
||||
# https://mirrors.aliyun.com/goproxy/,direct - 阿里云
|
||||
#
|
||||
# 删除下面行首的 # 来启用:
|
||||
# GO_PROXY=https://goproxy.cn,direct
|
||||
|
||||
|
||||
# -----------------------------
|
||||
# Alpine apk 镜像源(末尾不要斜杠)
|
||||
# -----------------------------
|
||||
#
|
||||
# 推荐镜像源:
|
||||
# mirrors.aliyun.com - 阿里云,推荐
|
||||
# mirrors.tuna.tsinghua.edu.cn - 清华大学
|
||||
# mirrors.ustc.edu.cn - 中科大
|
||||
#
|
||||
# 删除下面行首的 # 来启用:
|
||||
# ALPINE_MIRROR=mirrors.aliyun.com
|
||||
54
Dockerfile
54
Dockerfile
@@ -1,10 +1,20 @@
|
||||
# 多阶段构建 Dockerfile for Huobao Drama
|
||||
|
||||
# ==================== 阶段1: 构建前端 ====================
|
||||
FROM node:20-alpine AS frontend-builder
|
||||
# 声明构建参数(支持镜像源配置)
|
||||
ARG DOCKER_REGISTRY=
|
||||
ARG NPM_REGISTRY=
|
||||
|
||||
# 配置 npm 镜像源(国内加速)
|
||||
RUN npm config set registry https://registry.npmmirror.com
|
||||
FROM ${DOCKER_REGISTRY:-}node:20-alpine AS frontend-builder
|
||||
|
||||
# 重新声明 ARG(FROM 之后 ARG 作用域失效,需要重新声明)
|
||||
ARG NPM_REGISTRY=
|
||||
|
||||
# 配置 npm 镜像源(条件执行)
|
||||
ENV NPM_REGISTRY=${NPM_REGISTRY:-}
|
||||
RUN if [ -n "$NPM_REGISTRY" ]; then \
|
||||
npm config set registry "$NPM_REGISTRY" || true; \
|
||||
fi
|
||||
|
||||
WORKDIR /app/web
|
||||
|
||||
@@ -21,11 +31,26 @@ COPY web/ ./
|
||||
RUN npm run build
|
||||
|
||||
# ==================== 阶段2: 构建后端 ====================
|
||||
FROM golang:1.23-alpine AS backend-builder
|
||||
# 每个阶段前重新声明构建参数
|
||||
ARG DOCKER_REGISTRY=
|
||||
ARG GO_PROXY=
|
||||
ARG ALPINE_MIRROR=
|
||||
|
||||
# 配置 Go 代理(国内镜像加速)
|
||||
ENV GOPROXY=https://goproxy.cn,direct \
|
||||
GO111MODULE=on
|
||||
FROM ${DOCKER_REGISTRY:-}golang:1.23-alpine AS backend-builder
|
||||
|
||||
# 重新声明 ARG(FROM 之后 ARG 作用域失效,需要重新声明)
|
||||
ARG GO_PROXY=
|
||||
ARG ALPINE_MIRROR=
|
||||
|
||||
# 配置 Alpine 镜像源(条件执行)
|
||||
ENV ALPINE_MIRROR=${ALPINE_MIRROR:-}
|
||||
RUN if [ -n "$ALPINE_MIRROR" ]; then \
|
||||
sed -i "s@dl-cdn.alpinelinux.org@$ALPINE_MIRROR@g" /etc/apk/repositories 2>/dev/null || true; \
|
||||
fi
|
||||
|
||||
# 配置 Go 代理(使用 ENV 持久化到运行时)
|
||||
ENV GOPROXY=${GO_PROXY:-https://proxy.golang.org,direct}
|
||||
ENV GO111MODULE=on
|
||||
|
||||
# 安装必要的构建工具(纯 Go 编译,无需 CGO)
|
||||
RUN apk add --no-cache \
|
||||
@@ -51,7 +76,20 @@ COPY --from=frontend-builder /app/web/dist ./web/dist
|
||||
RUN CGO_ENABLED=0 go build -ldflags="-w -s" -o huobao-drama .
|
||||
|
||||
# ==================== 阶段3: 运行时镜像 ====================
|
||||
FROM alpine:latest
|
||||
# 每个阶段前重新声明构建参数
|
||||
ARG DOCKER_REGISTRY=
|
||||
ARG ALPINE_MIRROR=
|
||||
|
||||
FROM ${DOCKER_REGISTRY:-}alpine:latest
|
||||
|
||||
# 重新声明 ARG(FROM 之后 ARG 作用域失效,需要重新声明)
|
||||
ARG ALPINE_MIRROR=
|
||||
|
||||
# 配置 Alpine 镜像源(条件执行)
|
||||
ENV ALPINE_MIRROR=${ALPINE_MIRROR:-}
|
||||
RUN if [ -n "$ALPINE_MIRROR" ]; then \
|
||||
sed -i "s@dl-cdn.alpinelinux.org@$ALPINE_MIRROR@g" /etc/apk/repositories 2>/dev/null || true; \
|
||||
fi
|
||||
|
||||
# 安装运行时依赖
|
||||
RUN apk add --no-cache \
|
||||
|
||||
624
README-CN.md
Normal file
624
README-CN.md
Normal file
@@ -0,0 +1,624 @@
|
||||
# 🎬 Huobao Drama - AI 短剧生成平台
|
||||
|
||||
<div align="center">
|
||||
|
||||
**基于 Go + Vue3 的全栈 AI 短剧自动化生产平台**
|
||||
|
||||
[](https://golang.org)
|
||||
[](https://vuejs.org)
|
||||
[](https://creativecommons.org/licenses/by-nc-sa/4.0/)
|
||||
|
||||
[功能特性](#功能特性) • [快速开始](#快速开始) • [部署指南](#部署指南)
|
||||
|
||||
[简体中文](README-CN.md) | [English](README.md) | [日本語](README-JA.md)
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## 📖 项目简介
|
||||
|
||||
Huobao Drama 是一个基于 AI 的短剧自动化生产平台,实现从剧本生成、角色设计、分镜制作到视频合成的全流程自动化。
|
||||
|
||||
### 🎯 核心价值
|
||||
|
||||
- **🤖 AI 驱动**:使用大语言模型解析剧本,提取角色、场景和分镜信息
|
||||
- **🎨 智能创作**:AI 绘图生成角色形象和场景背景
|
||||
- **📹 视频生成**:基于文生视频和图生视频模型自动生成分镜视频
|
||||
- **🔄 工作流**:完整的短剧制作工作流,从创意到成片一站式完成
|
||||
|
||||
### 🛠️ 技术架构
|
||||
|
||||
采用**DDD 领域驱动设计**,清晰分层:
|
||||
|
||||
```
|
||||
├── API层 (Gin HTTP)
|
||||
├── 应用服务层 (Business Logic)
|
||||
├── 领域层 (Domain Models)
|
||||
└── 基础设施层 (Database, External Services)
|
||||
```
|
||||
|
||||
### 🎥 作品展示 / Demo Videos
|
||||
|
||||
体验 AI 短剧生成效果:
|
||||
|
||||
<div align="center">
|
||||
|
||||
**示例作品 1**
|
||||
|
||||
<video src="https://ffile.chatfire.site/cf/public/20260114094337396.mp4" controls width="640"></video>
|
||||
|
||||
**示例作品 2**
|
||||
|
||||
<video src="https://ffile.chatfire.site/cf/public/fcede75e8aeafe22031dbf78f86285b8.mp4" controls width="640"></video>
|
||||
|
||||
[点击观看视频 1](https://ffile.chatfire.site/cf/public/20260114094337396.mp4) | [点击观看视频 2](https://ffile.chatfire.site/cf/public/fcede75e8aeafe22031dbf78f86285b8.mp4)
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## ✨ 功能特性
|
||||
|
||||
### 🎭 角色管理
|
||||
|
||||
- ✅ AI 生成角色形象
|
||||
- ✅ 批量角色生成
|
||||
- ✅ 角色图片上传和管理
|
||||
|
||||
### 🎬 分镜制作
|
||||
|
||||
- ✅ 自动生成分镜脚本
|
||||
- ✅ 场景描述和镜头设计
|
||||
- ✅ 分镜图片生成(文生图)
|
||||
- ✅ 帧类型选择(首帧/关键帧/尾帧/分镜板)
|
||||
|
||||
### 🎥 视频生成
|
||||
|
||||
- ✅ 图生视频自动生成
|
||||
- ✅ 视频合成和剪辑
|
||||
- ✅ 转场效果
|
||||
|
||||
### 📦 资源管理
|
||||
|
||||
- ✅ 素材库统一管理
|
||||
- ✅ 本地存储支持
|
||||
- ✅ 资源导入导出
|
||||
- ✅ 任务进度追踪
|
||||
|
||||
---
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
### 📋 环境要求
|
||||
|
||||
| 软件 | 版本要求 | 说明 |
|
||||
| ----------- | -------- | -------------------- |
|
||||
| **Go** | 1.23+ | 后端运行环境 |
|
||||
| **Node.js** | 18+ | 前端构建环境 |
|
||||
| **npm** | 9+ | 包管理工具 |
|
||||
| **FFmpeg** | 4.0+ | 视频处理(**必需**) |
|
||||
| **SQLite** | 3.x | 数据库(已内置) |
|
||||
|
||||
#### 安装 FFmpeg
|
||||
|
||||
**macOS:**
|
||||
|
||||
```bash
|
||||
brew install ffmpeg
|
||||
```
|
||||
|
||||
**Ubuntu/Debian:**
|
||||
|
||||
```bash
|
||||
sudo apt update
|
||||
sudo apt install ffmpeg
|
||||
```
|
||||
|
||||
**Windows:**
|
||||
从 [FFmpeg 官网](https://ffmpeg.org/download.html) 下载并配置环境变量
|
||||
|
||||
验证安装:
|
||||
|
||||
```bash
|
||||
ffmpeg -version
|
||||
```
|
||||
|
||||
### ⚙️ 配置文件
|
||||
|
||||
复制并编辑配置文件:
|
||||
|
||||
```bash
|
||||
cp configs/config.example.yaml configs/config.yaml
|
||||
vim configs/config.yaml
|
||||
```
|
||||
|
||||
配置文件格式(`configs/config.yaml`):
|
||||
|
||||
```yaml
|
||||
app:
|
||||
name: "Huobao Drama API"
|
||||
version: "1.0.0"
|
||||
debug: true # 开发环境设为true,生产环境设为false
|
||||
|
||||
server:
|
||||
port: 5678
|
||||
host: "0.0.0.0"
|
||||
cors_origins:
|
||||
- "http://localhost:3012"
|
||||
read_timeout: 600
|
||||
write_timeout: 600
|
||||
|
||||
database:
|
||||
type: "sqlite"
|
||||
path: "./data/drama_generator.db"
|
||||
max_idle: 10
|
||||
max_open: 100
|
||||
|
||||
storage:
|
||||
type: "local"
|
||||
local_path: "./data/storage"
|
||||
base_url: "http://localhost:5678/static"
|
||||
|
||||
ai:
|
||||
default_text_provider: "openai"
|
||||
default_image_provider: "openai"
|
||||
default_video_provider: "doubao"
|
||||
```
|
||||
|
||||
**重要配置项:**
|
||||
|
||||
- `app.debug`: 调试模式开关(开发环境建议设为 true)
|
||||
- `server.port`: 服务运行端口
|
||||
- `server.cors_origins`: 允许跨域访问的前端地址
|
||||
- `database.path`: SQLite 数据库文件路径
|
||||
- `storage.local_path`: 本地文件存储路径
|
||||
- `storage.base_url`: 静态资源访问 URL
|
||||
- `ai.default_*_provider`: AI 服务提供商配置(在 Web 界面中配置具体的 API Key)
|
||||
|
||||
### 📥 安装依赖
|
||||
|
||||
```bash
|
||||
# 克隆项目
|
||||
git clone https://github.com/chatfire-AI/huobao-drama.git
|
||||
cd huobao-drama
|
||||
|
||||
# 安装Go依赖
|
||||
go mod download
|
||||
|
||||
# 安装前端依赖
|
||||
cd web
|
||||
npm install
|
||||
cd ..
|
||||
```
|
||||
|
||||
### 🎯 启动项目
|
||||
|
||||
#### 方式一:开发模式(推荐)
|
||||
|
||||
**前后端分离,支持热重载**
|
||||
|
||||
```bash
|
||||
# 终端1:启动后端服务
|
||||
go run main.go
|
||||
|
||||
# 终端2:启动前端开发服务器
|
||||
cd web
|
||||
npm run dev
|
||||
```
|
||||
|
||||
- 前端地址: `http://localhost:3012`
|
||||
- 后端 API: `http://localhost:5678/api/v1`
|
||||
- 前端自动代理 API 请求到后端
|
||||
|
||||
#### 方式二:单服务模式
|
||||
|
||||
**后端同时提供 API 和前端静态文件**
|
||||
|
||||
```bash
|
||||
# 1. 构建前端
|
||||
cd web
|
||||
npm run build
|
||||
cd ..
|
||||
|
||||
# 2. 启动服务
|
||||
go run main.go
|
||||
```
|
||||
|
||||
访问: `http://localhost:5678`
|
||||
|
||||
### 🗄️ 数据库初始化
|
||||
|
||||
数据库表会在首次启动时自动创建(使用 GORM AutoMigrate),无需手动迁移。
|
||||
|
||||
---
|
||||
|
||||
## 📦 部署指南
|
||||
|
||||
### 🐳 Docker 部署(推荐)
|
||||
|
||||
#### 方式一:Docker Compose(推荐)
|
||||
|
||||
#### 🚀 国内网络加速(可选)
|
||||
|
||||
如果您在国内网络环境下,Docker 拉取镜像和安装依赖可能较慢。可以通过配置镜像源加速构建过程。
|
||||
|
||||
**步骤 1:创建环境变量文件**
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
**步骤 2:编辑 `.env` 文件,取消注释需要的镜像源**
|
||||
|
||||
```bash
|
||||
# 启用 Docker Hub 镜像(推荐)
|
||||
DOCKER_REGISTRY=docker.1ms.run/
|
||||
|
||||
# 启用 npm 镜像
|
||||
NPM_REGISTRY=https://registry.npmmirror.com/
|
||||
|
||||
# 启用 Go 代理
|
||||
GO_PROXY=https://goproxy.cn,direct
|
||||
|
||||
# 启用 Alpine 镜像
|
||||
ALPINE_MIRROR=mirrors.aliyun.com
|
||||
```
|
||||
|
||||
**步骤 3:使用 docker compose 构建(必须)**
|
||||
|
||||
```bash
|
||||
docker compose build
|
||||
```
|
||||
|
||||
> **重要说明**:
|
||||
>
|
||||
> - ⚠️ 必须使用 `docker compose build` 才能自动加载 `.env` 文件中的镜像源配置
|
||||
> - ❌ 如果使用 `docker build` 命令,需要手动传递 `--build-arg` 参数
|
||||
> - ✅ 推荐始终使用 `docker compose build` 进行构建
|
||||
|
||||
**效果对比**:
|
||||
|
||||
| 操作 | 不配置镜像源 | 配置镜像源后 |
|
||||
| ------------- | ------------ | ------------ |
|
||||
| 拉取基础镜像 | 5-30 分钟 | 1-5 分钟 |
|
||||
| 安装 npm 依赖 | 可能失败 | 快速成功 |
|
||||
| 下载 Go 依赖 | 5-10 分钟 | 30 秒-1 分钟 |
|
||||
|
||||
> **注意**:国外用户请勿配置镜像源,使用默认配置即可。
|
||||
|
||||
```bash
|
||||
# 启动服务
|
||||
docker-compose up -d
|
||||
|
||||
# 查看日志
|
||||
docker-compose logs -f
|
||||
|
||||
# 停止服务
|
||||
docker-compose down
|
||||
```
|
||||
|
||||
#### 方式二:Docker 命令
|
||||
|
||||
> **注意**:Linux 用户需添加 `--add-host=host.docker.internal:host-gateway` 以访问宿主机服务
|
||||
|
||||
```bash
|
||||
# 从 Docker Hub 运行
|
||||
docker run -d \
|
||||
--name huobao-drama \
|
||||
-p 5678:5678 \
|
||||
-v $(pwd)/data:/app/data \
|
||||
--restart unless-stopped \
|
||||
huobao/huobao-drama:latest
|
||||
|
||||
# 查看日志
|
||||
docker logs -f huobao-drama
|
||||
```
|
||||
|
||||
**本地构建**(可选):
|
||||
|
||||
```bash
|
||||
docker build -t huobao-drama:latest .
|
||||
docker run -d --name huobao-drama -p 5678:5678 -v $(pwd)/data:/app/data huobao-drama:latest
|
||||
```
|
||||
|
||||
**Docker 部署优势:**
|
||||
|
||||
- ✅ 开箱即用,内置默认配置
|
||||
- ✅ 环境一致性,避免依赖问题
|
||||
- ✅ 一键启动,无需安装 Go、Node.js、FFmpeg
|
||||
- ✅ 易于迁移和扩展
|
||||
- ✅ 自动健康检查和重启
|
||||
- ✅ 自动处理文件权限,无需手动配置
|
||||
|
||||
#### 🔗 访问宿主机服务(Ollama/本地模型)
|
||||
|
||||
容器已配置支持访问宿主机服务,直接使用 `http://host.docker.internal:端口号` 即可。
|
||||
|
||||
**配置步骤:**
|
||||
|
||||
1. **宿主机启动服务(监听所有接口)**
|
||||
|
||||
```bash
|
||||
export OLLAMA_HOST=0.0.0.0:11434 && ollama serve
|
||||
```
|
||||
|
||||
2. **前端 AI 服务配置**
|
||||
- Base URL: `http://host.docker.internal:11434/v1`
|
||||
- Provider: `openai`
|
||||
- Model: `qwen2.5:latest`
|
||||
|
||||
---
|
||||
|
||||
### 🏭 传统部署方式
|
||||
|
||||
#### 1. 编译构建
|
||||
|
||||
```bash
|
||||
# 1. 构建前端
|
||||
cd web
|
||||
npm run build
|
||||
cd ..
|
||||
|
||||
# 2. 编译后端
|
||||
go build -o huobao-drama .
|
||||
```
|
||||
|
||||
生成文件:
|
||||
|
||||
- `huobao-drama` - 后端可执行文件
|
||||
- `web/dist/` - 前端静态文件(已嵌入后端)
|
||||
|
||||
#### 2. 准备部署文件
|
||||
|
||||
需要上传到服务器的文件:
|
||||
|
||||
```
|
||||
huobao-drama # 后端可执行文件
|
||||
configs/config.yaml # 配置文件
|
||||
data/ # 数据目录(可选,首次运行自动创建)
|
||||
```
|
||||
|
||||
#### 3. 服务器配置
|
||||
|
||||
```bash
|
||||
# 上传文件到服务器
|
||||
scp huobao-drama user@server:/opt/huobao-drama/
|
||||
scp configs/config.yaml user@server:/opt/huobao-drama/configs/
|
||||
|
||||
# SSH登录服务器
|
||||
ssh user@server
|
||||
|
||||
# 修改配置文件
|
||||
cd /opt/huobao-drama
|
||||
vim configs/config.yaml
|
||||
# 设置mode为production
|
||||
# 配置域名和存储路径
|
||||
|
||||
# 创建数据目录并设置权限(重要!)
|
||||
# 注意:将 YOUR_USER 替换为实际运行服务的用户名(如 www-data、ubuntu、deploy 等)
|
||||
sudo mkdir -p /opt/huobao-drama/data/storage
|
||||
sudo chown -R YOUR_USER:YOUR_USER /opt/huobao-drama/data
|
||||
sudo chmod -R 755 /opt/huobao-drama/data
|
||||
|
||||
# 赋予执行权限
|
||||
chmod +x huobao-drama
|
||||
|
||||
# 启动服务
|
||||
./huobao-drama
|
||||
```
|
||||
|
||||
#### 4. 使用 systemd 管理服务
|
||||
|
||||
创建服务文件 `/etc/systemd/system/huobao-drama.service`:
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=Huobao Drama Service
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=YOUR_USER
|
||||
WorkingDirectory=/opt/huobao-drama
|
||||
ExecStart=/opt/huobao-drama/huobao-drama
|
||||
Restart=on-failure
|
||||
RestartSec=10
|
||||
|
||||
# 环境变量(可选)
|
||||
# Environment="GIN_MODE=release"
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
启动服务:
|
||||
|
||||
```bash
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable huobao-drama
|
||||
sudo systemctl start huobao-drama
|
||||
sudo systemctl status huobao-drama
|
||||
```
|
||||
|
||||
**⚠️ 常见问题:SQLite 写权限错误**
|
||||
|
||||
如果遇到 `attempt to write a readonly database` 错误:
|
||||
|
||||
```bash
|
||||
# 1. 确认当前运行服务的用户
|
||||
sudo systemctl status huobao-drama | grep "Main PID"
|
||||
ps aux | grep huobao-drama
|
||||
|
||||
# 2. 修复权限(将 YOUR_USER 替换为实际用户名)
|
||||
sudo chown -R YOUR_USER:YOUR_USER /opt/huobao-drama/data
|
||||
sudo chmod -R 755 /opt/huobao-drama/data
|
||||
|
||||
# 3. 验证权限
|
||||
ls -la /opt/huobao-drama/data
|
||||
# 应该显示所有者为运行服务的用户
|
||||
|
||||
# 4. 重启服务
|
||||
sudo systemctl restart huobao-drama
|
||||
```
|
||||
|
||||
**原因说明**:
|
||||
|
||||
- SQLite 需要对数据库文件 **和** 所在目录都有写权限
|
||||
- 需要在目录中创建临时文件(如 `-wal`、`-journal`)
|
||||
- **关键**:确保 systemd 配置中的 `User` 与数据目录所有者一致
|
||||
|
||||
**常用用户名**:
|
||||
|
||||
- Ubuntu/Debian: `www-data`、`ubuntu`
|
||||
- CentOS/RHEL: `nginx`、`apache`
|
||||
- 自定义部署: `deploy`、`app`、当前登录用户
|
||||
|
||||
#### 5. Nginx 反向代理
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name your-domain.com;
|
||||
|
||||
location / {
|
||||
proxy_pass http://localhost:5678;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
}
|
||||
|
||||
# 静态文件直接访问
|
||||
location /static/ {
|
||||
alias /opt/huobao-drama/data/storage/;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎨 技术栈
|
||||
|
||||
### 后端技术
|
||||
|
||||
- **语言**: Go 1.23+
|
||||
- **Web 框架**: Gin 1.9+
|
||||
- **ORM**: GORM
|
||||
- **数据库**: SQLite
|
||||
- **日志**: Zap
|
||||
- **视频处理**: FFmpeg
|
||||
- **AI 服务**: OpenAI、Gemini、火山等
|
||||
|
||||
### 前端技术
|
||||
|
||||
- **框架**: Vue 3.4+
|
||||
- **语言**: TypeScript 5+
|
||||
- **构建工具**: Vite 5
|
||||
- **UI 组件**: Element Plus
|
||||
- **CSS 框架**: TailwindCSS
|
||||
- **状态管理**: Pinia
|
||||
- **路由**: Vue Router 4
|
||||
|
||||
### 开发工具
|
||||
|
||||
- **包管理**: Go Modules, npm
|
||||
- **代码规范**: ESLint, Prettier
|
||||
- **版本控制**: Git
|
||||
|
||||
---
|
||||
|
||||
## 📝 常见问题
|
||||
|
||||
### Q: Docker 容器如何访问宿主机的 Ollama?
|
||||
|
||||
A: 使用 `http://host.docker.internal:11434/v1` 作为 Base URL。注意两点:
|
||||
|
||||
1. 宿主机 Ollama 需监听 `0.0.0.0`:`export OLLAMA_HOST=0.0.0.0:11434 && ollama serve`
|
||||
2. Linux 用户使用 `docker run` 需添加:`--add-host=host.docker.internal:host-gateway`
|
||||
|
||||
详见:[DOCKER_HOST_ACCESS.md](docs/DOCKER_HOST_ACCESS.md)
|
||||
|
||||
### Q: FFmpeg 未安装或找不到?
|
||||
|
||||
A: 确保 FFmpeg 已安装并在 PATH 环境变量中。运行 `ffmpeg -version` 验证。
|
||||
|
||||
### Q: 前端无法连接后端 API?
|
||||
|
||||
A: 检查后端是否启动,端口是否正确。开发模式下前端代理配置在 `web/vite.config.ts`。
|
||||
|
||||
### Q: 数据库表未创建?
|
||||
|
||||
A: GORM 会在首次启动时自动创建表,检查日志确认迁移是否成功。
|
||||
|
||||
---
|
||||
|
||||
## <20> 更新日志 / Changelog
|
||||
|
||||
### v1.0.2 (2026-01-16)
|
||||
|
||||
#### 🚀 重大更新
|
||||
|
||||
- SQLite 纯 Go 驱动(`modernc.org/sqlite`),支持 `CGO_ENABLED=0` 跨平台编译
|
||||
- 优化并发性能(WAL 模式),解决 "database is locked" 错误
|
||||
- Docker 跨平台支持 `host.docker.internal` 访问宿主机服务
|
||||
- 精简文档和部署指南
|
||||
|
||||
### v1.0.1 (2026-01-14)
|
||||
|
||||
#### 🐛 Bug Fixes / 🔧 Improvements
|
||||
|
||||
- 修复视频生成 API 响应解析问题
|
||||
- 添加 OpenAI Sora 视频端点配置
|
||||
- 优化错误处理和日志输出
|
||||
|
||||
---
|
||||
|
||||
## 🤝 贡献指南
|
||||
|
||||
欢迎提交 Issue 和 Pull Request!
|
||||
|
||||
1. Fork 本项目
|
||||
2. 创建特性分支 (`git checkout -b feature/AmazingFeature`)
|
||||
3. 提交改动 (`git commit -m 'Add some AmazingFeature'`)
|
||||
4. 推送到分支 (`git push origin feature/AmazingFeature`)
|
||||
5. 开启 Pull Request
|
||||
|
||||
---
|
||||
|
||||
## API 配置站点
|
||||
|
||||
2 分钟完成配置:[API 聚合站点](https://api.chatfire.site/models)
|
||||
|
||||
---
|
||||
|
||||
## 👨💻 关于我们
|
||||
|
||||
**AI 火宝 - AI 工作室创业中**
|
||||
|
||||
- 🏠 **位置**: 中国南京
|
||||
- 🚀 **状态**: 创业中
|
||||
- 📧 **Email**: [18550175439@163.com](mailto:18550175439@163.com)
|
||||
- 💬 **微信**: dangbao1117 (私人微信不解答任何技术问题)
|
||||
- 🐙 **GitHub**: [https://github.com/chatfire-AI/huobao-drama](https://github.com/chatfire-AI/huobao-drama)
|
||||
|
||||
> _"让 AI 帮我们做更有创造力的事"_
|
||||
|
||||
## 项目交流群
|
||||
|
||||

|
||||
|
||||
- 提交 [Issue](../../issues)
|
||||
- 发送邮件至项目维护者
|
||||
|
||||
---
|
||||
|
||||
<div align="center">
|
||||
|
||||
**⭐ 如果这个项目对你有帮助,请给一个 Star!**
|
||||
|
||||
## Star History
|
||||
|
||||
[](https://www.star-history.com/#chatfire-AI/huobao-drama&type=date&legend=top-left)
|
||||
Made with ❤️ by Huobao Team
|
||||
|
||||
</div>
|
||||
537
README-EN.md
537
README-EN.md
@@ -1,537 +0,0 @@
|
||||
# 🎬 Huobao Drama - AI Short Drama Production Platform
|
||||
|
||||
<div align="center">
|
||||
|
||||
**Full-stack AI Short Drama Automation Platform Based on Go + Vue3**
|
||||
|
||||
[](https://golang.org)
|
||||
[](https://vuejs.org)
|
||||
[](https://creativecommons.org/licenses/by-nc-sa/4.0/)
|
||||
|
||||
[Features](#features) • [Quick Start](#quick-start) • [Deployment](#deployment)
|
||||
|
||||
[简体中文](README.md) | [English](README-EN.md)
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## 📖 About
|
||||
|
||||
Huobao Drama is an AI-powered short drama production platform that automates the entire workflow from script generation, character design, storyboarding to video composition.
|
||||
|
||||
### 🎯 Core Features
|
||||
|
||||
- **🤖 AI-Driven**: Parse scripts using large language models to extract characters, scenes, and storyboards
|
||||
- **🎨 Intelligent Creation**: AI-generated character portraits and scene backgrounds
|
||||
- **📹 Video Generation**: Automatic storyboard video generation using text-to-video and image-to-video models
|
||||
- **🔄 Complete Workflow**: End-to-end production workflow from idea to final video
|
||||
|
||||
### 🛠️ Technical Architecture
|
||||
|
||||
Based on **DDD (Domain-Driven Design)** with clear layering:
|
||||
|
||||
```
|
||||
├── API Layer (Gin HTTP)
|
||||
├── Application Service Layer (Business Logic)
|
||||
├── Domain Layer (Domain Models)
|
||||
└── Infrastructure Layer (Database, External Services)
|
||||
```
|
||||
|
||||
### 🎥 Demo Videos
|
||||
|
||||
Experience AI short drama generation:
|
||||
|
||||
<div align="center">
|
||||
|
||||
**Sample Work 1**
|
||||
|
||||
<video src="https://ffile.chatfire.site/cf/public/20260114094337396.mp4" controls width="640"></video>
|
||||
|
||||
**Sample Work 2**
|
||||
|
||||
<video src="https://ffile.chatfire.site/cf/public/fcede75e8aeafe22031dbf78f86285b8.mp4" controls width="640"></video>
|
||||
|
||||
[Watch Video 1](https://ffile.chatfire.site/cf/public/20260114094337396.mp4) | [Watch Video 2](https://ffile.chatfire.site/cf/public/fcede75e8aeafe22031dbf78f86285b8.mp4)
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## ✨ Features
|
||||
|
||||
### 🎭 Character Management
|
||||
- ✅ AI-generated character portraits
|
||||
- ✅ Batch character generation
|
||||
- ✅ Character image upload and management
|
||||
|
||||
### 🎬 Storyboard Production
|
||||
- ✅ Automatic storyboard script generation
|
||||
- ✅ Scene descriptions and shot design
|
||||
- ✅ Storyboard image generation (text-to-image)
|
||||
- ✅ Frame type selection (first frame/key frame/last frame/panel)
|
||||
|
||||
### 🎥 Video Generation
|
||||
- ✅ Automatic image-to-video generation
|
||||
- ✅ Video composition and editing
|
||||
- ✅ Transition effects
|
||||
|
||||
### 📦 Asset Management
|
||||
- ✅ Unified asset library management
|
||||
- ✅ Local storage support
|
||||
- ✅ Asset import/export
|
||||
- ✅ Task progress tracking
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Quick Start
|
||||
|
||||
### 📋 Prerequisites
|
||||
|
||||
| Software | Version | Description |
|
||||
|----------|---------|-------------|
|
||||
| **Go** | 1.23+ | Backend runtime |
|
||||
| **Node.js** | 18+ | Frontend build environment |
|
||||
| **npm** | 9+ | Package manager |
|
||||
| **FFmpeg** | 4.0+ | Video processing (**Required**) |
|
||||
| **SQLite** | 3.x | Database (built-in) |
|
||||
|
||||
#### Installing FFmpeg
|
||||
|
||||
**macOS:**
|
||||
```bash
|
||||
brew install ffmpeg
|
||||
```
|
||||
|
||||
**Ubuntu/Debian:**
|
||||
```bash
|
||||
sudo apt update
|
||||
sudo apt install ffmpeg
|
||||
```
|
||||
|
||||
**Windows:**
|
||||
Download from [FFmpeg Official Site](https://ffmpeg.org/download.html) and configure environment variables
|
||||
|
||||
Verify installation:
|
||||
```bash
|
||||
ffmpeg -version
|
||||
```
|
||||
|
||||
### ⚙️ Configuration
|
||||
|
||||
Copy and edit the configuration file:
|
||||
|
||||
```bash
|
||||
cp configs/config.example.yaml configs/config.yaml
|
||||
vim configs/config.yaml
|
||||
```
|
||||
|
||||
Configuration file format (`configs/config.yaml`):
|
||||
|
||||
```yaml
|
||||
app:
|
||||
name: "Huobao Drama API"
|
||||
version: "1.0.0"
|
||||
debug: true # Set to true for development, false for production
|
||||
|
||||
server:
|
||||
port: 5678
|
||||
host: "0.0.0.0"
|
||||
cors_origins:
|
||||
- "http://localhost:3012"
|
||||
read_timeout: 600
|
||||
write_timeout: 600
|
||||
|
||||
database:
|
||||
type: "sqlite"
|
||||
path: "./data/drama_generator.db"
|
||||
max_idle: 10
|
||||
max_open: 100
|
||||
|
||||
storage:
|
||||
type: "local"
|
||||
local_path: "./data/storage"
|
||||
base_url: "http://localhost:5678/static"
|
||||
|
||||
ai:
|
||||
default_text_provider: "openai"
|
||||
default_image_provider: "openai"
|
||||
default_video_provider: "doubao"
|
||||
```
|
||||
|
||||
**Key Configuration Items:**
|
||||
- `app.debug`: Debug mode switch (recommended true for development)
|
||||
- `server.port`: Service port
|
||||
- `server.cors_origins`: Allowed CORS origins for frontend
|
||||
- `database.path`: SQLite database file path
|
||||
- `storage.local_path`: Local file storage path
|
||||
- `storage.base_url`: Static resource access URL
|
||||
- `ai.default_*_provider`: AI service provider configuration (API keys configured in Web UI)
|
||||
|
||||
### 📥 Installation
|
||||
|
||||
```bash
|
||||
# Clone the project
|
||||
git clone https://github.com/chatfire-AI/huobao-drama.git
|
||||
cd huobao-drama
|
||||
|
||||
# Install Go dependencies
|
||||
go mod download
|
||||
|
||||
# Install frontend dependencies
|
||||
cd web
|
||||
npm install
|
||||
cd ..
|
||||
```
|
||||
|
||||
### 🎯 Starting the Project
|
||||
|
||||
#### Method 1: Development Mode (Recommended)
|
||||
|
||||
**Frontend and backend separation with hot reload**
|
||||
|
||||
```bash
|
||||
# Terminal 1: Start backend service
|
||||
go run main.go
|
||||
|
||||
# Terminal 2: Start frontend dev server
|
||||
cd web
|
||||
npm run dev
|
||||
```
|
||||
|
||||
- Frontend: `http://localhost:3012`
|
||||
- Backend API: `http://localhost:5678/api/v1`
|
||||
- Frontend automatically proxies API requests to backend
|
||||
|
||||
#### Method 2: Single Service Mode
|
||||
|
||||
**Backend serves both API and frontend static files**
|
||||
|
||||
```bash
|
||||
# 1. Build frontend
|
||||
cd web
|
||||
npm run build
|
||||
cd ..
|
||||
|
||||
# 2. Start service
|
||||
go run main.go
|
||||
```
|
||||
|
||||
Access: `http://localhost:5678`
|
||||
|
||||
### 🗄️ Database Initialization
|
||||
|
||||
Database tables are automatically created on first startup (using GORM AutoMigrate), no manual migration needed.
|
||||
|
||||
---
|
||||
|
||||
## 📦 Deployment
|
||||
|
||||
### 🐳 Docker Deployment (Recommended)
|
||||
|
||||
#### Method 1: Docker Compose (Recommended)
|
||||
|
||||
```bash
|
||||
# Start services
|
||||
docker-compose up -d
|
||||
|
||||
# View logs
|
||||
docker-compose logs -f
|
||||
|
||||
# Stop services
|
||||
docker-compose down
|
||||
```
|
||||
|
||||
#### Method 2: Docker Command
|
||||
|
||||
> **Note**: Linux users need to add `--add-host=host.docker.internal:host-gateway` to access host services
|
||||
|
||||
```bash
|
||||
# Run from Docker Hub
|
||||
docker run -d \
|
||||
--name huobao-drama \
|
||||
-p 5678:5678 \
|
||||
-v $(pwd)/data:/app/data \
|
||||
--restart unless-stopped \
|
||||
huobao/huobao-drama:latest
|
||||
|
||||
# View logs
|
||||
docker logs -f huobao-drama
|
||||
```
|
||||
|
||||
**Local Build** (optional):
|
||||
```bash
|
||||
docker build -t huobao-drama:latest .
|
||||
docker run -d --name huobao-drama -p 5678:5678 -v $(pwd)/data:/app/data huobao-drama:latest
|
||||
```
|
||||
|
||||
**Docker Deployment Advantages:**
|
||||
- ✅ Ready to use with default configuration
|
||||
- ✅ Environment consistency, avoiding dependency issues
|
||||
- ✅ One-click start, no need to install Go, Node.js, FFmpeg
|
||||
- ✅ Easy to migrate and scale
|
||||
- ✅ Automatic health checks and restarts
|
||||
- ✅ Automatic file permission handling
|
||||
|
||||
#### 🔗 Accessing Host Services (Ollama/Local Models)
|
||||
|
||||
The container is configured to access host services using `http://host.docker.internal:PORT`.
|
||||
|
||||
**Configuration Steps:**
|
||||
|
||||
1. **Start service on host (listen on all interfaces)**
|
||||
```bash
|
||||
export OLLAMA_HOST=0.0.0.0:11434 && ollama serve
|
||||
```
|
||||
|
||||
2. **Frontend AI Service Configuration**
|
||||
- Base URL: `http://host.docker.internal:11434/v1`
|
||||
- Provider: `openai`
|
||||
- Model: `qwen2.5:latest`
|
||||
|
||||
---
|
||||
|
||||
### 🏭 Traditional Deployment
|
||||
|
||||
#### 1. Build
|
||||
|
||||
```bash
|
||||
# 1. Build frontend
|
||||
cd web
|
||||
npm run build
|
||||
cd ..
|
||||
|
||||
# 2. Compile backend
|
||||
go build -o huobao-drama .
|
||||
```
|
||||
|
||||
Generated files:
|
||||
- `huobao-drama` - Backend executable
|
||||
- `web/dist/` - Frontend static files (embedded in backend)
|
||||
|
||||
#### 2. Prepare Deployment Files
|
||||
|
||||
Files to upload to server:
|
||||
```
|
||||
huobao-drama # Backend executable
|
||||
configs/config.yaml # Configuration file
|
||||
data/ # Data directory (optional, auto-created on first run)
|
||||
```
|
||||
|
||||
#### 3. Server Configuration
|
||||
|
||||
```bash
|
||||
# Upload files to server
|
||||
scp huobao-drama user@server:/opt/huobao-drama/
|
||||
scp configs/config.yaml user@server:/opt/huobao-drama/configs/
|
||||
|
||||
# SSH to server
|
||||
ssh user@server
|
||||
|
||||
# Modify configuration file
|
||||
cd /opt/huobao-drama
|
||||
vim configs/config.yaml
|
||||
# Set mode to production
|
||||
# Configure domain and storage path
|
||||
|
||||
# Create data directory and set permissions (Important!)
|
||||
# Note: Replace YOUR_USER with actual user running the service (e.g., www-data, ubuntu, deploy)
|
||||
sudo mkdir -p /opt/huobao-drama/data/storage
|
||||
sudo chown -R YOUR_USER:YOUR_USER /opt/huobao-drama/data
|
||||
sudo chmod -R 755 /opt/huobao-drama/data
|
||||
|
||||
# Grant execute permission
|
||||
chmod +x huobao-drama
|
||||
|
||||
# Start service
|
||||
./huobao-drama
|
||||
```
|
||||
|
||||
#### 4. Manage Service with systemd
|
||||
|
||||
Create service file `/etc/systemd/system/huobao-drama.service`:
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=Huobao Drama Service
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=YOUR_USER
|
||||
WorkingDirectory=/opt/huobao-drama
|
||||
ExecStart=/opt/huobao-drama/huobao-drama
|
||||
Restart=on-failure
|
||||
RestartSec=10
|
||||
|
||||
# Environment variables (optional)
|
||||
# Environment="GIN_MODE=release"
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
Start service:
|
||||
```bash
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable huobao-drama
|
||||
sudo systemctl start huobao-drama
|
||||
sudo systemctl status huobao-drama
|
||||
```
|
||||
|
||||
**⚠️ Common Issue: SQLite Write Permission Error**
|
||||
|
||||
If you encounter `attempt to write a readonly database` error:
|
||||
|
||||
```bash
|
||||
# 1. Check current user running the service
|
||||
sudo systemctl status huobao-drama | grep "Main PID"
|
||||
ps aux | grep huobao-drama
|
||||
|
||||
# 2. Fix permissions (replace YOUR_USER with actual username)
|
||||
sudo chown -R YOUR_USER:YOUR_USER /opt/huobao-drama/data
|
||||
sudo chmod -R 755 /opt/huobao-drama/data
|
||||
|
||||
# 3. Verify permissions
|
||||
ls -la /opt/huobao-drama/data
|
||||
# Should show owner as the user running the service
|
||||
|
||||
# 4. Restart service
|
||||
sudo systemctl restart huobao-drama
|
||||
```
|
||||
|
||||
**Reason:**
|
||||
- SQLite requires write permission on both the database file **and** its directory
|
||||
- Needs to create temporary files in the directory (e.g., `-wal`, `-journal`)
|
||||
- **Key**: Ensure systemd `User` matches data directory owner
|
||||
|
||||
**Common Usernames:**
|
||||
- Ubuntu/Debian: `www-data`, `ubuntu`
|
||||
- CentOS/RHEL: `nginx`, `apache`
|
||||
- Custom deployment: `deploy`, `app`, current logged-in user
|
||||
|
||||
#### 5. Nginx Reverse Proxy
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name your-domain.com;
|
||||
|
||||
location / {
|
||||
proxy_pass http://localhost:5678;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
}
|
||||
|
||||
# Direct access to static files
|
||||
location /static/ {
|
||||
alias /opt/huobao-drama/data/storage/;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎨 Tech Stack
|
||||
|
||||
### Backend
|
||||
- **Language**: Go 1.23+
|
||||
- **Web Framework**: Gin 1.9+
|
||||
- **ORM**: GORM
|
||||
- **Database**: SQLite
|
||||
- **Logging**: Zap
|
||||
- **Video Processing**: FFmpeg
|
||||
- **AI Services**: OpenAI, Gemini, Doubao, etc.
|
||||
|
||||
### Frontend
|
||||
- **Framework**: Vue 3.4+
|
||||
- **Language**: TypeScript 5+
|
||||
- **Build Tool**: Vite 5
|
||||
- **UI Components**: Element Plus
|
||||
- **CSS Framework**: TailwindCSS
|
||||
- **State Management**: Pinia
|
||||
- **Router**: Vue Router 4
|
||||
|
||||
### Development Tools
|
||||
- **Package Management**: Go Modules, npm
|
||||
- **Code Standards**: ESLint, Prettier
|
||||
- **Version Control**: Git
|
||||
|
||||
---
|
||||
|
||||
## 📝 FAQ
|
||||
|
||||
### Q: How can Docker containers access Ollama on the host?
|
||||
A: Use `http://host.docker.internal:11434/v1` as Base URL. Note two things:
|
||||
1. Host Ollama needs to listen on `0.0.0.0`: `export OLLAMA_HOST=0.0.0.0:11434 && ollama serve`
|
||||
2. Linux users using `docker run` need to add: `--add-host=host.docker.internal:host-gateway`
|
||||
|
||||
See: [DOCKER_HOST_ACCESS.md](docs/DOCKER_HOST_ACCESS.md)
|
||||
|
||||
### Q: FFmpeg not installed or not found?
|
||||
A: Ensure FFmpeg is installed and in the PATH environment variable. Verify with `ffmpeg -version`.
|
||||
|
||||
### Q: Frontend cannot connect to backend API?
|
||||
A: Check if backend is running and port is correct. In development mode, frontend proxy config is in `web/vite.config.ts`.
|
||||
|
||||
### Q: Database tables not created?
|
||||
A: GORM automatically creates tables on first startup, check logs to confirm migration success.
|
||||
|
||||
---
|
||||
|
||||
## 📋 Changelog
|
||||
|
||||
### v1.0.2 (2026-01-16)
|
||||
|
||||
#### 🚀 Major Updates
|
||||
- Pure Go SQLite driver (`modernc.org/sqlite`), supports `CGO_ENABLED=0` cross-platform compilation
|
||||
- Optimized concurrency performance (WAL mode), resolved "database is locked" errors
|
||||
- Docker cross-platform support for `host.docker.internal` to access host services
|
||||
- Streamlined documentation and deployment guides
|
||||
|
||||
### v1.0.1 (2026-01-14)
|
||||
|
||||
#### 🐛 Bug Fixes / 🔧 Improvements
|
||||
- Fixed video generation API response parsing issues
|
||||
- Added OpenAI Sora video endpoint configuration
|
||||
- Optimized error handling and logging
|
||||
|
||||
---
|
||||
|
||||
## 🤝 Contributing
|
||||
|
||||
Issues and Pull Requests are welcome!
|
||||
|
||||
1. Fork this project
|
||||
2. Create a feature branch (`git checkout -b feature/AmazingFeature`)
|
||||
3. Commit your changes (`git commit -m 'Add some AmazingFeature'`)
|
||||
4. Push to the branch (`git push origin feature/AmazingFeature`)
|
||||
5. Open a Pull Request
|
||||
|
||||
---
|
||||
|
||||
## API Configuration Site
|
||||
Configure in 2 minutes: [API Aggregation Site](https://api.chatfire.site/models)
|
||||
|
||||
## 📧 Contact
|
||||
Business Contact (WeChat): dangbao1117
|
||||
|
||||
## Community Group
|
||||

|
||||
- Submit [Issue](../../issues)
|
||||
- Email project maintainers
|
||||
|
||||
---
|
||||
|
||||
<div align="center">
|
||||
|
||||
**⭐ If this project helps you, please give it a Star!**
|
||||
|
||||
## Star History
|
||||
|
||||
[](https://www.star-history.com/#chatfire-AI/huobao-drama&type=date&legend=top-left)
|
||||
|
||||
Made with ❤️ by Huobao Team
|
||||
|
||||
</div>
|
||||
625
README-JA.md
Normal file
625
README-JA.md
Normal file
@@ -0,0 +1,625 @@
|
||||
# 🎬 Huobao Drama - AI ショートドラマ制作プラットフォーム
|
||||
|
||||
<div align="center">
|
||||
|
||||
**Go + Vue3 ベースのフルスタック AI ショートドラマ自動化プラットフォーム**
|
||||
|
||||
[](https://golang.org)
|
||||
[](https://vuejs.org)
|
||||
[](https://creativecommons.org/licenses/by-nc-sa/4.0/)
|
||||
|
||||
[機能](#機能) • [クイックスタート](#クイックスタート) • [デプロイ](#デプロイ)
|
||||
|
||||
[简体中文](README-CN.md) | [English](README.md) | [日本語](README-JA.md)
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## 📖 概要
|
||||
|
||||
Huobao Drama は、脚本生成、キャラクターデザイン、絵コンテ作成から動画合成までの全ワークフローを自動化する AI 駆動のショートドラマ制作プラットフォームです。
|
||||
|
||||
### 🎯 主要機能
|
||||
|
||||
- **🤖 AI 駆動**: 大規模言語モデルを使用して脚本を解析し、キャラクター、シーン、絵コンテ情報を抽出
|
||||
- **🎨 インテリジェント創作**: AI によるキャラクターポートレートとシーン背景の生成
|
||||
- **📹 動画生成**: テキストから動画、画像から動画モデルによる絵コンテ動画の自動生成
|
||||
- **🔄 完全なワークフロー**: アイデアから完成動画までのエンドツーエンド制作ワークフロー
|
||||
|
||||
### 🛠️ 技術アーキテクチャ
|
||||
|
||||
**DDD(ドメイン駆動設計)** に基づく明確なレイヤー構造:
|
||||
|
||||
```
|
||||
├── APIレイヤー (Gin HTTP)
|
||||
├── アプリケーションサービスレイヤー (ビジネスロジック)
|
||||
├── ドメインレイヤー (ドメインモデル)
|
||||
└── インフラストラクチャレイヤー (データベース、外部サービス)
|
||||
```
|
||||
|
||||
### 🎥 デモ動画
|
||||
|
||||
AI ショートドラマ生成を体験:
|
||||
|
||||
<div align="center">
|
||||
|
||||
**サンプル作品 1**
|
||||
|
||||
<video src="https://ffile.chatfire.site/cf/public/20260114094337396.mp4" controls width="640"></video>
|
||||
|
||||
**サンプル作品 2**
|
||||
|
||||
<video src="https://ffile.chatfire.site/cf/public/fcede75e8aeafe22031dbf78f86285b8.mp4" controls width="640"></video>
|
||||
|
||||
[動画 1 を見る](https://ffile.chatfire.site/cf/public/20260114094337396.mp4) | [動画 2 を見る](https://ffile.chatfire.site/cf/public/fcede75e8aeafe22031dbf78f86285b8.mp4)
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## ✨ 機能
|
||||
|
||||
### 🎭 キャラクター管理
|
||||
|
||||
- ✅ AI 生成キャラクターポートレート
|
||||
- ✅ バッチキャラクター生成
|
||||
- ✅ キャラクター画像のアップロードと管理
|
||||
|
||||
### 🎬 絵コンテ制作
|
||||
|
||||
- ✅ 自動絵コンテスクリプト生成
|
||||
- ✅ シーン説明とショットデザイン
|
||||
- ✅ 絵コンテ画像生成(テキストから画像)
|
||||
- ✅ フレームタイプ選択(先頭フレーム/キーフレーム/末尾フレーム/パネル)
|
||||
|
||||
### 🎥 動画生成
|
||||
|
||||
- ✅ 画像から動画の自動生成
|
||||
- ✅ 動画合成と編集
|
||||
- ✅ トランジション効果
|
||||
|
||||
### 📦 アセット管理
|
||||
|
||||
- ✅ 統合アセットライブラリ管理
|
||||
- ✅ ローカルストレージサポート
|
||||
- ✅ アセットのインポート/エクスポート
|
||||
- ✅ タスク進捗トラッキング
|
||||
|
||||
---
|
||||
|
||||
## 🚀 クイックスタート
|
||||
|
||||
### 📋 前提条件
|
||||
|
||||
| ソフトウェア | バージョン | 説明 |
|
||||
| ------------ | ---------- | ------------------------ |
|
||||
| **Go** | 1.23+ | バックエンドランタイム |
|
||||
| **Node.js** | 18+ | フロントエンドビルド環境 |
|
||||
| **npm** | 9+ | パッケージマネージャー |
|
||||
| **FFmpeg** | 4.0+ | 動画処理(**必須**) |
|
||||
| **SQLite** | 3.x | データベース(内蔵) |
|
||||
|
||||
#### FFmpeg のインストール
|
||||
|
||||
**macOS:**
|
||||
|
||||
```bash
|
||||
brew install ffmpeg
|
||||
```
|
||||
|
||||
**Ubuntu/Debian:**
|
||||
|
||||
```bash
|
||||
sudo apt update
|
||||
sudo apt install ffmpeg
|
||||
```
|
||||
|
||||
**Windows:**
|
||||
[FFmpeg 公式サイト](https://ffmpeg.org/download.html)からダウンロードし、環境変数を設定
|
||||
|
||||
インストール確認:
|
||||
|
||||
```bash
|
||||
ffmpeg -version
|
||||
```
|
||||
|
||||
### ⚙️ 設定
|
||||
|
||||
設定ファイルをコピーして編集:
|
||||
|
||||
```bash
|
||||
cp configs/config.example.yaml configs/config.yaml
|
||||
vim configs/config.yaml
|
||||
```
|
||||
|
||||
設定ファイル形式(`configs/config.yaml`):
|
||||
|
||||
```yaml
|
||||
app:
|
||||
name: "Huobao Drama API"
|
||||
version: "1.0.0"
|
||||
debug: true # 開発環境ではtrue、本番環境ではfalseに設定
|
||||
|
||||
server:
|
||||
port: 5678
|
||||
host: "0.0.0.0"
|
||||
cors_origins:
|
||||
- "http://localhost:3012"
|
||||
read_timeout: 600
|
||||
write_timeout: 600
|
||||
|
||||
database:
|
||||
type: "sqlite"
|
||||
path: "./data/drama_generator.db"
|
||||
max_idle: 10
|
||||
max_open: 100
|
||||
|
||||
storage:
|
||||
type: "local"
|
||||
local_path: "./data/storage"
|
||||
base_url: "http://localhost:5678/static"
|
||||
|
||||
ai:
|
||||
default_text_provider: "openai"
|
||||
default_image_provider: "openai"
|
||||
default_video_provider: "doubao"
|
||||
```
|
||||
|
||||
**主要設定項目:**
|
||||
|
||||
- `app.debug`: デバッグモードスイッチ(開発環境では true を推奨)
|
||||
- `server.port`: サービスポート
|
||||
- `server.cors_origins`: フロントエンドの許可 CORS オリジン
|
||||
- `database.path`: SQLite データベースファイルパス
|
||||
- `storage.local_path`: ローカルファイルストレージパス
|
||||
- `storage.base_url`: 静的リソースアクセス URL
|
||||
- `ai.default_*_provider`: AI サービスプロバイダー設定(API キーは Web UI で設定)
|
||||
|
||||
### 📥 インストール
|
||||
|
||||
```bash
|
||||
# プロジェクトをクローン
|
||||
git clone https://github.com/chatfire-AI/huobao-drama.git
|
||||
cd huobao-drama
|
||||
|
||||
# Go依存関係をインストール
|
||||
go mod download
|
||||
|
||||
# フロントエンド依存関係をインストール
|
||||
cd web
|
||||
npm install
|
||||
cd ..
|
||||
```
|
||||
|
||||
### 🎯 プロジェクトの起動
|
||||
|
||||
#### 方法 1: 開発モード(推奨)
|
||||
|
||||
**フロントエンドとバックエンドを分離、ホットリロード対応**
|
||||
|
||||
```bash
|
||||
# ターミナル1: バックエンドサービスを起動
|
||||
go run main.go
|
||||
|
||||
# ターミナル2: フロントエンド開発サーバーを起動
|
||||
cd web
|
||||
npm run dev
|
||||
```
|
||||
|
||||
- フロントエンド: `http://localhost:3012`
|
||||
- バックエンド API: `http://localhost:5678/api/v1`
|
||||
- フロントエンドは API リクエストを自動的にバックエンドにプロキシ
|
||||
|
||||
#### 方法 2: シングルサービスモード
|
||||
|
||||
**バックエンドが API とフロントエンド静的ファイルの両方を提供**
|
||||
|
||||
```bash
|
||||
# 1. フロントエンドをビルド
|
||||
cd web
|
||||
npm run build
|
||||
cd ..
|
||||
|
||||
# 2. サービスを起動
|
||||
go run main.go
|
||||
```
|
||||
|
||||
アクセス: `http://localhost:5678`
|
||||
|
||||
### 🗄️ データベース初期化
|
||||
|
||||
データベーステーブルは初回起動時に自動作成されます(GORM AutoMigrate を使用)。手動マイグレーションは不要です。
|
||||
|
||||
---
|
||||
|
||||
## 📦 デプロイ
|
||||
|
||||
### 🐳 Docker デプロイ(推奨)
|
||||
|
||||
#### 方法 1: Docker Compose(推奨)
|
||||
|
||||
#### 🚀 中国国内ネットワーク高速化(オプション)
|
||||
|
||||
中国国内のネットワーク環境では、Docker イメージのプルや依存関係のインストールが遅い場合があります。ミラーソースを設定することでビルドプロセスを高速化できます。
|
||||
|
||||
**ステップ 1: 環境変数ファイルを作成**
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
**ステップ 2: `.env` ファイルを編集し、必要なミラーソースのコメントを解除**
|
||||
|
||||
```bash
|
||||
# Docker Hub ミラーを有効化(推奨)
|
||||
DOCKER_REGISTRY=docker.1ms.run/
|
||||
|
||||
# npm ミラーを有効化
|
||||
NPM_REGISTRY=https://registry.npmmirror.com/
|
||||
|
||||
# Go プロキシを有効化
|
||||
GO_PROXY=https://goproxy.cn,direct
|
||||
|
||||
# Alpine ミラーを有効化
|
||||
ALPINE_MIRROR=mirrors.aliyun.com
|
||||
```
|
||||
|
||||
**ステップ 3: docker compose でビルド(必須)**
|
||||
|
||||
```bash
|
||||
docker compose build
|
||||
```
|
||||
|
||||
> **重要な注意事項**:
|
||||
>
|
||||
> - ⚠️ `.env` ファイルのミラーソース設定を自動的に読み込むには `docker compose build` を使用する必要があります
|
||||
> - ❌ `docker build` コマンドを使用する場合は、手動で `--build-arg` パラメータを渡す必要があります
|
||||
> - ✅ 常に `docker compose build` を使用してビルドすることを推奨
|
||||
|
||||
**パフォーマンス比較**:
|
||||
|
||||
| 操作 | ミラー未設定 | ミラー設定後 |
|
||||
| ------------------------ | -------------- | ------------ |
|
||||
| ベースイメージのプル | 5-30 分 | 1-5 分 |
|
||||
| npm 依存関係インストール | 失敗する可能性 | 高速成功 |
|
||||
| Go 依存関係ダウンロード | 5-10 分 | 30 秒-1 分 |
|
||||
|
||||
> **注意**: 中国国外のユーザーはミラーソースを設定せず、デフォルト設定を使用してください。
|
||||
|
||||
```bash
|
||||
# サービスを起動
|
||||
docker-compose up -d
|
||||
|
||||
# ログを表示
|
||||
docker-compose logs -f
|
||||
|
||||
# サービスを停止
|
||||
docker-compose down
|
||||
```
|
||||
|
||||
#### 方法 2: Docker コマンド
|
||||
|
||||
> **注意**: Linux ユーザーはホストサービスにアクセスするために `--add-host=host.docker.internal:host-gateway` を追加する必要があります
|
||||
|
||||
```bash
|
||||
# Docker Hubから実行
|
||||
docker run -d \
|
||||
--name huobao-drama \
|
||||
-p 5678:5678 \
|
||||
-v $(pwd)/data:/app/data \
|
||||
--restart unless-stopped \
|
||||
huobao/huobao-drama:latest
|
||||
|
||||
# ログを表示
|
||||
docker logs -f huobao-drama
|
||||
```
|
||||
|
||||
**ローカルビルド**(オプション):
|
||||
|
||||
```bash
|
||||
docker build -t huobao-drama:latest .
|
||||
docker run -d --name huobao-drama -p 5678:5678 -v $(pwd)/data:/app/data huobao-drama:latest
|
||||
```
|
||||
|
||||
**Docker デプロイの利点:**
|
||||
|
||||
- ✅ デフォルト設定ですぐに使用可能
|
||||
- ✅ 環境の一貫性、依存関係の問題を回避
|
||||
- ✅ ワンクリック起動、Go、Node.js、FFmpeg のインストール不要
|
||||
- ✅ 移行とスケーリングが容易
|
||||
- ✅ 自動ヘルスチェックと再起動
|
||||
- ✅ ファイル権限の自動処理
|
||||
|
||||
#### 🔗 ホストサービスへのアクセス(Ollama/ローカルモデル)
|
||||
|
||||
コンテナは `http://host.docker.internal:ポート番号` を使用してホストサービスにアクセスするよう設定されています。
|
||||
|
||||
**設定手順:**
|
||||
|
||||
1. **ホストでサービスを起動(全インターフェースでリッスン)**
|
||||
|
||||
```bash
|
||||
export OLLAMA_HOST=0.0.0.0:11434 && ollama serve
|
||||
```
|
||||
|
||||
2. **フロントエンド AI サービス設定**
|
||||
- Base URL: `http://host.docker.internal:11434/v1`
|
||||
- Provider: `openai`
|
||||
- Model: `qwen2.5:latest`
|
||||
|
||||
---
|
||||
|
||||
### 🏭 従来のデプロイ方法
|
||||
|
||||
#### 1. ビルド
|
||||
|
||||
```bash
|
||||
# 1. フロントエンドをビルド
|
||||
cd web
|
||||
npm run build
|
||||
cd ..
|
||||
|
||||
# 2. バックエンドをコンパイル
|
||||
go build -o huobao-drama .
|
||||
```
|
||||
|
||||
生成ファイル:
|
||||
|
||||
- `huobao-drama` - バックエンド実行ファイル
|
||||
- `web/dist/` - フロントエンド静的ファイル(バックエンドに埋め込み)
|
||||
|
||||
#### 2. デプロイファイルの準備
|
||||
|
||||
サーバーにアップロードするファイル:
|
||||
|
||||
```
|
||||
huobao-drama # バックエンド実行ファイル
|
||||
configs/config.yaml # 設定ファイル
|
||||
data/ # データディレクトリ(オプション、初回実行時に自動作成)
|
||||
```
|
||||
|
||||
#### 3. サーバー設定
|
||||
|
||||
```bash
|
||||
# ファイルをサーバーにアップロード
|
||||
scp huobao-drama user@server:/opt/huobao-drama/
|
||||
scp configs/config.yaml user@server:/opt/huobao-drama/configs/
|
||||
|
||||
# サーバーにSSH接続
|
||||
ssh user@server
|
||||
|
||||
# 設定ファイルを編集
|
||||
cd /opt/huobao-drama
|
||||
vim configs/config.yaml
|
||||
# modeをproductionに設定
|
||||
# ドメインとストレージパスを設定
|
||||
|
||||
# データディレクトリを作成し権限を設定(重要!)
|
||||
# 注意: YOUR_USERを実際にサービスを実行するユーザー名に置き換え(例: www-data、ubuntu、deploy)
|
||||
sudo mkdir -p /opt/huobao-drama/data/storage
|
||||
sudo chown -R YOUR_USER:YOUR_USER /opt/huobao-drama/data
|
||||
sudo chmod -R 755 /opt/huobao-drama/data
|
||||
|
||||
# 実行権限を付与
|
||||
chmod +x huobao-drama
|
||||
|
||||
# サービスを起動
|
||||
./huobao-drama
|
||||
```
|
||||
|
||||
#### 4. systemd でサービス管理
|
||||
|
||||
サービスファイル `/etc/systemd/system/huobao-drama.service` を作成:
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=Huobao Drama Service
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=YOUR_USER
|
||||
WorkingDirectory=/opt/huobao-drama
|
||||
ExecStart=/opt/huobao-drama/huobao-drama
|
||||
Restart=on-failure
|
||||
RestartSec=10
|
||||
|
||||
# 環境変数(オプション)
|
||||
# Environment="GIN_MODE=release"
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
サービスを起動:
|
||||
|
||||
```bash
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable huobao-drama
|
||||
sudo systemctl start huobao-drama
|
||||
sudo systemctl status huobao-drama
|
||||
```
|
||||
|
||||
**⚠️ よくある問題: SQLite 書き込み権限エラー**
|
||||
|
||||
`attempt to write a readonly database` エラーが発生した場合:
|
||||
|
||||
```bash
|
||||
# 1. サービスを実行中のユーザーを確認
|
||||
sudo systemctl status huobao-drama | grep "Main PID"
|
||||
ps aux | grep huobao-drama
|
||||
|
||||
# 2. 権限を修正(YOUR_USERを実際のユーザー名に置き換え)
|
||||
sudo chown -R YOUR_USER:YOUR_USER /opt/huobao-drama/data
|
||||
sudo chmod -R 755 /opt/huobao-drama/data
|
||||
|
||||
# 3. 権限を確認
|
||||
ls -la /opt/huobao-drama/data
|
||||
# サービスを実行するユーザーが所有者として表示されるはず
|
||||
|
||||
# 4. サービスを再起動
|
||||
sudo systemctl restart huobao-drama
|
||||
```
|
||||
|
||||
**原因:**
|
||||
|
||||
- SQLite はデータベースファイル**と**そのディレクトリの両方に書き込み権限が必要
|
||||
- ディレクトリ内に一時ファイル(例: `-wal`、`-journal`)を作成する必要がある
|
||||
- **重要**: systemd の`User`がデータディレクトリの所有者と一致していることを確認
|
||||
|
||||
**一般的なユーザー名:**
|
||||
|
||||
- Ubuntu/Debian: `www-data`、`ubuntu`
|
||||
- CentOS/RHEL: `nginx`、`apache`
|
||||
- カスタムデプロイ: `deploy`、`app`、現在ログインしているユーザー
|
||||
|
||||
#### 5. Nginx リバースプロキシ
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name your-domain.com;
|
||||
|
||||
location / {
|
||||
proxy_pass http://localhost:5678;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
}
|
||||
|
||||
# 静的ファイルへの直接アクセス
|
||||
location /static/ {
|
||||
alias /opt/huobao-drama/data/storage/;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎨 技術スタック
|
||||
|
||||
### バックエンド
|
||||
|
||||
- **言語**: Go 1.23+
|
||||
- **Web フレームワーク**: Gin 1.9+
|
||||
- **ORM**: GORM
|
||||
- **データベース**: SQLite
|
||||
- **ログ**: Zap
|
||||
- **動画処理**: FFmpeg
|
||||
- **AI サービス**: OpenAI、Gemini、Doubao など
|
||||
|
||||
### フロントエンド
|
||||
|
||||
- **フレームワーク**: Vue 3.4+
|
||||
- **言語**: TypeScript 5+
|
||||
- **ビルドツール**: Vite 5
|
||||
- **UI コンポーネント**: Element Plus
|
||||
- **CSS フレームワーク**: TailwindCSS
|
||||
- **状態管理**: Pinia
|
||||
- **ルーター**: Vue Router 4
|
||||
|
||||
### 開発ツール
|
||||
|
||||
- **パッケージ管理**: Go Modules、npm
|
||||
- **コード規約**: ESLint、Prettier
|
||||
- **バージョン管理**: Git
|
||||
|
||||
---
|
||||
|
||||
## 📝 よくある質問
|
||||
|
||||
### Q: Docker コンテナからホストの Ollama にアクセスするには?
|
||||
|
||||
A: Base URL として `http://host.docker.internal:11434/v1` を使用します。注意点:
|
||||
|
||||
1. ホストの Ollama は `0.0.0.0` でリッスンする必要があります: `export OLLAMA_HOST=0.0.0.0:11434 && ollama serve`
|
||||
2. `docker run` を使用する Linux ユーザーは追加が必要: `--add-host=host.docker.internal:host-gateway`
|
||||
|
||||
詳細: [DOCKER_HOST_ACCESS.md](docs/DOCKER_HOST_ACCESS.md)
|
||||
|
||||
### Q: FFmpeg がインストールされていない、または見つからない?
|
||||
|
||||
A: FFmpeg がインストールされ、PATH 環境変数に含まれていることを確認してください。`ffmpeg -version` で確認。
|
||||
|
||||
### Q: フロントエンドがバックエンド API に接続できない?
|
||||
|
||||
A: バックエンドが実行中で、ポートが正しいか確認してください。開発モードでは、フロントエンドプロキシ設定は `web/vite.config.ts` にあります。
|
||||
|
||||
### Q: データベーステーブルが作成されない?
|
||||
|
||||
A: GORM は初回起動時にテーブルを自動作成します。ログでマイグレーション成功を確認してください。
|
||||
|
||||
---
|
||||
|
||||
## 📋 更新履歴
|
||||
|
||||
### v1.0.2 (2026-01-16)
|
||||
|
||||
#### 🚀 主要アップデート
|
||||
|
||||
- 純粋な Go SQLite ドライバー(`modernc.org/sqlite`)、`CGO_ENABLED=0` クロスプラットフォームコンパイルをサポート
|
||||
- 並行性能を最適化(WAL モード)、"database is locked" エラーを解決
|
||||
- ホストサービスへのアクセス用 `host.docker.internal` の Docker クロスプラットフォームサポート
|
||||
- ドキュメントとデプロイガイドの簡素化
|
||||
|
||||
### v1.0.1 (2026-01-14)
|
||||
|
||||
#### 🐛 バグ修正 / 🔧 改善
|
||||
|
||||
- 動画生成 API レスポンスのパース問題を修正
|
||||
- OpenAI Sora 動画エンドポイント設定を追加
|
||||
- エラー処理とログ出力を最適化
|
||||
|
||||
---
|
||||
|
||||
## 🤝 コントリビューション
|
||||
|
||||
Issue と Pull Request を歓迎します!
|
||||
|
||||
1. このプロジェクトをフォーク
|
||||
2. フィーチャーブランチを作成 (`git checkout -b feature/AmazingFeature`)
|
||||
3. 変更をコミット (`git commit -m 'Add some AmazingFeature'`)
|
||||
4. ブランチにプッシュ (`git push origin feature/AmazingFeature`)
|
||||
5. Pull Request を作成
|
||||
|
||||
---
|
||||
|
||||
## API 設定サイト
|
||||
|
||||
2 分で設定完了: [API 集約サイト](https://api.chatfire.site/models)
|
||||
|
||||
---
|
||||
|
||||
## 👨💻 私たちについて
|
||||
|
||||
**AI 火宝 - AI スタジオ起業中**
|
||||
|
||||
- 🏠 **所在地**: 中国南京
|
||||
- 🚀 **ステータス**: 起業中
|
||||
- 📧 **Email**: [18550175439@163.com](mailto:18550175439@163.com)
|
||||
- 💬 **WeChat**: dangbao1117 (個人 WeChat - 技術的な質問には対応しません)
|
||||
- 🐙 **GitHub**: [https://github.com/chatfire-AI/huobao-drama](https://github.com/chatfire-AI/huobao-drama)
|
||||
|
||||
> _「AI に私たちのより創造的なことを手伝ってもらおう」_
|
||||
|
||||
## コミュニティグループ
|
||||
|
||||

|
||||
|
||||
- [Issue](../../issues)を提出
|
||||
- プロジェクトメンテナにメール
|
||||
|
||||
---
|
||||
|
||||
<div align="center">
|
||||
|
||||
**⭐ このプロジェクトが役に立ったら、Star をお願いします!**
|
||||
|
||||
## Star 履歴
|
||||
|
||||
[](https://www.star-history.com/#chatfire-AI/huobao-drama&type=date&legend=top-left)
|
||||
|
||||
Made with ❤️ by Huobao Team
|
||||
|
||||
</div>
|
||||
505
README.md
505
README.md
@@ -1,140 +1,145 @@
|
||||
# 🎬 Huobao Drama - AI短剧生成平台
|
||||
# 🎬 Huobao Drama - AI Short Drama Production Platform
|
||||
|
||||
<div align="center">
|
||||
|
||||
**基于 Go + Vue3 的全栈AI短剧自动化生产平台**
|
||||
**Full-stack AI Short Drama Automation Platform Based on Go + Vue3**
|
||||
|
||||
[](https://golang.org)
|
||||
[](https://vuejs.org)
|
||||
[](https://creativecommons.org/licenses/by-nc-sa/4.0/)
|
||||
|
||||
[功能特性](#功能特性) • [快速开始](#快速开始) • [部署指南](#部署指南)
|
||||
[Features](#features) • [Quick Start](#quick-start) • [Deployment](#deployment)
|
||||
|
||||
[简体中文](README.md) | [English](README-EN.md)
|
||||
[简体中文](README-CN.md) | [English](README.md) | [日本語](README-JA.md)
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## 📖 项目简介 / About
|
||||
|
||||
Huobao Drama 是一个基于AI的短剧自动化生产平台,实现从剧本生成、角色设计、分镜制作到视频合成的全流程自动化。
|
||||
## 📖 About
|
||||
|
||||
Huobao Drama is an AI-powered short drama production platform that automates the entire workflow from script generation, character design, storyboarding to video composition.
|
||||
|
||||
### 🎯 核心价值 / Core Features
|
||||
### 🎯 Core Features
|
||||
|
||||
- **🤖 AI驱动 / AI-Driven**:使用大语言模型解析剧本,提取角色、场景和分镜信息 | Parse scripts using large language models to extract characters, scenes, and storyboards
|
||||
- **🎨 智能创作 / Intelligent Creation**:AI绘图生成角色形象和场景背景 | AI-generated character portraits and scene backgrounds
|
||||
- **📹 视频生成 / Video Generation**:基于文生视频和图生视频模型自动生成分镜视频 | Automatic storyboard video generation using text-to-video and image-to-video models
|
||||
- **🔄 工作流 / Workflow**:完整的短剧制作工作流,从创意到成片一站式完成 | Complete production workflow from idea to final video
|
||||
- **🤖 AI-Driven**: Parse scripts using large language models to extract characters, scenes, and storyboards
|
||||
- **🎨 Intelligent Creation**: AI-generated character portraits and scene backgrounds
|
||||
- **📹 Video Generation**: Automatic storyboard video generation using text-to-video and image-to-video models
|
||||
- **🔄 Complete Workflow**: End-to-end production workflow from idea to final video
|
||||
|
||||
### 🛠️ 技术架构
|
||||
### 🛠️ Technical Architecture
|
||||
|
||||
采用**DDD领域驱动设计**,清晰分层:
|
||||
Based on **DDD (Domain-Driven Design)** with clear layering:
|
||||
|
||||
```
|
||||
├── API层 (Gin HTTP)
|
||||
├── 应用服务层 (Business Logic)
|
||||
├── 领域层 (Domain Models)
|
||||
└── 基础设施层 (Database, External Services)
|
||||
├── API Layer (Gin HTTP)
|
||||
├── Application Service Layer (Business Logic)
|
||||
├── Domain Layer (Domain Models)
|
||||
└── Infrastructure Layer (Database, External Services)
|
||||
```
|
||||
|
||||
### 🎥 作品展示 / Demo Videos
|
||||
### 🎥 Demo Videos
|
||||
|
||||
体验 AI 短剧生成效果:
|
||||
Experience AI short drama generation:
|
||||
|
||||
<div align="center">
|
||||
|
||||
**示例作品 1**
|
||||
**Sample Work 1**
|
||||
|
||||
<video src="https://ffile.chatfire.site/cf/public/20260114094337396.mp4" controls width="640"></video>
|
||||
|
||||
**示例作品 2**
|
||||
**Sample Work 2**
|
||||
|
||||
<video src="https://ffile.chatfire.site/cf/public/fcede75e8aeafe22031dbf78f86285b8.mp4" controls width="640"></video>
|
||||
|
||||
[点击观看视频 1](https://ffile.chatfire.site/cf/public/20260114094337396.mp4) | [点击观看视频 2](https://ffile.chatfire.site/cf/public/fcede75e8aeafe22031dbf78f86285b8.mp4)
|
||||
[Watch Video 1](https://ffile.chatfire.site/cf/public/20260114094337396.mp4) | [Watch Video 2](https://ffile.chatfire.site/cf/public/fcede75e8aeafe22031dbf78f86285b8.mp4)
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## ✨ 功能特性
|
||||
## ✨ Features
|
||||
|
||||
### 🎭 角色管理
|
||||
- ✅ AI生成角色形象
|
||||
- ✅ 批量角色生成
|
||||
- ✅ 角色图片上传和管理
|
||||
### 🎭 Character Management
|
||||
|
||||
### 🎬 分镜制作
|
||||
- ✅ 自动生成分镜脚本
|
||||
- ✅ 场景描述和镜头设计
|
||||
- ✅ 分镜图片生成(文生图)
|
||||
- ✅ 帧类型选择(首帧/关键帧/尾帧/分镜板)
|
||||
- ✅ AI-generated character portraits
|
||||
- ✅ Batch character generation
|
||||
- ✅ Character image upload and management
|
||||
|
||||
### 🎥 视频生成
|
||||
- ✅ 图生视频自动生成
|
||||
- ✅ 视频合成和剪辑
|
||||
- ✅ 转场效果
|
||||
### 🎬 Storyboard Production
|
||||
|
||||
### 📦 资源管理
|
||||
- ✅ 素材库统一管理
|
||||
- ✅ 本地存储支持
|
||||
- ✅ 资源导入导出
|
||||
- ✅ 任务进度追踪
|
||||
- ✅ Automatic storyboard script generation
|
||||
- ✅ Scene descriptions and shot design
|
||||
- ✅ Storyboard image generation (text-to-image)
|
||||
- ✅ Frame type selection (first frame/key frame/last frame/panel)
|
||||
|
||||
### 🎥 Video Generation
|
||||
|
||||
- ✅ Automatic image-to-video generation
|
||||
- ✅ Video composition and editing
|
||||
- ✅ Transition effects
|
||||
|
||||
### 📦 Asset Management
|
||||
|
||||
- ✅ Unified asset library management
|
||||
- ✅ Local storage support
|
||||
- ✅ Asset import/export
|
||||
- ✅ Task progress tracking
|
||||
|
||||
---
|
||||
|
||||
## 🚀 快速开始
|
||||
## 🚀 Quick Start
|
||||
|
||||
### 📋 环境要求
|
||||
### 📋 Prerequisites
|
||||
|
||||
| 软件 | 版本要求 | 说明 |
|
||||
|------|---------|------|
|
||||
| **Go** | 1.23+ | 后端运行环境 |
|
||||
| **Node.js** | 18+ | 前端构建环境 |
|
||||
| **npm** | 9+ | 包管理工具 |
|
||||
| **FFmpeg** | 4.0+ | 视频处理(**必需**) |
|
||||
| **SQLite** | 3.x | 数据库(已内置) |
|
||||
| Software | Version | Description |
|
||||
| ----------- | ------- | ------------------------------- |
|
||||
| **Go** | 1.23+ | Backend runtime |
|
||||
| **Node.js** | 18+ | Frontend build environment |
|
||||
| **npm** | 9+ | Package manager |
|
||||
| **FFmpeg** | 4.0+ | Video processing (**Required**) |
|
||||
| **SQLite** | 3.x | Database (built-in) |
|
||||
|
||||
#### 安装 FFmpeg
|
||||
#### Installing FFmpeg
|
||||
|
||||
**macOS:**
|
||||
|
||||
```bash
|
||||
brew install ffmpeg
|
||||
```
|
||||
|
||||
**Ubuntu/Debian:**
|
||||
|
||||
```bash
|
||||
sudo apt update
|
||||
sudo apt install ffmpeg
|
||||
```
|
||||
|
||||
**Windows:**
|
||||
从 [FFmpeg官网](https://ffmpeg.org/download.html) 下载并配置环境变量
|
||||
Download from [FFmpeg Official Site](https://ffmpeg.org/download.html) and configure environment variables
|
||||
|
||||
Verify installation:
|
||||
|
||||
验证安装:
|
||||
```bash
|
||||
ffmpeg -version
|
||||
```
|
||||
|
||||
### ⚙️ 配置文件
|
||||
### ⚙️ Configuration
|
||||
|
||||
复制并编辑配置文件:
|
||||
Copy and edit the configuration file:
|
||||
|
||||
```bash
|
||||
cp configs/config.example.yaml configs/config.yaml
|
||||
vim configs/config.yaml
|
||||
```
|
||||
|
||||
配置文件格式(`configs/config.yaml`):
|
||||
Configuration file format (`configs/config.yaml`):
|
||||
|
||||
```yaml
|
||||
app:
|
||||
name: "Huobao Drama API"
|
||||
version: "1.0.0"
|
||||
debug: true # 开发环境设为true,生产环境设为false
|
||||
debug: true # Set to true for development, false for production
|
||||
|
||||
server:
|
||||
port: 5678
|
||||
@@ -161,95 +166,144 @@ ai:
|
||||
default_video_provider: "doubao"
|
||||
```
|
||||
|
||||
**重要配置项:**
|
||||
- `app.debug`: 调试模式开关(开发环境建议设为true)
|
||||
- `server.port`: 服务运行端口
|
||||
- `server.cors_origins`: 允许跨域访问的前端地址
|
||||
- `database.path`: SQLite数据库文件路径
|
||||
- `storage.local_path`: 本地文件存储路径
|
||||
- `storage.base_url`: 静态资源访问URL
|
||||
- `ai.default_*_provider`: AI服务提供商配置(在Web界面中配置具体的API Key)
|
||||
**Key Configuration Items:**
|
||||
|
||||
### 📥 安装依赖
|
||||
- `app.debug`: Debug mode switch (recommended true for development)
|
||||
- `server.port`: Service port
|
||||
- `server.cors_origins`: Allowed CORS origins for frontend
|
||||
- `database.path`: SQLite database file path
|
||||
- `storage.local_path`: Local file storage path
|
||||
- `storage.base_url`: Static resource access URL
|
||||
- `ai.default_*_provider`: AI service provider configuration (API keys configured in Web UI)
|
||||
|
||||
### 📥 Installation
|
||||
|
||||
```bash
|
||||
# 克隆项目
|
||||
# Clone the project
|
||||
git clone https://github.com/chatfire-AI/huobao-drama.git
|
||||
cd huobao-drama
|
||||
|
||||
# 安装Go依赖
|
||||
# Install Go dependencies
|
||||
go mod download
|
||||
|
||||
# 安装前端依赖
|
||||
# Install frontend dependencies
|
||||
cd web
|
||||
npm install
|
||||
cd ..
|
||||
```
|
||||
|
||||
### 🎯 启动项目
|
||||
### 🎯 Starting the Project
|
||||
|
||||
#### 方式一:开发模式(推荐)
|
||||
#### Method 1: Development Mode (Recommended)
|
||||
|
||||
**前后端分离,支持热重载**
|
||||
**Frontend and backend separation with hot reload**
|
||||
|
||||
```bash
|
||||
# 终端1:启动后端服务
|
||||
# Terminal 1: Start backend service
|
||||
go run main.go
|
||||
|
||||
# 终端2:启动前端开发服务器
|
||||
# Terminal 2: Start frontend dev server
|
||||
cd web
|
||||
npm run dev
|
||||
```
|
||||
|
||||
- 前端地址: `http://localhost:3012`
|
||||
- 后端API: `http://localhost:5678/api/v1`
|
||||
- 前端自动代理API请求到后端
|
||||
- Frontend: `http://localhost:3012`
|
||||
- Backend API: `http://localhost:5678/api/v1`
|
||||
- Frontend automatically proxies API requests to backend
|
||||
|
||||
#### 方式二:单服务模式
|
||||
#### Method 2: Single Service Mode
|
||||
|
||||
**后端同时提供API和前端静态文件**
|
||||
**Backend serves both API and frontend static files**
|
||||
|
||||
```bash
|
||||
# 1. 构建前端
|
||||
# 1. Build frontend
|
||||
cd web
|
||||
npm run build
|
||||
cd ..
|
||||
|
||||
# 2. 启动服务
|
||||
# 2. Start service
|
||||
go run main.go
|
||||
```
|
||||
|
||||
访问: `http://localhost:5678`
|
||||
Access: `http://localhost:5678`
|
||||
|
||||
### 🗄️ 数据库初始化
|
||||
### 🗄️ Database Initialization
|
||||
|
||||
数据库表会在首次启动时自动创建(使用GORM AutoMigrate),无需手动迁移。
|
||||
Database tables are automatically created on first startup (using GORM AutoMigrate), no manual migration needed.
|
||||
|
||||
---
|
||||
|
||||
## 📦 部署指南
|
||||
## 📦 Deployment
|
||||
|
||||
### 🐳 Docker 部署(推荐)
|
||||
### 🐳 Docker Deployment (Recommended)
|
||||
|
||||
#### 方式一:Docker Compose(推荐)
|
||||
#### Method 1: Docker Compose (Recommended)
|
||||
|
||||
#### 🚀 China Network Acceleration (Optional)
|
||||
|
||||
If you are in China, pulling Docker images and installing dependencies may be slow. You can speed up the build process by configuring mirror sources.
|
||||
|
||||
**Step 1: Create environment variable file**
|
||||
|
||||
```bash
|
||||
# 启动服务
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
**Step 2: Edit `.env` file and uncomment the mirror sources you need**
|
||||
|
||||
```bash
|
||||
# Enable Docker Hub mirror (recommended)
|
||||
DOCKER_REGISTRY=docker.1ms.run/
|
||||
|
||||
# Enable npm mirror
|
||||
NPM_REGISTRY=https://registry.npmmirror.com/
|
||||
|
||||
# Enable Go proxy
|
||||
GO_PROXY=https://goproxy.cn,direct
|
||||
|
||||
# Enable Alpine mirror
|
||||
ALPINE_MIRROR=mirrors.aliyun.com
|
||||
```
|
||||
|
||||
**Step 3: Build with docker compose (required)**
|
||||
|
||||
```bash
|
||||
docker compose build
|
||||
```
|
||||
|
||||
> **Important Note**:
|
||||
>
|
||||
> - ⚠️ You must use `docker compose build` to automatically load mirror source configurations from the `.env` file
|
||||
> - ❌ If using `docker build` command, you need to manually pass `--build-arg` parameters
|
||||
> - ✅ Always recommended to use `docker compose build` for building
|
||||
|
||||
**Performance Comparison**:
|
||||
|
||||
| Operation | Without Mirrors | With Mirrors |
|
||||
| ---------------- | --------------- | ------------ |
|
||||
| Pull base images | 5-30 minutes | 1-5 minutes |
|
||||
| Install npm deps | May fail | Fast success |
|
||||
| Download Go deps | 5-10 minutes | 30s-1 minute |
|
||||
|
||||
> **Note**: Users outside China should not configure mirror sources, use default settings.
|
||||
|
||||
```bash
|
||||
# Start services
|
||||
docker-compose up -d
|
||||
|
||||
# 查看日志
|
||||
# View logs
|
||||
docker-compose logs -f
|
||||
|
||||
# 停止服务
|
||||
# Stop services
|
||||
docker-compose down
|
||||
```
|
||||
|
||||
#### 方式二:Docker 命令
|
||||
#### Method 2: Docker Command
|
||||
|
||||
> **注意**:Linux 用户需添加 `--add-host=host.docker.internal:host-gateway` 以访问宿主机服务
|
||||
> **Note**: Linux users need to add `--add-host=host.docker.internal:host-gateway` to access host services
|
||||
|
||||
```bash
|
||||
# 从 Docker Hub 运行
|
||||
# Run from Docker Hub
|
||||
docker run -d \
|
||||
--name huobao-drama \
|
||||
-p 5678:5678 \
|
||||
@@ -257,101 +311,106 @@ docker run -d \
|
||||
--restart unless-stopped \
|
||||
huobao/huobao-drama:latest
|
||||
|
||||
# 查看日志
|
||||
# View logs
|
||||
docker logs -f huobao-drama
|
||||
```
|
||||
|
||||
**本地构建**(可选):
|
||||
**Local Build** (optional):
|
||||
|
||||
```bash
|
||||
docker build -t huobao-drama:latest .
|
||||
docker run -d --name huobao-drama -p 5678:5678 -v $(pwd)/data:/app/data huobao-drama:latest
|
||||
```
|
||||
|
||||
**Docker 部署优势:**
|
||||
- ✅ 开箱即用,内置默认配置
|
||||
- ✅ 环境一致性,避免依赖问题
|
||||
- ✅ 一键启动,无需安装 Go、Node.js、FFmpeg
|
||||
- ✅ 易于迁移和扩展
|
||||
- ✅ 自动健康检查和重启
|
||||
- ✅ 自动处理文件权限,无需手动配置
|
||||
**Docker Deployment Advantages:**
|
||||
|
||||
#### 🔗 访问宿主机服务(Ollama/本地模型)
|
||||
- ✅ Ready to use with default configuration
|
||||
- ✅ Environment consistency, avoiding dependency issues
|
||||
- ✅ One-click start, no need to install Go, Node.js, FFmpeg
|
||||
- ✅ Easy to migrate and scale
|
||||
- ✅ Automatic health checks and restarts
|
||||
- ✅ Automatic file permission handling
|
||||
|
||||
容器已配置支持访问宿主机服务,直接使用 `http://host.docker.internal:端口号` 即可。
|
||||
#### 🔗 Accessing Host Services (Ollama/Local Models)
|
||||
|
||||
**配置步骤:**
|
||||
The container is configured to access host services using `http://host.docker.internal:PORT`.
|
||||
|
||||
**Configuration Steps:**
|
||||
|
||||
1. **Start service on host (listen on all interfaces)**
|
||||
|
||||
1. **宿主机启动服务(监听所有接口)**
|
||||
```bash
|
||||
export OLLAMA_HOST=0.0.0.0:11434 && ollama serve
|
||||
```
|
||||
|
||||
2. **前端 AI 服务配置**
|
||||
2. **Frontend AI Service Configuration**
|
||||
- Base URL: `http://host.docker.internal:11434/v1`
|
||||
- Provider: `openai`
|
||||
- Model: `qwen2.5:latest`
|
||||
|
||||
---
|
||||
|
||||
### 🏭 传统部署方式
|
||||
### 🏭 Traditional Deployment
|
||||
|
||||
#### 1. 编译构建
|
||||
#### 1. Build
|
||||
|
||||
```bash
|
||||
# 1. 构建前端
|
||||
# 1. Build frontend
|
||||
cd web
|
||||
npm run build
|
||||
cd ..
|
||||
|
||||
# 2. 编译后端
|
||||
# 2. Compile backend
|
||||
go build -o huobao-drama .
|
||||
```
|
||||
|
||||
生成文件:
|
||||
- `huobao-drama` - 后端可执行文件
|
||||
- `web/dist/` - 前端静态文件(已嵌入后端)
|
||||
Generated files:
|
||||
|
||||
#### 2. 准备部署文件
|
||||
- `huobao-drama` - Backend executable
|
||||
- `web/dist/` - Frontend static files (embedded in backend)
|
||||
|
||||
#### 2. Prepare Deployment Files
|
||||
|
||||
Files to upload to server:
|
||||
|
||||
需要上传到服务器的文件:
|
||||
```
|
||||
huobao-drama # 后端可执行文件
|
||||
configs/config.yaml # 配置文件
|
||||
data/ # 数据目录(可选,首次运行自动创建)
|
||||
huobao-drama # Backend executable
|
||||
configs/config.yaml # Configuration file
|
||||
data/ # Data directory (optional, auto-created on first run)
|
||||
```
|
||||
|
||||
#### 3. 服务器配置
|
||||
#### 3. Server Configuration
|
||||
|
||||
```bash
|
||||
# 上传文件到服务器
|
||||
# Upload files to server
|
||||
scp huobao-drama user@server:/opt/huobao-drama/
|
||||
scp configs/config.yaml user@server:/opt/huobao-drama/configs/
|
||||
|
||||
# SSH登录服务器
|
||||
# SSH to server
|
||||
ssh user@server
|
||||
|
||||
# 修改配置文件
|
||||
# Modify configuration file
|
||||
cd /opt/huobao-drama
|
||||
vim configs/config.yaml
|
||||
# 设置mode为production
|
||||
# 配置域名和存储路径
|
||||
# Set mode to production
|
||||
# Configure domain and storage path
|
||||
|
||||
# 创建数据目录并设置权限(重要!)
|
||||
# 注意:将 YOUR_USER 替换为实际运行服务的用户名(如 www-data、ubuntu、deploy 等)
|
||||
# Create data directory and set permissions (Important!)
|
||||
# Note: Replace YOUR_USER with actual user running the service (e.g., www-data, ubuntu, deploy)
|
||||
sudo mkdir -p /opt/huobao-drama/data/storage
|
||||
sudo chown -R YOUR_USER:YOUR_USER /opt/huobao-drama/data
|
||||
sudo chmod -R 755 /opt/huobao-drama/data
|
||||
|
||||
# 赋予执行权限
|
||||
# Grant execute permission
|
||||
chmod +x huobao-drama
|
||||
|
||||
# 启动服务
|
||||
# Start service
|
||||
./huobao-drama
|
||||
```
|
||||
|
||||
#### 4. 使用 systemd 管理服务
|
||||
#### 4. Manage Service with systemd
|
||||
|
||||
创建服务文件 `/etc/systemd/system/huobao-drama.service`:
|
||||
Create service file `/etc/systemd/system/huobao-drama.service`:
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
@@ -366,14 +425,15 @@ ExecStart=/opt/huobao-drama/huobao-drama
|
||||
Restart=on-failure
|
||||
RestartSec=10
|
||||
|
||||
# 环境变量(可选)
|
||||
# Environment variables (optional)
|
||||
# Environment="GIN_MODE=release"
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
启动服务:
|
||||
Start service:
|
||||
|
||||
```bash
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable huobao-drama
|
||||
@@ -381,38 +441,40 @@ sudo systemctl start huobao-drama
|
||||
sudo systemctl status huobao-drama
|
||||
```
|
||||
|
||||
**⚠️ 常见问题:SQLite 写权限错误**
|
||||
**⚠️ Common Issue: SQLite Write Permission Error**
|
||||
|
||||
如果遇到 `attempt to write a readonly database` 错误:
|
||||
If you encounter `attempt to write a readonly database` error:
|
||||
|
||||
```bash
|
||||
# 1. 确认当前运行服务的用户
|
||||
# 1. Check current user running the service
|
||||
sudo systemctl status huobao-drama | grep "Main PID"
|
||||
ps aux | grep huobao-drama
|
||||
|
||||
# 2. 修复权限(将 YOUR_USER 替换为实际用户名)
|
||||
# 2. Fix permissions (replace YOUR_USER with actual username)
|
||||
sudo chown -R YOUR_USER:YOUR_USER /opt/huobao-drama/data
|
||||
sudo chmod -R 755 /opt/huobao-drama/data
|
||||
|
||||
# 3. 验证权限
|
||||
# 3. Verify permissions
|
||||
ls -la /opt/huobao-drama/data
|
||||
# 应该显示所有者为运行服务的用户
|
||||
# Should show owner as the user running the service
|
||||
|
||||
# 4. 重启服务
|
||||
# 4. Restart service
|
||||
sudo systemctl restart huobao-drama
|
||||
```
|
||||
|
||||
**原因说明**:
|
||||
- SQLite 需要对数据库文件 **和** 所在目录都有写权限
|
||||
- 需要在目录中创建临时文件(如 `-wal`、`-journal`)
|
||||
- **关键**:确保 systemd 配置中的 `User` 与数据目录所有者一致
|
||||
**Reason:**
|
||||
|
||||
**常用用户名**:
|
||||
- Ubuntu/Debian: `www-data`、`ubuntu`
|
||||
- CentOS/RHEL: `nginx`、`apache`
|
||||
- 自定义部署: `deploy`、`app`、当前登录用户
|
||||
- SQLite requires write permission on both the database file **and** its directory
|
||||
- Needs to create temporary files in the directory (e.g., `-wal`, `-journal`)
|
||||
- **Key**: Ensure systemd `User` matches data directory owner
|
||||
|
||||
#### 5. Nginx 反向代理
|
||||
**Common Usernames:**
|
||||
|
||||
- Ubuntu/Debian: `www-data`, `ubuntu`
|
||||
- CentOS/RHEL: `nginx`, `apache`
|
||||
- Custom deployment: `deploy`, `app`, current logged-in user
|
||||
|
||||
#### 5. Nginx Reverse Proxy
|
||||
|
||||
```nginx
|
||||
server {
|
||||
@@ -426,7 +488,7 @@ server {
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
}
|
||||
|
||||
# 静态文件直接访问
|
||||
# Direct access to static files
|
||||
location /static/ {
|
||||
alias /opt/huobao-drama/data/storage/;
|
||||
}
|
||||
@@ -435,102 +497,129 @@ server {
|
||||
|
||||
---
|
||||
|
||||
## 🎨 技术栈
|
||||
## 🎨 Tech Stack
|
||||
|
||||
### 后端技术
|
||||
- **语言**: Go 1.23+
|
||||
- **Web框架**: Gin 1.9+
|
||||
### Backend
|
||||
|
||||
- **Language**: Go 1.23+
|
||||
- **Web Framework**: Gin 1.9+
|
||||
- **ORM**: GORM
|
||||
- **数据库**: SQLite
|
||||
- **日志**: Zap
|
||||
- **视频处理**: FFmpeg
|
||||
- **AI服务**: OpenAI、Gemini、火山等
|
||||
- **Database**: SQLite
|
||||
- **Logging**: Zap
|
||||
- **Video Processing**: FFmpeg
|
||||
- **AI Services**: OpenAI, Gemini, Doubao, etc.
|
||||
|
||||
### 前端技术
|
||||
- **框架**: Vue 3.4+
|
||||
- **语言**: TypeScript 5+
|
||||
- **构建工具**: Vite 5
|
||||
- **UI组件**: Element Plus
|
||||
- **CSS框架**: TailwindCSS
|
||||
- **状态管理**: Pinia
|
||||
- **路由**: Vue Router 4
|
||||
### Frontend
|
||||
|
||||
### 开发工具
|
||||
- **包管理**: Go Modules, npm
|
||||
- **代码规范**: ESLint, Prettier
|
||||
- **版本控制**: Git
|
||||
- **Framework**: Vue 3.4+
|
||||
- **Language**: TypeScript 5+
|
||||
- **Build Tool**: Vite 5
|
||||
- **UI Components**: Element Plus
|
||||
- **CSS Framework**: TailwindCSS
|
||||
- **State Management**: Pinia
|
||||
- **Router**: Vue Router 4
|
||||
|
||||
### Development Tools
|
||||
|
||||
- **Package Management**: Go Modules, npm
|
||||
- **Code Standards**: ESLint, Prettier
|
||||
- **Version Control**: Git
|
||||
|
||||
---
|
||||
|
||||
## 📝 常见问题
|
||||
## 📝 FAQ
|
||||
|
||||
### Q: Docker 容器如何访问宿主机的 Ollama?
|
||||
A: 使用 `http://host.docker.internal:11434/v1` 作为 Base URL。注意两点:
|
||||
1. 宿主机 Ollama 需监听 `0.0.0.0`:`export OLLAMA_HOST=0.0.0.0:11434 && ollama serve`
|
||||
2. Linux 用户使用 `docker run` 需添加:`--add-host=host.docker.internal:host-gateway`
|
||||
### Q: How can Docker containers access Ollama on the host?
|
||||
|
||||
详见:[DOCKER_HOST_ACCESS.md](docs/DOCKER_HOST_ACCESS.md)
|
||||
A: Use `http://host.docker.internal:11434/v1` as Base URL. Note two things:
|
||||
|
||||
### Q: FFmpeg未安装或找不到?
|
||||
A: 确保FFmpeg已安装并在PATH环境变量中。运行 `ffmpeg -version` 验证。
|
||||
1. Host Ollama needs to listen on `0.0.0.0`: `export OLLAMA_HOST=0.0.0.0:11434 && ollama serve`
|
||||
2. Linux users using `docker run` need to add: `--add-host=host.docker.internal:host-gateway`
|
||||
|
||||
### Q: 前端无法连接后端API?
|
||||
A: 检查后端是否启动,端口是否正确。开发模式下前端代理配置在 `web/vite.config.ts`。
|
||||
See: [DOCKER_HOST_ACCESS.md](docs/DOCKER_HOST_ACCESS.md)
|
||||
|
||||
### Q: 数据库表未创建?
|
||||
A: GORM会在首次启动时自动创建表,检查日志确认迁移是否成功。
|
||||
### Q: FFmpeg not installed or not found?
|
||||
|
||||
A: Ensure FFmpeg is installed and in the PATH environment variable. Verify with `ffmpeg -version`.
|
||||
|
||||
### Q: Frontend cannot connect to backend API?
|
||||
|
||||
A: Check if backend is running and port is correct. In development mode, frontend proxy config is in `web/vite.config.ts`.
|
||||
|
||||
### Q: Database tables not created?
|
||||
|
||||
A: GORM automatically creates tables on first startup, check logs to confirm migration success.
|
||||
|
||||
---
|
||||
|
||||
## <EFBFBD> 更新日志 / Changelog
|
||||
## 📋 Changelog
|
||||
|
||||
### v1.0.2 (2026-01-16)
|
||||
|
||||
#### 🚀 重大更新
|
||||
- SQLite 纯 Go 驱动(`modernc.org/sqlite`),支持 `CGO_ENABLED=0` 跨平台编译
|
||||
- 优化并发性能(WAL 模式),解决 "database is locked" 错误
|
||||
- Docker 跨平台支持 `host.docker.internal` 访问宿主机服务
|
||||
- 精简文档和部署指南
|
||||
#### 🚀 Major Updates
|
||||
|
||||
- Pure Go SQLite driver (`modernc.org/sqlite`), supports `CGO_ENABLED=0` cross-platform compilation
|
||||
- Optimized concurrency performance (WAL mode), resolved "database is locked" errors
|
||||
- Docker cross-platform support for `host.docker.internal` to access host services
|
||||
- Streamlined documentation and deployment guides
|
||||
|
||||
### v1.0.1 (2026-01-14)
|
||||
|
||||
#### 🐛 Bug Fixes / 🔧 Improvements
|
||||
- 修复视频生成 API 响应解析问题
|
||||
- 添加 OpenAI Sora 视频端点配置
|
||||
- 优化错误处理和日志输出
|
||||
|
||||
- Fixed video generation API response parsing issues
|
||||
- Added OpenAI Sora video endpoint configuration
|
||||
- Optimized error handling and logging
|
||||
|
||||
---
|
||||
|
||||
## 🤝 贡献指南
|
||||
## 🤝 Contributing
|
||||
|
||||
欢迎提交 Issue 和 Pull Request!
|
||||
Issues and Pull Requests are welcome!
|
||||
|
||||
1. Fork 本项目
|
||||
2. 创建特性分支 (`git checkout -b feature/AmazingFeature`)
|
||||
3. 提交改动 (`git commit -m 'Add some AmazingFeature'`)
|
||||
4. 推送到分支 (`git push origin feature/AmazingFeature`)
|
||||
5. 开启 Pull Request
|
||||
1. Fork this project
|
||||
2. Create a feature branch (`git checkout -b feature/AmazingFeature`)
|
||||
3. Commit your changes (`git commit -m 'Add some AmazingFeature'`)
|
||||
4. Push to the branch (`git push origin feature/AmazingFeature`)
|
||||
5. Open a Pull Request
|
||||
|
||||
---
|
||||
|
||||
## API配置站点
|
||||
2分钟完成配置:[API聚合站点](https://api.chatfire.site/models)
|
||||
## API Configuration Site
|
||||
|
||||
## 📧 联系方式
|
||||
商务联系V:dangbao1117
|
||||
## 项目交流群
|
||||

|
||||
- 提交 [Issue](../../issues)
|
||||
- 发送邮件至项目维护者
|
||||
Configure in 2 minutes: [API Aggregation Site](https://api.chatfire.site/models)
|
||||
|
||||
---
|
||||
|
||||
## 👨💻 About Us
|
||||
|
||||
**AI Huobao - AI Studio Startup**
|
||||
|
||||
- 🏠 **Location**: Nanjing, China
|
||||
- 🚀 **Status**: Startup in Progress
|
||||
- 📧 **Email**: [18550175439@163.com](mailto:18550175439@163.com)
|
||||
- 💬 **WeChat**: dangbao1117 (Personal WeChat - No technical support)
|
||||
- 🐙 **GitHub**: [https://github.com/chatfire-AI/huobao-drama](https://github.com/chatfire-AI/huobao-drama)
|
||||
|
||||
> _"Let AI help us do more creative things"_
|
||||
|
||||
## Community Group
|
||||
|
||||

|
||||
|
||||
- Submit [Issue](../../issues)
|
||||
- Email project maintainers
|
||||
|
||||
---
|
||||
|
||||
<div align="center">
|
||||
|
||||
**⭐ 如果这个项目对你有帮助,请给一个Star!**
|
||||
**⭐ If this project helps you, please give it a Star!**
|
||||
|
||||
## Star History
|
||||
|
||||
[](https://www.star-history.com/#chatfire-AI/huobao-drama&type=date&legend=top-left)
|
||||
|
||||
Made with ❤️ by Huobao Team
|
||||
|
||||
</div>
|
||||
|
||||
@@ -74,6 +74,28 @@ func (h *SceneHandler) GenerateSceneImage(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
func (h *SceneHandler) UpdateScenePrompt(c *gin.Context) {
|
||||
sceneID := c.Param("scene_id")
|
||||
|
||||
var req services2.UpdateScenePromptRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.BadRequest(c, "Invalid request")
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.sceneService.UpdateScenePrompt(sceneID, &req); err != nil {
|
||||
h.log.Errorw("Failed to update scene prompt", "error", err, "scene_id", sceneID)
|
||||
if err.Error() == "scene not found" {
|
||||
response.NotFound(c, "场景不存在")
|
||||
return
|
||||
}
|
||||
response.InternalError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, gin.H{"message": "场景提示词已更新"})
|
||||
}
|
||||
|
||||
func (h *SceneHandler) DeleteScene(c *gin.Context) {
|
||||
sceneID := c.Param("scene_id")
|
||||
|
||||
|
||||
@@ -137,6 +137,7 @@ func SetupRouter(cfg *config.Config, db *gorm.DB, log *logger.Logger, localStora
|
||||
scenes := api.Group("/scenes")
|
||||
{
|
||||
scenes.PUT("/:scene_id", sceneHandler.UpdateScene)
|
||||
scenes.PUT("/:scene_id/prompt", sceneHandler.UpdateScenePrompt)
|
||||
scenes.DELETE("/:scene_id", sceneHandler.DeleteScene)
|
||||
scenes.POST("/generate-image", sceneHandler.GenerateSceneImage)
|
||||
}
|
||||
|
||||
@@ -399,6 +399,28 @@ func (s *StoryboardCompositionService) GenerateSceneImage(req *GenerateSceneImag
|
||||
return nil, fmt.Errorf("image generation service not available")
|
||||
}
|
||||
|
||||
type UpdateScenePromptRequest struct {
|
||||
Prompt string `json:"prompt"`
|
||||
}
|
||||
|
||||
func (s *StoryboardCompositionService) UpdateScenePrompt(sceneID string, req *UpdateScenePromptRequest) error {
|
||||
var scene models.Scene
|
||||
if err := s.db.Where("id = ?", sceneID).First(&scene).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return fmt.Errorf("scene not found")
|
||||
}
|
||||
return fmt.Errorf("failed to find scene: %w", err)
|
||||
}
|
||||
|
||||
scene.Prompt = req.Prompt
|
||||
if err := s.db.Save(&scene).Error; err != nil {
|
||||
return fmt.Errorf("failed to update scene prompt: %w", err)
|
||||
}
|
||||
|
||||
s.log.Infow("Scene prompt updated", "scene_id", sceneID, "prompt", req.Prompt)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *StoryboardCompositionService) DeleteScene(sceneID string) error {
|
||||
var scene models.Scene
|
||||
if err := s.db.Where("id = ?", sceneID).First(&scene).Error; err != nil {
|
||||
|
||||
@@ -5,6 +5,15 @@ services:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
args:
|
||||
# Docker Hub 镜像源(注意末尾斜杠)
|
||||
DOCKER_REGISTRY: ${DOCKER_REGISTRY:-}
|
||||
# npm 镜像源
|
||||
NPM_REGISTRY: ${NPM_REGISTRY:-}
|
||||
# Go 代理
|
||||
GO_PROXY: ${GO_PROXY:-}
|
||||
# Alpine apk 镜像源
|
||||
ALPINE_MIRROR: ${ALPINE_MIRROR:-}
|
||||
ports:
|
||||
- "5678:5678"
|
||||
volumes:
|
||||
|
||||
BIN
drama.png
BIN
drama.png
Binary file not shown.
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 182 KiB |
@@ -168,6 +168,7 @@ func (c *OpenAIClient) sendChatRequest(req *ChatCompletionRequest) (*ChatComplet
|
||||
if len(chatResp.Choices) > 0 {
|
||||
finishReason := chatResp.Choices[0].FinishReason
|
||||
content := chatResp.Choices[0].Message.Content
|
||||
usage := chatResp.Usage
|
||||
|
||||
fmt.Printf("OpenAI: finish_reason=%s, content_length=%d\n", finishReason, len(content))
|
||||
|
||||
@@ -175,7 +176,7 @@ func (c *OpenAIClient) sendChatRequest(req *ChatCompletionRequest) (*ChatComplet
|
||||
return nil, fmt.Errorf("AI内容被安全过滤器拦截,可能因为:\n1. 请求内容触发了安全策略\n2. 生成的内容包含敏感信息\n3. 建议:调整输入内容或联系API提供商调整过滤策略")
|
||||
}
|
||||
|
||||
if content == "" && finishReason != "stop" {
|
||||
if usage.TotalTokens == 0 && finishReason != "stop" {
|
||||
return nil, fmt.Errorf("AI返回内容为空 (finish_reason: %s),可能的原因:\n1. 内容被过滤\n2. Token限制\n3. API异常", finishReason)
|
||||
}
|
||||
}
|
||||
@@ -238,7 +239,7 @@ func (c *OpenAIClient) TestConnection() error {
|
||||
},
|
||||
}
|
||||
|
||||
_, err := c.ChatCompletion(messages, WithMaxTokens(10))
|
||||
_, err := c.ChatCompletion(messages, WithMaxTokens(50))
|
||||
if err != nil {
|
||||
fmt.Printf("OpenAI: TestConnection failed: %v\n", err)
|
||||
} else {
|
||||
|
||||
@@ -107,7 +107,7 @@ func NewGeminiImageClient(baseURL, apiKey, model, endpoint string) *GeminiImageC
|
||||
|
||||
func (c *GeminiImageClient) GenerateImage(prompt string, opts ...ImageOption) (*ImageResult, error) {
|
||||
options := &ImageOptions{
|
||||
Size: "1024x1024",
|
||||
Size: "1920x1920",
|
||||
Quality: "standard",
|
||||
}
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ func NewVolcEngineImageClient(baseURL, apiKey, model, endpoint, queryEndpoint st
|
||||
|
||||
func (c *VolcEngineImageClient) GenerateImage(prompt string, opts ...ImageOption) (*ImageResult, error) {
|
||||
options := &ImageOptions{
|
||||
Size: "1024x1024",
|
||||
Size: "1920x1920",
|
||||
Quality: "standard",
|
||||
}
|
||||
|
||||
|
||||
@@ -108,8 +108,12 @@ export const dramaAPI = {
|
||||
return request.put(`/scenes/${sceneId}`, data)
|
||||
},
|
||||
|
||||
generateSceneImage(data: { scene_id: string; prompt?: string; model?: string }) {
|
||||
return request.post('/scenes/generate-image', data)
|
||||
generateSceneImage(data: { scene_id: number; prompt?: string; model?: string }) {
|
||||
return request.post<{ image_generation: { id: number } }>('/scenes/generate-image', data)
|
||||
},
|
||||
|
||||
updateScenePrompt(sceneId: string, prompt: string) {
|
||||
return request.put(`/scenes/${sceneId}/prompt`, { prompt })
|
||||
},
|
||||
|
||||
deleteScene(sceneId: string) {
|
||||
|
||||
@@ -68,7 +68,9 @@ export default {
|
||||
tip: '提示',
|
||||
status: '状态',
|
||||
createdAt: '创建时间',
|
||||
updatedAt: '更新时间'
|
||||
updatedAt: '更新时间',
|
||||
name: '名称',
|
||||
description: '描述'
|
||||
},
|
||||
settings: {
|
||||
title: '设置',
|
||||
|
||||
@@ -241,10 +241,10 @@
|
||||
<el-dialog v-model="addSceneDialogVisible" :title="$t('common.add')" width="600px">
|
||||
<el-form :model="newScene" label-width="100px">
|
||||
<el-form-item :label="$t('common.name')">
|
||||
<el-input v-model="newScene.name" :placeholder="$t('common.name')" />
|
||||
<el-input v-model="newScene.location" :placeholder="$t('common.name')" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('common.description')">
|
||||
<el-input v-model="newScene.description" type="textarea" :rows="4" :placeholder="$t('common.description')" />
|
||||
<el-input v-model="newScene.prompt" type="textarea" :rows="4" :placeholder="$t('common.description')" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
@@ -285,8 +285,8 @@ const newCharacter = ref({
|
||||
})
|
||||
|
||||
const newScene = ref({
|
||||
name: '',
|
||||
description: ''
|
||||
location: '',
|
||||
prompt: ''
|
||||
})
|
||||
|
||||
const episodesCount = computed(() => drama.value?.episodes?.length || 0)
|
||||
@@ -494,14 +494,14 @@ const deleteCharacter = async (character: any) => {
|
||||
|
||||
const openAddSceneDialog = () => {
|
||||
newScene.value = {
|
||||
name: '',
|
||||
description: ''
|
||||
location: '',
|
||||
prompt: ''
|
||||
}
|
||||
addSceneDialogVisible.value = true
|
||||
}
|
||||
|
||||
const addScene = async () => {
|
||||
if (!newScene.value.name.trim()) {
|
||||
if (!newScene.value.location.trim()) {
|
||||
ElMessage.warning('请输入场景名称')
|
||||
return
|
||||
}
|
||||
@@ -517,7 +517,11 @@ const addScene = async () => {
|
||||
}
|
||||
|
||||
const editScene = (scene: any) => {
|
||||
ElMessage.info('编辑功能开发中')
|
||||
newScene.value = {
|
||||
location: scene.location || scene.name || '',
|
||||
prompt: scene.prompt || scene.description || ''
|
||||
}
|
||||
addSceneDialogVisible.value = true
|
||||
}
|
||||
|
||||
const deleteScene = async (scene: any) => {
|
||||
|
||||
@@ -1601,8 +1601,9 @@ const saveShotEdit = async () => {
|
||||
// 对话框相关方法
|
||||
const openPromptDialog = (item: any, type: 'character' | 'scene') => {
|
||||
currentEditItem.value = item
|
||||
currentEditItem.value.name = item.name || item.location
|
||||
currentEditType.value = type
|
||||
editPrompt.value = item.appearance || item.description || ''
|
||||
editPrompt.value = item.prompt || item.appearance || item.description || ''
|
||||
promptDialogVisible.value = true
|
||||
}
|
||||
|
||||
@@ -1614,10 +1615,32 @@ const savePrompt = async () => {
|
||||
})
|
||||
await generateCharacterImage(currentEditItem.value.id)
|
||||
} else {
|
||||
await dramaAPI.generateSceneImage({
|
||||
// 1. 先保存场景提示词
|
||||
await dramaAPI.updateScenePrompt(currentEditItem.value.id.toString(), editPrompt.value)
|
||||
|
||||
// 2. 生成场景图片
|
||||
const model = selectedImageModel.value || undefined
|
||||
const response = await dramaAPI.generateSceneImage({
|
||||
scene_id: parseInt(currentEditItem.value.id),
|
||||
prompt: editPrompt.value
|
||||
prompt: editPrompt.value,
|
||||
model
|
||||
})
|
||||
const imageGenId = response.image_generation?.id
|
||||
|
||||
// 3. 轮询图片生成状态
|
||||
if (imageGenId) {
|
||||
ElMessage.info('场景图片生成中,请稍候...')
|
||||
generatingSceneImages.value[currentEditItem.value.id] = true
|
||||
pollImageStatus(imageGenId, async () => {
|
||||
await loadDramaData()
|
||||
ElMessage.success('场景图片生成完成!')
|
||||
}).finally(() => {
|
||||
generatingSceneImages.value[currentEditItem.value.id] = false
|
||||
})
|
||||
} else {
|
||||
ElMessage.success('场景图片生成已启动')
|
||||
await loadDramaData()
|
||||
}
|
||||
}
|
||||
promptDialogVisible.value = false
|
||||
} catch (error: any) {
|
||||
|
||||
@@ -386,6 +386,32 @@
|
||||
<!-- 首帧 -->
|
||||
<div v-show="selectedVideoFrameType === 'first'" class="image-scroll-container"
|
||||
style="max-height: 280px; overflow-y: auto; overflow-x: hidden;">
|
||||
|
||||
<!-- 上一镜头尾帧推荐(紧凑版) -->
|
||||
<div v-if="previousStoryboardLastFrames.length > 0" class="previous-frame-section">
|
||||
<div style="display: flex; align-items: center; gap: 6px; margin-bottom: 6px;">
|
||||
<el-tag size="small" type="primary">
|
||||
上一镜头 #{{ previousStoryboard?.storyboard_number }} 尾帧
|
||||
</el-tag>
|
||||
<span class="hint-text">点击添加为首帧参考</span>
|
||||
</div>
|
||||
<div style="display: flex; gap: 8px; flex-wrap: wrap;">
|
||||
<div v-for="img in previousStoryboardLastFrames" :key="'prev-' + img.id"
|
||||
class="reference-item"
|
||||
:class="{ selected: selectedImagesForVideo.includes(img.id) }"
|
||||
style="position: relative; border: 2px solid #1890ff; border-radius: 4px; overflow: hidden; cursor: pointer;"
|
||||
@click="selectPreviousLastFrame(img)">
|
||||
<el-image :src="img.image_url" fit="cover"
|
||||
style="width: 60px; height: 40px; display: block; pointer-events: none;" />
|
||||
<div v-if="selectedImagesForVideo.includes(img.id)"
|
||||
style="position: absolute; top: 0; right: 0; background: #52c41a; color: #fff; font-size: 10px; padding: 1px 4px;">
|
||||
✓
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 当前镜头首帧列表 -->
|
||||
<div class="reference-grid"
|
||||
style="display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; max-width: 600px;">
|
||||
<div
|
||||
@@ -404,7 +430,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<el-empty
|
||||
v-if="!videoReferenceImages.some(i => i.status === 'completed' && i.image_url && i.frame_type === 'first')"
|
||||
v-if="!videoReferenceImages.some(i => i.status === 'completed' && i.image_url && i.frame_type === 'first') && previousStoryboardLastFrames.length === 0"
|
||||
description="暂无首帧图片" size="small" />
|
||||
</div>
|
||||
|
||||
@@ -909,7 +935,7 @@ import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import {
|
||||
ArrowLeft, Plus, Picture, VideoPlay, VideoPause, View, Setting,
|
||||
Upload, MagicStick, VideoCamera, ZoomIn, ZoomOut, Top, Bottom, Check, Close, Right,
|
||||
Timer, Calendar, Clock, Loading, WarningFilled, Delete
|
||||
Timer, Calendar, Clock, Loading, WarningFilled, Delete, Connection
|
||||
} from '@element-plus/icons-vue'
|
||||
import { dramaAPI } from '@/api/drama'
|
||||
import { generateFramePrompt, type FrameType } from '@/api/frame'
|
||||
@@ -1254,6 +1280,67 @@ const currentStoryboard = computed(() => {
|
||||
return storyboards.value.find(s => String(s.id) === String(currentStoryboardId.value)) || null
|
||||
})
|
||||
|
||||
// 获取上一个镜头
|
||||
const previousStoryboard = computed(() => {
|
||||
if (!currentStoryboardId.value || storyboards.value.length < 2) return null
|
||||
const currentIndex = storyboards.value.findIndex(s => String(s.id) === String(currentStoryboardId.value))
|
||||
if (currentIndex <= 0) return null
|
||||
return storyboards.value[currentIndex - 1]
|
||||
})
|
||||
|
||||
// 上一个镜头的尾帧图片列表(支持多个)
|
||||
const previousStoryboardLastFrames = ref<any[]>([])
|
||||
|
||||
// 加载上一个镜头的尾帧
|
||||
const loadPreviousStoryboardLastFrame = async () => {
|
||||
if (!previousStoryboard.value) {
|
||||
previousStoryboardLastFrames.value = []
|
||||
return
|
||||
}
|
||||
try {
|
||||
const result = await imageAPI.listImages({
|
||||
storyboard_id: previousStoryboard.value.id,
|
||||
frame_type: 'last',
|
||||
page: 1,
|
||||
page_size: 10
|
||||
})
|
||||
const images = result.items || []
|
||||
previousStoryboardLastFrames.value = images.filter((img: any) => img.status === 'completed' && img.image_url)
|
||||
} catch (error) {
|
||||
console.error('加载上一镜头尾帧失败:', error)
|
||||
previousStoryboardLastFrames.value = []
|
||||
}
|
||||
}
|
||||
|
||||
// 选择上一镜头尾帧作为首帧参考
|
||||
const selectPreviousLastFrame = (img: any) => {
|
||||
// 检查是否已选中,已选中则取消
|
||||
const currentIndex = selectedImagesForVideo.value.indexOf(img.id)
|
||||
if (currentIndex > -1) {
|
||||
selectedImagesForVideo.value.splice(currentIndex, 1)
|
||||
ElMessage.success('已取消首帧参考')
|
||||
return
|
||||
}
|
||||
|
||||
// 参考handleImageSelect的逻辑,根据模式处理
|
||||
if (!selectedReferenceMode.value || selectedReferenceMode.value === 'single') {
|
||||
// 单图模式或未选模式:直接替换
|
||||
selectedImagesForVideo.value = [img.id]
|
||||
} else if (selectedReferenceMode.value === 'first_last') {
|
||||
// 首尾帧模式:作为首帧参考
|
||||
selectedImagesForVideo.value = [img.id]
|
||||
} else if (selectedReferenceMode.value === 'multiple') {
|
||||
// 多图模式:添加到列表
|
||||
const capability = currentModelCapability.value
|
||||
if (capability && selectedImagesForVideo.value.length >= capability.maxImages) {
|
||||
ElMessage.warning(`最多只能选择${capability.maxImages}张图片`)
|
||||
return
|
||||
}
|
||||
selectedImagesForVideo.value.push(img.id)
|
||||
}
|
||||
ElMessage.success('已添加为首帧参考')
|
||||
}
|
||||
|
||||
// 监听帧类型切换,从存储中加载或清空
|
||||
watch(selectedFrameType, (newType) => {
|
||||
// 切换帧类型时,停止之前的轮询,避免旧结果覆盖新帧类型
|
||||
@@ -1297,6 +1384,7 @@ watch(currentStoryboard, async (newStoryboard) => {
|
||||
generatedImages.value = []
|
||||
generatedVideos.value = []
|
||||
videoReferenceImages.value = []
|
||||
previousStoryboardLastFrames.value = []
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1325,6 +1413,9 @@ watch(currentStoryboard, async (newStoryboard) => {
|
||||
|
||||
// 加载该分镜的视频列表
|
||||
await loadStoryboardVideos(newStoryboard.id)
|
||||
|
||||
// 加载上一镜头的尾帧
|
||||
await loadPreviousStoryboardLastFrame()
|
||||
})
|
||||
|
||||
// 监听提示词变化,自动保存到sessionStorage
|
||||
@@ -1879,13 +1970,17 @@ const selectedImageObjects = computed(() => {
|
||||
const firstFrameSlotImage = computed(() => {
|
||||
if (selectedImagesForVideo.value.length === 0) return null
|
||||
const firstImageId = selectedImagesForVideo.value[0]
|
||||
return videoReferenceImages.value.find(img => img.id === firstImageId)
|
||||
// 同时搜索当前镜头图片和上一镜头尾帧
|
||||
return videoReferenceImages.value.find(img => img.id === firstImageId)
|
||||
|| previousStoryboardLastFrames.value.find(img => img.id === firstImageId)
|
||||
})
|
||||
|
||||
// 首尾帧模式:获取尾帧图片
|
||||
const lastFrameSlotImage = computed(() => {
|
||||
if (!selectedLastImageForVideo.value) return null
|
||||
// 同时搜索当前镜头图片和上一镜头尾帧
|
||||
return videoReferenceImages.value.find(img => img.id === selectedLastImageForVideo.value)
|
||||
|| previousStoryboardLastFrames.value.find(img => img.id === selectedLastImageForVideo.value)
|
||||
})
|
||||
|
||||
// 移除已选择的图片
|
||||
@@ -1924,7 +2019,9 @@ const generateVideo = async () => {
|
||||
// 获取第一张选中的图片(仅在需要图片的模式下)
|
||||
let selectedImage = null
|
||||
if (selectedReferenceMode.value !== 'none' && selectedImagesForVideo.value.length > 0) {
|
||||
// 同时搜索当前镜头图片和上一镜头尾帧
|
||||
selectedImage = videoReferenceImages.value.find(img => img.id === selectedImagesForVideo.value[0])
|
||||
|| previousStoryboardLastFrames.value.find(img => img.id === selectedImagesForVideo.value[0])
|
||||
if (!selectedImage || !selectedImage.image_url) {
|
||||
ElMessage.error('请选择有效的参考图片')
|
||||
return
|
||||
@@ -1956,9 +2053,11 @@ const generateVideo = async () => {
|
||||
break
|
||||
|
||||
case 'first_last':
|
||||
// 首尾帧模式
|
||||
// 首尾帧模式(同时搜索当前镜头图片和上一镜头尾帧)
|
||||
const firstImage = videoReferenceImages.value.find(img => img.id === selectedImagesForVideo.value[0])
|
||||
|| previousStoryboardLastFrames.value.find(img => img.id === selectedImagesForVideo.value[0])
|
||||
const lastImage = videoReferenceImages.value.find(img => img.id === selectedLastImageForVideo.value)
|
||||
|| previousStoryboardLastFrames.value.find(img => img.id === selectedLastImageForVideo.value)
|
||||
|
||||
if (firstImage?.image_url) {
|
||||
requestParams.first_frame_url = firstImage.image_url
|
||||
@@ -4031,6 +4130,19 @@ onBeforeUnmount(() => {
|
||||
}
|
||||
}
|
||||
|
||||
.previous-frame-section {
|
||||
margin-bottom: 12px;
|
||||
padding: 8px;
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-primary);
|
||||
border-radius: 6px;
|
||||
|
||||
.hint-text {
|
||||
color: var(--text-muted);
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
||||
.reference-grid {
|
||||
display: grid !important;
|
||||
grid-template-columns: repeat(4, 1fr) !important;
|
||||
|
||||
Reference in New Issue
Block a user