vcshort 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- vcshort-0.2.0/PKG-INFO +255 -0
- vcshort-0.2.0/README.md +232 -0
- vcshort-0.2.0/pyproject.toml +40 -0
- vcshort-0.2.0/setup.cfg +4 -0
- vcshort-0.2.0/src/vcshort/__init__.py +3 -0
- vcshort-0.2.0/src/vcshort/__main__.py +6 -0
- vcshort-0.2.0/src/vcshort/apply_flashback_fog.py +136 -0
- vcshort-0.2.0/src/vcshort/cli.py +253 -0
- vcshort-0.2.0/src/vcshort/compose_chapter.py +274 -0
- vcshort-0.2.0/src/vcshort/config_manager.py +115 -0
- vcshort-0.2.0/src/vcshort/extract.py +375 -0
- vcshort-0.2.0/src/vcshort/fix_image.py +240 -0
- vcshort-0.2.0/src/vcshort/gen_image.py +417 -0
- vcshort-0.2.0/src/vcshort/gen_keyframe.py +372 -0
- vcshort-0.2.0/src/vcshort/gen_shots.py +733 -0
- vcshort-0.2.0/src/vcshort/gen_video.py +625 -0
- vcshort-0.2.0/src/vcshort/gen_voice.py +287 -0
- vcshort-0.2.0/src/vcshort/init.py +151 -0
- vcshort-0.2.0/src/vcshort/reshape_image.py +244 -0
- vcshort-0.2.0/src/vcshort.egg-info/PKG-INFO +255 -0
- vcshort-0.2.0/src/vcshort.egg-info/SOURCES.txt +23 -0
- vcshort-0.2.0/src/vcshort.egg-info/dependency_links.txt +1 -0
- vcshort-0.2.0/src/vcshort.egg-info/entry_points.txt +2 -0
- vcshort-0.2.0/src/vcshort.egg-info/requires.txt +5 -0
- vcshort-0.2.0/src/vcshort.egg-info/top_level.txt +1 -0
vcshort-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: vcshort
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: AI 短视频制作 CLI:剧本 → 资产 → 分镜 → 视频 → 合成
|
|
5
|
+
Author: skywalker
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/itrxiAi/vc-short-plugin
|
|
8
|
+
Project-URL: Repository, https://github.com/itrxiAi/vc-short-plugin
|
|
9
|
+
Keywords: short-video,ai,storyboard,video-generation
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Requires-Python: >=3.10
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
Requires-Dist: requests>=2.28
|
|
19
|
+
Requires-Dist: PyYAML>=6.0
|
|
20
|
+
Requires-Dist: ruamel.yaml>=0.17
|
|
21
|
+
Requires-Dist: opencv-python-headless<5,>=4.8
|
|
22
|
+
Requires-Dist: imageio-ffmpeg>=0.4.9
|
|
23
|
+
|
|
24
|
+
# vc-short-plugin
|
|
25
|
+
|
|
26
|
+
AI 短视频制作插件(剧本 → 资产 → 分镜 → 视频 → 合成),支持 WorkBuddy、Devin、Claude Code、Cursor。
|
|
27
|
+
|
|
28
|
+
## 安装
|
|
29
|
+
|
|
30
|
+
分两部分:**CLI**(`vcshort` 命令,通过 pip 安装)和**插件**(`skills/` 目录,通过各 agent 的插件机制安装)。skill 里直接调用 `vcshort`,不依赖固定路径。
|
|
31
|
+
|
|
32
|
+
### 1. 安装 Python
|
|
33
|
+
|
|
34
|
+
- **Windows**:https://www.python.org/downloads/windows/ 下载 `python-3.x.x-amd64.exe`,安装时勾选 **Add Python to PATH**
|
|
35
|
+
- **macOS**:`brew install python@3.12` 或从 python.org 下载安装包
|
|
36
|
+
- **Linux**:`sudo apt install python3 python3-pip`
|
|
37
|
+
|
|
38
|
+
### 2. 安装 vcshort CLI
|
|
39
|
+
|
|
40
|
+
发布到 PyPI 后:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pip install vcshort
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
本地源码安装:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
git clone https://github.com/itrxiAi/vc-short-plugin.git
|
|
50
|
+
cd vc-short-plugin
|
|
51
|
+
pip install -e .
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### 3. 安装 agent 插件
|
|
55
|
+
|
|
56
|
+
插件目录需要让 agent 能读到 `skills/` 下的 `SKILL.md`。
|
|
57
|
+
|
|
58
|
+
### WorkBuddy
|
|
59
|
+
|
|
60
|
+
WorkBuddy → 插件市场 → 添加市场 → 填入本仓库地址
|
|
61
|
+
`https://github.com/itrxiAi/vc-short-plugin` → 搜索 `vc-short` → 安装
|
|
62
|
+
(或 CLI 内:`/plugin marketplace add itrxiAi/vc-short-plugin` → `/plugin install vc-short@vc-short`)
|
|
63
|
+
|
|
64
|
+
### Devin / Claude Code / Cursor
|
|
65
|
+
|
|
66
|
+
指向仓库克隆目录(假设 clone 到 `~/vc-short-plugin`):
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
# Devin CLI
|
|
70
|
+
devin plugins install --local ~/vc-short-plugin
|
|
71
|
+
|
|
72
|
+
# Claude Code(macOS/Linux)
|
|
73
|
+
ln -s ~/vc-short-plugin ~/.claude/plugins/vc-short
|
|
74
|
+
|
|
75
|
+
# Claude Code(Windows,cmd)
|
|
76
|
+
mklink /J "%USERPROFILE%\.claude\plugins\vc-short" "%USERPROFILE%\vc-short-plugin"
|
|
77
|
+
|
|
78
|
+
# Cursor:将 ~/vc-short-plugin 拷贝或链接到 ~/.cursor/plugins/local/vc-short
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
安装完成后在 agent 对话中调用 `/vc-short:init` 等技能即可。
|
|
82
|
+
|
|
83
|
+
### 目录内容
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
vc-short-plugin/
|
|
87
|
+
├── skills/ # 12 个技能(SKILL.md)
|
|
88
|
+
├── agents/ # 子代理(script-reviewer)
|
|
89
|
+
├── src/vcshort/ # CLI 源码
|
|
90
|
+
├── pyproject.toml # 包元数据与依赖
|
|
91
|
+
├── requirements.txt # 依赖列表
|
|
92
|
+
├── .codebuddy-plugin/marketplace.json # WorkBuddy/CodeBuddy 插件市场清单
|
|
93
|
+
└── plugin.json # 插件清单(WorkBuddy / Claude Code / Devin 兼容)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### 本地构建与发布(可选)
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
git clone https://github.com/itrxiAi/vc-short-plugin.git
|
|
100
|
+
cd vc-short-plugin
|
|
101
|
+
./build.sh # 构建 wheel/sdist 并本地验证
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
发布到 PyPI:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
python -m twine upload dist/*
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
或打 tag 触发 GitHub Actions 自动发布(见 `.github/workflows/publish.yml`)。
|
|
111
|
+
|
|
112
|
+
## 使用
|
|
113
|
+
|
|
114
|
+
安装后技能通过 slash command 调用,建议按顺序执行:
|
|
115
|
+
|
|
116
|
+
### `/vc-short:init <项目名>` — 初始化项目
|
|
117
|
+
|
|
118
|
+
创建项目骨架目录。**产物:**
|
|
119
|
+
|
|
120
|
+
- `<项目名>/config.yaml` — 全局配置(`style`、`aspect_ratio`、`api.api_key`)
|
|
121
|
+
- `<项目名>/assets/` — 资产根目录(含 `characters/`、`scenes/`、`costumes/`、`props/` 子目录)
|
|
122
|
+
- `<项目名>/chapters/` — 章节根目录
|
|
123
|
+
|
|
124
|
+
执行后需准备 `chapters/ch01/novel.md`:可以直接粘贴小说原文,也可以用 `/vc-short:crawl-novel` 从阅读页爬取。
|
|
125
|
+
|
|
126
|
+
### `/vc-short:crawl-novel` — 爬取小说章节
|
|
127
|
+
|
|
128
|
+
用 Playwright 打开小说阅读页,带重叠滚动截图,再由 Agent 逐张读图转写。**产物:**
|
|
129
|
+
|
|
130
|
+
- `chapters/<章节号>/novel.md` — 该章小说原文
|
|
131
|
+
- `chapters/<章节号>/brief.md` — 该章简述(一句话梗概、出场人物、场景、主要事件、章末钩子)
|
|
132
|
+
- 中间产物(截图、拼接预览图、crawl.json)放在 `<项目>/.crawl/ch<NN>/`,转写完成后删除;章节目录下保留 `novel.md` 和 `brief.md`
|
|
133
|
+
|
|
134
|
+
阅读页正文用自定义字体混淆,DOM 文本不可用,因此必须读截图转写,不读 DOM、不调用 OCR。不绕过登录、验证码或付费墙,检测到付费墙即停止并报告。
|
|
135
|
+
|
|
136
|
+
依赖**系统 `python3` + playwright**(插件便携 Python 不含):`python3 -m pip install playwright && python3 -m playwright install chromium`。
|
|
137
|
+
|
|
138
|
+
### `/vc-short:extract` — 提取角色/场景/道具
|
|
139
|
+
|
|
140
|
+
从 `chapters/<章节号>/script.md` 提取角色、场景和道具,缺描述时按需 grep `novel.md` 搜名字补描述,与已有 `assets/` 目录匹配,用户确认后生成映射和 asset yaml。**产物:**
|
|
141
|
+
|
|
142
|
+
- `chapters/<章节号>/extract.tmp.json` — 临时文件(LLM 生成 → 脚本写回 `matched` → 用户确认 → `--confirm` 后删除)
|
|
143
|
+
- `chapters/<章节号>/character_map.yaml` — 角色映射(剧本角色名 → assets 目录名,最终版)
|
|
144
|
+
- `chapters/<章节号>/scene_map.yaml` — 场景映射(剧本场景描述 → assets 目录名)
|
|
145
|
+
- `chapters/<章节号>/prop_map.yaml` — 道具映射(剧本道具名 → assets 目录名)
|
|
146
|
+
- `assets/characters/<名称>/character.yaml` — 角色档案(不覆盖已有)
|
|
147
|
+
- `assets/scenes/<名称>/scene.yaml` — 场景档案(不覆盖已有)
|
|
148
|
+
- `assets/props/<名称>/prop.yaml` — 道具档案(不覆盖已有)
|
|
149
|
+
|
|
150
|
+
未匹配的新资产需用 `/vc-short:gen-image` 生成图片。
|
|
151
|
+
|
|
152
|
+
### `/vc-short:gen-image` — 生成图片资产
|
|
153
|
+
|
|
154
|
+
调用 doubao-seedream API 生成角色/服装/道具/场景图片,保存到 `assets/` 目录。**产物:**
|
|
155
|
+
|
|
156
|
+
- 角色:`assets/characters/<角色名>/<角色名>.png`(默认形态)或 `<角色名>-<形态>.png`(其他形态)
|
|
157
|
+
- 服装:`assets/costumes/<服装名>.png`
|
|
158
|
+
- 道具:`assets/props/<道具名>.png`
|
|
159
|
+
- 场景:`assets/scenes/<场景名>/<N>.png`(数字递增,支持多张)
|
|
160
|
+
|
|
161
|
+
### `/vc-short:fix-image` — 修改图片
|
|
162
|
+
|
|
163
|
+
基于原图 + 提示词调用 doubao-seededit API 编辑已有资产。**产物:**
|
|
164
|
+
|
|
165
|
+
- 覆盖原图片文件(如 `assets/characters/<角色名>/<角色名>.png`)
|
|
166
|
+
- `<原文件名>.png.bak` — 原图自动备份,不满意可恢复
|
|
167
|
+
|
|
168
|
+
### `/vc-short:gen-script` — 改编剧本
|
|
169
|
+
|
|
170
|
+
将 `chapters/<章节号>/novel.md` 改编为适合短视频的剧本。**产物:**
|
|
171
|
+
|
|
172
|
+
- `chapters/<章节号>/script.md` — 改编剧本(场景用 `## SC001 内 · 地点 · 时间` 标题分隔,按戏剧工作切场景,不按时长;支持 `[连续性]`/`[画面文字]`/`[转场]` 生产标签)
|
|
173
|
+
|
|
174
|
+
### `/vc-short:gen-shots` — 拆分分镜
|
|
175
|
+
|
|
176
|
+
按镜头职责拆分:每镜有唯一职责(本镜结束时观众知道了什么变化)、`起点 → 唯一动作 → 终点` 状态链、对白估时定档(5/10/15 秒)、冻结首帧提示词。LLM 直接写 Markdown 分镜文档,CLI 编译为 shot YAML。**产物:**
|
|
177
|
+
|
|
178
|
+
- `chapters/<章节号>/shots.md` — 分镜文档(drama 风格 `## SHOT-场序-镜序` 块,人可读的创作产物,保留不删)
|
|
179
|
+
- `chapters/<章节号>/shots/shot_001_01/shot.yaml` — 分镜参数(`source`、`purpose`、`script_segment`、`action`、`keyframe_prompt`、`end_state`、`characters`、`scene`、`camera`、`status: pending`)
|
|
180
|
+
- `chapters/<章节号>/shots/shot_001_02/shot.yaml` — 同主号 = 「同一地点 + 同一批角色」的连续分镜,子号递增
|
|
181
|
+
- `chapters/<章节号>/shots/shot_002_01/shot.yaml` — 地点或角色组变化后开新主号
|
|
182
|
+
|
|
183
|
+
### `/vc-short:gen-video` — 生成分镜视频
|
|
184
|
+
|
|
185
|
+
调用图生视频 API,将分镜 YAML 转为视频,并提取首尾帧保持分镜连贯。首帧优先用 `keyframe.png`(可用 `--with-keyframe` 按 `keyframe_prompt` 自动生成),无首帧图时用上一镜尾帧保持连贯。**产物:**
|
|
186
|
+
|
|
187
|
+
- `chapters/<章节号>/shots/shot_<N>/shot.mp4` — 分镜视频(5/10/15 秒,分辨率 720p)
|
|
188
|
+
- `chapters/<章节号>/shots/shot_<N>/keyframe.png` — 首帧图(可选,`--with-keyframe` 自动生成)
|
|
189
|
+
- `chapters/<章节号>/shots/shot_<N>/first_frame.png` — 视频首帧
|
|
190
|
+
- `chapters/<章节号>/shots/shot_<N>/last_frame.png` — 视频尾帧(下一分镜生成时作为参考图)
|
|
191
|
+
|
|
192
|
+
### `/vc-short:compose-chapter` — 合成章节视频
|
|
193
|
+
|
|
194
|
+
用 ffmpeg concat 按分镜号顺序拼接章节下所有 `shot.mp4`。**产物:**
|
|
195
|
+
|
|
196
|
+
- `chapters/<章节号>/chapter.mp4` — 章节完整视频(可用 `--output` 自定义文件名)
|
|
197
|
+
|
|
198
|
+
### `/vc-short:config-manager list` — 列出资产
|
|
199
|
+
|
|
200
|
+
扫描 `assets/` 目录,按角色/场景/服装/道具分类列出已有资产及图片数量,不产生文件,仅打印到终端。
|
|
201
|
+
|
|
202
|
+
## 目录结构
|
|
203
|
+
|
|
204
|
+
```
|
|
205
|
+
vc-short-plugin/
|
|
206
|
+
├── .devin-plugin/
|
|
207
|
+
│ └── plugin.json # Devin 插件清单
|
|
208
|
+
├── .claude-plugin/
|
|
209
|
+
│ └── plugin.json # Claude Code / WorkBuddy 插件清单
|
|
210
|
+
├── plugin.json # Cursor / Agent Plugins 清单
|
|
211
|
+
├── skills/ # 12 个技能
|
|
212
|
+
│ ├── init/SKILL.md
|
|
213
|
+
│ ├── crawl-novel/SKILL.md
|
|
214
|
+
│ ├── extract/SKILL.md
|
|
215
|
+
│ ├── gen-character/SKILL.md
|
|
216
|
+
│ ├── gen-image/SKILL.md
|
|
217
|
+
│ ├── fix-image/SKILL.md
|
|
218
|
+
│ ├── gen-voice/SKILL.md
|
|
219
|
+
│ ├── gen-script/SKILL.md
|
|
220
|
+
│ ├── gen-shots/SKILL.md
|
|
221
|
+
│ ├── gen-video/SKILL.md
|
|
222
|
+
│ ├── compose-chapter/SKILL.md
|
|
223
|
+
│ └── config-manager/SKILL.md
|
|
224
|
+
├── agents/ # 子代理定义
|
|
225
|
+
├── src/vcshort/ # Python CLI 源码
|
|
226
|
+
│ ├── cli.py # 主入口(subparsers 分发)
|
|
227
|
+
│ ├── init.py
|
|
228
|
+
│ ├── extract.py
|
|
229
|
+
│ ├── gen_image.py
|
|
230
|
+
│ ├── fix_image.py
|
|
231
|
+
│ ├── gen_voice.py
|
|
232
|
+
│ ├── gen_shots.py
|
|
233
|
+
│ ├── gen_video.py
|
|
234
|
+
│ ├── compose_chapter.py
|
|
235
|
+
│ └── config_manager.py
|
|
236
|
+
├── build.sh # 本地构建脚本:生成 wheel/sdist 并验证
|
|
237
|
+
├── pyproject.toml # 包元数据、依赖与 console_scripts 入口
|
|
238
|
+
├── requirements.txt # 依赖列表(与 pyproject.toml 同步)
|
|
239
|
+
└── README.md
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## 工作原理
|
|
243
|
+
|
|
244
|
+
- 用户机器需自行安装 **Python 3.10+**
|
|
245
|
+
- `pip install vcshort` 后,`vcshort` 命令加入 PATH,skill 里直接调用
|
|
246
|
+
- `src/vcshort/__main__.py` 是 `python -m vcshort` 入口;`vcshort` 命令由 `pyproject.toml` 的 `[project.scripts]` 注册
|
|
247
|
+
- 技能目录(`skills/`)由各 agent 的插件机制加载;skill 与 CLI 通过 `vcshort` 命令解耦
|
|
248
|
+
- CI 按 tag 触发:
|
|
249
|
+
- `.github/workflows/build.yml` 验证源码并打包 wheel
|
|
250
|
+
- `.github/workflows/publish.yml` 把 wheel 上传到 PyPI
|
|
251
|
+
|
|
252
|
+
## 依赖
|
|
253
|
+
|
|
254
|
+
- 系统 Python 3.10+ 及 `pyproject.toml` 中的包:`requests`、`PyYAML`、`ruamel.yaml`、`opencv-python-headless`、`imageio-ffmpeg`
|
|
255
|
+
- 火山引擎方舟 API Key(填入项目的 `config.yaml`)
|
vcshort-0.2.0/README.md
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
# vc-short-plugin
|
|
2
|
+
|
|
3
|
+
AI 短视频制作插件(剧本 → 资产 → 分镜 → 视频 → 合成),支持 WorkBuddy、Devin、Claude Code、Cursor。
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
分两部分:**CLI**(`vcshort` 命令,通过 pip 安装)和**插件**(`skills/` 目录,通过各 agent 的插件机制安装)。skill 里直接调用 `vcshort`,不依赖固定路径。
|
|
8
|
+
|
|
9
|
+
### 1. 安装 Python
|
|
10
|
+
|
|
11
|
+
- **Windows**:https://www.python.org/downloads/windows/ 下载 `python-3.x.x-amd64.exe`,安装时勾选 **Add Python to PATH**
|
|
12
|
+
- **macOS**:`brew install python@3.12` 或从 python.org 下载安装包
|
|
13
|
+
- **Linux**:`sudo apt install python3 python3-pip`
|
|
14
|
+
|
|
15
|
+
### 2. 安装 vcshort CLI
|
|
16
|
+
|
|
17
|
+
发布到 PyPI 后:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install vcshort
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
本地源码安装:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
git clone https://github.com/itrxiAi/vc-short-plugin.git
|
|
27
|
+
cd vc-short-plugin
|
|
28
|
+
pip install -e .
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### 3. 安装 agent 插件
|
|
32
|
+
|
|
33
|
+
插件目录需要让 agent 能读到 `skills/` 下的 `SKILL.md`。
|
|
34
|
+
|
|
35
|
+
### WorkBuddy
|
|
36
|
+
|
|
37
|
+
WorkBuddy → 插件市场 → 添加市场 → 填入本仓库地址
|
|
38
|
+
`https://github.com/itrxiAi/vc-short-plugin` → 搜索 `vc-short` → 安装
|
|
39
|
+
(或 CLI 内:`/plugin marketplace add itrxiAi/vc-short-plugin` → `/plugin install vc-short@vc-short`)
|
|
40
|
+
|
|
41
|
+
### Devin / Claude Code / Cursor
|
|
42
|
+
|
|
43
|
+
指向仓库克隆目录(假设 clone 到 `~/vc-short-plugin`):
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
# Devin CLI
|
|
47
|
+
devin plugins install --local ~/vc-short-plugin
|
|
48
|
+
|
|
49
|
+
# Claude Code(macOS/Linux)
|
|
50
|
+
ln -s ~/vc-short-plugin ~/.claude/plugins/vc-short
|
|
51
|
+
|
|
52
|
+
# Claude Code(Windows,cmd)
|
|
53
|
+
mklink /J "%USERPROFILE%\.claude\plugins\vc-short" "%USERPROFILE%\vc-short-plugin"
|
|
54
|
+
|
|
55
|
+
# Cursor:将 ~/vc-short-plugin 拷贝或链接到 ~/.cursor/plugins/local/vc-short
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
安装完成后在 agent 对话中调用 `/vc-short:init` 等技能即可。
|
|
59
|
+
|
|
60
|
+
### 目录内容
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
vc-short-plugin/
|
|
64
|
+
├── skills/ # 12 个技能(SKILL.md)
|
|
65
|
+
├── agents/ # 子代理(script-reviewer)
|
|
66
|
+
├── src/vcshort/ # CLI 源码
|
|
67
|
+
├── pyproject.toml # 包元数据与依赖
|
|
68
|
+
├── requirements.txt # 依赖列表
|
|
69
|
+
├── .codebuddy-plugin/marketplace.json # WorkBuddy/CodeBuddy 插件市场清单
|
|
70
|
+
└── plugin.json # 插件清单(WorkBuddy / Claude Code / Devin 兼容)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### 本地构建与发布(可选)
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
git clone https://github.com/itrxiAi/vc-short-plugin.git
|
|
77
|
+
cd vc-short-plugin
|
|
78
|
+
./build.sh # 构建 wheel/sdist 并本地验证
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
发布到 PyPI:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
python -m twine upload dist/*
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
或打 tag 触发 GitHub Actions 自动发布(见 `.github/workflows/publish.yml`)。
|
|
88
|
+
|
|
89
|
+
## 使用
|
|
90
|
+
|
|
91
|
+
安装后技能通过 slash command 调用,建议按顺序执行:
|
|
92
|
+
|
|
93
|
+
### `/vc-short:init <项目名>` — 初始化项目
|
|
94
|
+
|
|
95
|
+
创建项目骨架目录。**产物:**
|
|
96
|
+
|
|
97
|
+
- `<项目名>/config.yaml` — 全局配置(`style`、`aspect_ratio`、`api.api_key`)
|
|
98
|
+
- `<项目名>/assets/` — 资产根目录(含 `characters/`、`scenes/`、`costumes/`、`props/` 子目录)
|
|
99
|
+
- `<项目名>/chapters/` — 章节根目录
|
|
100
|
+
|
|
101
|
+
执行后需准备 `chapters/ch01/novel.md`:可以直接粘贴小说原文,也可以用 `/vc-short:crawl-novel` 从阅读页爬取。
|
|
102
|
+
|
|
103
|
+
### `/vc-short:crawl-novel` — 爬取小说章节
|
|
104
|
+
|
|
105
|
+
用 Playwright 打开小说阅读页,带重叠滚动截图,再由 Agent 逐张读图转写。**产物:**
|
|
106
|
+
|
|
107
|
+
- `chapters/<章节号>/novel.md` — 该章小说原文
|
|
108
|
+
- `chapters/<章节号>/brief.md` — 该章简述(一句话梗概、出场人物、场景、主要事件、章末钩子)
|
|
109
|
+
- 中间产物(截图、拼接预览图、crawl.json)放在 `<项目>/.crawl/ch<NN>/`,转写完成后删除;章节目录下保留 `novel.md` 和 `brief.md`
|
|
110
|
+
|
|
111
|
+
阅读页正文用自定义字体混淆,DOM 文本不可用,因此必须读截图转写,不读 DOM、不调用 OCR。不绕过登录、验证码或付费墙,检测到付费墙即停止并报告。
|
|
112
|
+
|
|
113
|
+
依赖**系统 `python3` + playwright**(插件便携 Python 不含):`python3 -m pip install playwright && python3 -m playwright install chromium`。
|
|
114
|
+
|
|
115
|
+
### `/vc-short:extract` — 提取角色/场景/道具
|
|
116
|
+
|
|
117
|
+
从 `chapters/<章节号>/script.md` 提取角色、场景和道具,缺描述时按需 grep `novel.md` 搜名字补描述,与已有 `assets/` 目录匹配,用户确认后生成映射和 asset yaml。**产物:**
|
|
118
|
+
|
|
119
|
+
- `chapters/<章节号>/extract.tmp.json` — 临时文件(LLM 生成 → 脚本写回 `matched` → 用户确认 → `--confirm` 后删除)
|
|
120
|
+
- `chapters/<章节号>/character_map.yaml` — 角色映射(剧本角色名 → assets 目录名,最终版)
|
|
121
|
+
- `chapters/<章节号>/scene_map.yaml` — 场景映射(剧本场景描述 → assets 目录名)
|
|
122
|
+
- `chapters/<章节号>/prop_map.yaml` — 道具映射(剧本道具名 → assets 目录名)
|
|
123
|
+
- `assets/characters/<名称>/character.yaml` — 角色档案(不覆盖已有)
|
|
124
|
+
- `assets/scenes/<名称>/scene.yaml` — 场景档案(不覆盖已有)
|
|
125
|
+
- `assets/props/<名称>/prop.yaml` — 道具档案(不覆盖已有)
|
|
126
|
+
|
|
127
|
+
未匹配的新资产需用 `/vc-short:gen-image` 生成图片。
|
|
128
|
+
|
|
129
|
+
### `/vc-short:gen-image` — 生成图片资产
|
|
130
|
+
|
|
131
|
+
调用 doubao-seedream API 生成角色/服装/道具/场景图片,保存到 `assets/` 目录。**产物:**
|
|
132
|
+
|
|
133
|
+
- 角色:`assets/characters/<角色名>/<角色名>.png`(默认形态)或 `<角色名>-<形态>.png`(其他形态)
|
|
134
|
+
- 服装:`assets/costumes/<服装名>.png`
|
|
135
|
+
- 道具:`assets/props/<道具名>.png`
|
|
136
|
+
- 场景:`assets/scenes/<场景名>/<N>.png`(数字递增,支持多张)
|
|
137
|
+
|
|
138
|
+
### `/vc-short:fix-image` — 修改图片
|
|
139
|
+
|
|
140
|
+
基于原图 + 提示词调用 doubao-seededit API 编辑已有资产。**产物:**
|
|
141
|
+
|
|
142
|
+
- 覆盖原图片文件(如 `assets/characters/<角色名>/<角色名>.png`)
|
|
143
|
+
- `<原文件名>.png.bak` — 原图自动备份,不满意可恢复
|
|
144
|
+
|
|
145
|
+
### `/vc-short:gen-script` — 改编剧本
|
|
146
|
+
|
|
147
|
+
将 `chapters/<章节号>/novel.md` 改编为适合短视频的剧本。**产物:**
|
|
148
|
+
|
|
149
|
+
- `chapters/<章节号>/script.md` — 改编剧本(场景用 `## SC001 内 · 地点 · 时间` 标题分隔,按戏剧工作切场景,不按时长;支持 `[连续性]`/`[画面文字]`/`[转场]` 生产标签)
|
|
150
|
+
|
|
151
|
+
### `/vc-short:gen-shots` — 拆分分镜
|
|
152
|
+
|
|
153
|
+
按镜头职责拆分:每镜有唯一职责(本镜结束时观众知道了什么变化)、`起点 → 唯一动作 → 终点` 状态链、对白估时定档(5/10/15 秒)、冻结首帧提示词。LLM 直接写 Markdown 分镜文档,CLI 编译为 shot YAML。**产物:**
|
|
154
|
+
|
|
155
|
+
- `chapters/<章节号>/shots.md` — 分镜文档(drama 风格 `## SHOT-场序-镜序` 块,人可读的创作产物,保留不删)
|
|
156
|
+
- `chapters/<章节号>/shots/shot_001_01/shot.yaml` — 分镜参数(`source`、`purpose`、`script_segment`、`action`、`keyframe_prompt`、`end_state`、`characters`、`scene`、`camera`、`status: pending`)
|
|
157
|
+
- `chapters/<章节号>/shots/shot_001_02/shot.yaml` — 同主号 = 「同一地点 + 同一批角色」的连续分镜,子号递增
|
|
158
|
+
- `chapters/<章节号>/shots/shot_002_01/shot.yaml` — 地点或角色组变化后开新主号
|
|
159
|
+
|
|
160
|
+
### `/vc-short:gen-video` — 生成分镜视频
|
|
161
|
+
|
|
162
|
+
调用图生视频 API,将分镜 YAML 转为视频,并提取首尾帧保持分镜连贯。首帧优先用 `keyframe.png`(可用 `--with-keyframe` 按 `keyframe_prompt` 自动生成),无首帧图时用上一镜尾帧保持连贯。**产物:**
|
|
163
|
+
|
|
164
|
+
- `chapters/<章节号>/shots/shot_<N>/shot.mp4` — 分镜视频(5/10/15 秒,分辨率 720p)
|
|
165
|
+
- `chapters/<章节号>/shots/shot_<N>/keyframe.png` — 首帧图(可选,`--with-keyframe` 自动生成)
|
|
166
|
+
- `chapters/<章节号>/shots/shot_<N>/first_frame.png` — 视频首帧
|
|
167
|
+
- `chapters/<章节号>/shots/shot_<N>/last_frame.png` — 视频尾帧(下一分镜生成时作为参考图)
|
|
168
|
+
|
|
169
|
+
### `/vc-short:compose-chapter` — 合成章节视频
|
|
170
|
+
|
|
171
|
+
用 ffmpeg concat 按分镜号顺序拼接章节下所有 `shot.mp4`。**产物:**
|
|
172
|
+
|
|
173
|
+
- `chapters/<章节号>/chapter.mp4` — 章节完整视频(可用 `--output` 自定义文件名)
|
|
174
|
+
|
|
175
|
+
### `/vc-short:config-manager list` — 列出资产
|
|
176
|
+
|
|
177
|
+
扫描 `assets/` 目录,按角色/场景/服装/道具分类列出已有资产及图片数量,不产生文件,仅打印到终端。
|
|
178
|
+
|
|
179
|
+
## 目录结构
|
|
180
|
+
|
|
181
|
+
```
|
|
182
|
+
vc-short-plugin/
|
|
183
|
+
├── .devin-plugin/
|
|
184
|
+
│ └── plugin.json # Devin 插件清单
|
|
185
|
+
├── .claude-plugin/
|
|
186
|
+
│ └── plugin.json # Claude Code / WorkBuddy 插件清单
|
|
187
|
+
├── plugin.json # Cursor / Agent Plugins 清单
|
|
188
|
+
├── skills/ # 12 个技能
|
|
189
|
+
│ ├── init/SKILL.md
|
|
190
|
+
│ ├── crawl-novel/SKILL.md
|
|
191
|
+
│ ├── extract/SKILL.md
|
|
192
|
+
│ ├── gen-character/SKILL.md
|
|
193
|
+
│ ├── gen-image/SKILL.md
|
|
194
|
+
│ ├── fix-image/SKILL.md
|
|
195
|
+
│ ├── gen-voice/SKILL.md
|
|
196
|
+
│ ├── gen-script/SKILL.md
|
|
197
|
+
│ ├── gen-shots/SKILL.md
|
|
198
|
+
│ ├── gen-video/SKILL.md
|
|
199
|
+
│ ├── compose-chapter/SKILL.md
|
|
200
|
+
│ └── config-manager/SKILL.md
|
|
201
|
+
├── agents/ # 子代理定义
|
|
202
|
+
├── src/vcshort/ # Python CLI 源码
|
|
203
|
+
│ ├── cli.py # 主入口(subparsers 分发)
|
|
204
|
+
│ ├── init.py
|
|
205
|
+
│ ├── extract.py
|
|
206
|
+
│ ├── gen_image.py
|
|
207
|
+
│ ├── fix_image.py
|
|
208
|
+
│ ├── gen_voice.py
|
|
209
|
+
│ ├── gen_shots.py
|
|
210
|
+
│ ├── gen_video.py
|
|
211
|
+
│ ├── compose_chapter.py
|
|
212
|
+
│ └── config_manager.py
|
|
213
|
+
├── build.sh # 本地构建脚本:生成 wheel/sdist 并验证
|
|
214
|
+
├── pyproject.toml # 包元数据、依赖与 console_scripts 入口
|
|
215
|
+
├── requirements.txt # 依赖列表(与 pyproject.toml 同步)
|
|
216
|
+
└── README.md
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## 工作原理
|
|
220
|
+
|
|
221
|
+
- 用户机器需自行安装 **Python 3.10+**
|
|
222
|
+
- `pip install vcshort` 后,`vcshort` 命令加入 PATH,skill 里直接调用
|
|
223
|
+
- `src/vcshort/__main__.py` 是 `python -m vcshort` 入口;`vcshort` 命令由 `pyproject.toml` 的 `[project.scripts]` 注册
|
|
224
|
+
- 技能目录(`skills/`)由各 agent 的插件机制加载;skill 与 CLI 通过 `vcshort` 命令解耦
|
|
225
|
+
- CI 按 tag 触发:
|
|
226
|
+
- `.github/workflows/build.yml` 验证源码并打包 wheel
|
|
227
|
+
- `.github/workflows/publish.yml` 把 wheel 上传到 PyPI
|
|
228
|
+
|
|
229
|
+
## 依赖
|
|
230
|
+
|
|
231
|
+
- 系统 Python 3.10+ 及 `pyproject.toml` 中的包:`requests`、`PyYAML`、`ruamel.yaml`、`opencv-python-headless`、`imageio-ffmpeg`
|
|
232
|
+
- 火山引擎方舟 API Key(填入项目的 `config.yaml`)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "vcshort"
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "AI 短视频制作 CLI:剧本 → 资产 → 分镜 → 视频 → 合成"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "skywalker"},
|
|
14
|
+
]
|
|
15
|
+
keywords = ["short-video", "ai", "storyboard", "video-generation"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 3 - Alpha",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.10",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
]
|
|
24
|
+
dependencies = [
|
|
25
|
+
"requests>=2.28",
|
|
26
|
+
"PyYAML>=6.0",
|
|
27
|
+
"ruamel.yaml>=0.17",
|
|
28
|
+
"opencv-python-headless>=4.8,<5",
|
|
29
|
+
"imageio-ffmpeg>=0.4.9",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[project.scripts]
|
|
33
|
+
vcshort = "vcshort.cli:main"
|
|
34
|
+
|
|
35
|
+
[project.urls]
|
|
36
|
+
Homepage = "https://github.com/itrxiAi/vc-short-plugin"
|
|
37
|
+
Repository = "https://github.com/itrxiAi/vc-short-plugin"
|
|
38
|
+
|
|
39
|
+
[tool.setuptools.packages.find]
|
|
40
|
+
where = ["src"]
|
vcshort-0.2.0/setup.cfg
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""回忆/闪回视频后处理:给画面边缘叠加一圈白色雾气。"""
|
|
3
|
+
|
|
4
|
+
import subprocess
|
|
5
|
+
import sys
|
|
6
|
+
import tempfile
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
try:
|
|
10
|
+
import cv2
|
|
11
|
+
import numpy as np
|
|
12
|
+
except ImportError:
|
|
13
|
+
cv2 = None
|
|
14
|
+
np = None
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _has_ffmpeg() -> bool:
|
|
18
|
+
try:
|
|
19
|
+
subprocess.run(["ffmpeg", "-version"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True)
|
|
20
|
+
return True
|
|
21
|
+
except Exception:
|
|
22
|
+
return False
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def apply_flashback_fog(
|
|
26
|
+
video_path: str | Path,
|
|
27
|
+
output_path: str | Path | None = None,
|
|
28
|
+
inner: float = 0.55,
|
|
29
|
+
outer: float = 1.0,
|
|
30
|
+
max_alpha: float = 0.75,
|
|
31
|
+
gamma: float = 2.0,
|
|
32
|
+
) -> Path:
|
|
33
|
+
"""给视频叠加边缘白雾,返回输出视频路径。
|
|
34
|
+
|
|
35
|
+
参数(已按 004_01 效果固定):
|
|
36
|
+
- inner: 中心清晰区半径比例(0~1),默认 0.55
|
|
37
|
+
- outer: 雾完全覆盖区半径比例,默认 1.0
|
|
38
|
+
- max_alpha: 边缘最大雾浓度,默认 0.75
|
|
39
|
+
- gamma: 雾浓度曲线幂次,默认 2.0(越靠边越浓)
|
|
40
|
+
"""
|
|
41
|
+
if cv2 is None or np is None:
|
|
42
|
+
print("错误:回忆雾效需要 opencv-python 和 numpy,请安装:pip install opencv-python numpy", file=sys.stderr)
|
|
43
|
+
sys.exit(1)
|
|
44
|
+
if not _has_ffmpeg():
|
|
45
|
+
print("错误:回忆雾效需要 ffmpeg", file=sys.stderr)
|
|
46
|
+
sys.exit(1)
|
|
47
|
+
|
|
48
|
+
video_path = Path(video_path)
|
|
49
|
+
if output_path is None:
|
|
50
|
+
output_path = video_path
|
|
51
|
+
else:
|
|
52
|
+
output_path = Path(output_path)
|
|
53
|
+
|
|
54
|
+
# 临时文件
|
|
55
|
+
tmp_dir = Path(tempfile.gettempdir())
|
|
56
|
+
video_no_audio = tmp_dir / f"{video_path.stem}_fog_no_audio.mp4"
|
|
57
|
+
audio_path = tmp_dir / f"{video_path.stem}_audio.aac"
|
|
58
|
+
|
|
59
|
+
try:
|
|
60
|
+
# 提取音频
|
|
61
|
+
subprocess.run(
|
|
62
|
+
["ffmpeg", "-y", "-i", str(video_path), "-vn", "-c:a", "copy", str(audio_path)],
|
|
63
|
+
check=True, stdout=subprocess.DEVNULL, stderr=subprocess.PIPE,
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
# 读取视频
|
|
67
|
+
cap = cv2.VideoCapture(str(video_path))
|
|
68
|
+
if not cap.isOpened():
|
|
69
|
+
print(f"错误:无法打开视频 {video_path}", file=sys.stderr)
|
|
70
|
+
sys.exit(1)
|
|
71
|
+
|
|
72
|
+
fps = cap.get(cv2.CAP_PROP_FPS)
|
|
73
|
+
w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
|
|
74
|
+
h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
|
|
75
|
+
fourcc = cv2.VideoWriter_fourcc(*"mp4v")
|
|
76
|
+
out = cv2.VideoWriter(str(video_no_audio), fourcc, fps, (w, h))
|
|
77
|
+
|
|
78
|
+
# 径向雾遮罩:中心清晰,边缘白色
|
|
79
|
+
x = np.linspace(-1, 1, w)
|
|
80
|
+
y = np.linspace(-1, 1, h)
|
|
81
|
+
xx, yy = np.meshgrid(x, y)
|
|
82
|
+
dist = np.sqrt(xx**2 + yy**2)
|
|
83
|
+
alpha = np.clip((dist - inner) / (outer - inner), 0, 1)
|
|
84
|
+
alpha = (alpha ** gamma) * max_alpha
|
|
85
|
+
alpha = alpha[:, :, np.newaxis].astype(np.float32)
|
|
86
|
+
|
|
87
|
+
frame_count = 0
|
|
88
|
+
while True:
|
|
89
|
+
ret, frame = cap.read()
|
|
90
|
+
if not ret:
|
|
91
|
+
break
|
|
92
|
+
frame_f = frame.astype(np.float32)
|
|
93
|
+
fogged = frame_f * (1 - alpha) + 255.0 * alpha
|
|
94
|
+
fogged = np.clip(fogged, 0, 255).astype(np.uint8)
|
|
95
|
+
out.write(fogged)
|
|
96
|
+
frame_count += 1
|
|
97
|
+
|
|
98
|
+
cap.release()
|
|
99
|
+
out.release()
|
|
100
|
+
print(f"回忆雾效处理 {frame_count} 帧")
|
|
101
|
+
|
|
102
|
+
# 合并音频
|
|
103
|
+
subprocess.run(
|
|
104
|
+
[
|
|
105
|
+
"ffmpeg", "-y",
|
|
106
|
+
"-i", str(video_no_audio),
|
|
107
|
+
"-i", str(audio_path),
|
|
108
|
+
"-c:v", "libx264", "-c:a", "copy",
|
|
109
|
+
"-pix_fmt", "yuv420p",
|
|
110
|
+
"-shortest",
|
|
111
|
+
str(output_path),
|
|
112
|
+
],
|
|
113
|
+
check=True, stdout=subprocess.DEVNULL, stderr=subprocess.PIPE,
|
|
114
|
+
)
|
|
115
|
+
print(f"已保存回忆雾效视频: {output_path}")
|
|
116
|
+
finally:
|
|
117
|
+
# 清理临时文件
|
|
118
|
+
if video_no_audio.exists():
|
|
119
|
+
video_no_audio.unlink()
|
|
120
|
+
if audio_path.exists():
|
|
121
|
+
audio_path.unlink()
|
|
122
|
+
|
|
123
|
+
return output_path
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def main(argv=None) -> int:
|
|
127
|
+
parser = argparse.ArgumentParser(prog="apply_flashback_fog", description="给视频叠加回忆白雾")
|
|
128
|
+
parser.add_argument("video", help="输入视频路径")
|
|
129
|
+
parser.add_argument("-o", "--output", default=None, help="输出路径(默认覆盖输入)")
|
|
130
|
+
args = parser.parse_args(argv)
|
|
131
|
+
apply_flashback_fog(args.video, args.output)
|
|
132
|
+
return 0
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
if __name__ == "__main__":
|
|
136
|
+
sys.exit(main())
|