Initial commit to git.yoin
24
video-creator/README.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# Video Creator
|
||||
|
||||
视频生成技能,图片+音频合成视频。
|
||||
|
||||
## 依赖
|
||||
|
||||
```bash
|
||||
brew install ffmpeg
|
||||
pip install edge-tts pyyaml
|
||||
```
|
||||
|
||||
## 功能
|
||||
|
||||
- 图片序列 + 音频 → 视频
|
||||
- 淡入淡出转场
|
||||
- 自动拼接片尾
|
||||
- 添加 BGM
|
||||
- 烧录字幕
|
||||
|
||||
## 资源
|
||||
|
||||
- 片尾视频:支持 9 种比例(1x1/9x16/16x9 等)
|
||||
- BGM:科技感/史诗感
|
||||
- Logo
|
||||
316
video-creator/SKILL.md
Normal file
@@ -0,0 +1,316 @@
|
||||
---
|
||||
name: video-creator
|
||||
description: 视频创作技能。图片+音频合成视频,支持淡入淡出转场、自动拼接片尾、添加BGM。当用户提到「生成视频」「图文转视频」「做视频号」时触发此技能。
|
||||
---
|
||||
|
||||
# Video Creator
|
||||
|
||||
图片+音频合成视频工具。
|
||||
|
||||
## 核心流程(铁律)
|
||||
|
||||
### 故事类视频生成流程(套娃流程)
|
||||
|
||||
当用户提供故事/剧情/剧本时,**必须严格按以下套娃流程执行**:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 第一层:故事 → 拆分场景 → 并发生成场景主图(文生图) │
|
||||
│ │
|
||||
│ 大闹天宫 → 场景1:弼马温受辱 │
|
||||
│ 场景2:筋斗云回花果山 │
|
||||
│ 场景3:玉帝派兵 │
|
||||
│ ... │
|
||||
│ → 并发调用 text_to_image.py 生成每个场景主图 │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 第二层:每个场景主图 → 图生图拆出细镜头(保持角色一致) │
|
||||
│ │
|
||||
│ 场景1主图 → 细镜头1:悟空看官印疑惑 │
|
||||
│ 细镜头2:悟空踢翻马槽 │
|
||||
│ 场景2主图 → 细镜头1:踏筋斗云腾空 │
|
||||
│ 细镜头2:花果山自封大圣 │
|
||||
│ → 并发调用 image_to_image.py,以主图为参考 │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 第三层:生成配音 + 字幕 + 合成视频 │
|
||||
│ │
|
||||
│ 1. tts_generator.py 生成配音 + 时间戳 │
|
||||
│ 2. 【铁律】根据时间戳精确计算每张图的duration(见下方规范) │
|
||||
│ 3. 生成 SRT 字幕 │
|
||||
│ 4. 生成 video_config.yaml 前必须校验总时长 │
|
||||
│ 5. video_maker.py 合成: │
|
||||
│ → 图片合成(带转场) │
|
||||
│ → 合并音频 │
|
||||
│ → 烧录字幕(ASS格式,底部居中固定) │
|
||||
│ → 自动拼接片尾(二维码+"点关注不迷路") │
|
||||
│ → 添加BGM │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
|
||||
**铁律:所有视频必须自动拼接片尾!**
|
||||
```
|
||||
|
||||
### 目录结构规范
|
||||
|
||||
```
|
||||
assets/generated/{project_name}/
|
||||
├── scene1/
|
||||
│ ├── main.png # 场景1主图(文生图)
|
||||
│ ├── shot_01.png # 细镜头1(图生图)
|
||||
│ └── shot_02.png # 细镜头2(图生图)
|
||||
├── scene2/
|
||||
│ ├── main.png
|
||||
│ ├── shot_01.png
|
||||
│ └── shot_02.png
|
||||
├── ...
|
||||
├── narration.mp3 # 配音
|
||||
├── narration.json # 时间戳
|
||||
├── subtitles.srt # 字幕
|
||||
├── video_config.yaml # 视频配置
|
||||
└── {project_name}.mp4 # 最终视频
|
||||
```
|
||||
|
||||
### 执行命令示例
|
||||
|
||||
```bash
|
||||
# 第一层:并发生成场景主图
|
||||
python .opencode/skills/image-service/scripts/text_to_image.py "风格描述,场景1内容" -r 9:16 -o scene1/main.png &
|
||||
python .opencode/skills/image-service/scripts/text_to_image.py "风格描述,场景2内容" -r 9:16 -o scene2/main.png &
|
||||
wait
|
||||
|
||||
# 第二层:并发图生图生成细镜头
|
||||
python .opencode/skills/image-service/scripts/image_to_image.py scene1/main.png "保持角色风格,细镜头描述" -r 9:16 -o scene1/shot_01.png &
|
||||
python .opencode/skills/image-service/scripts/image_to_image.py scene1/main.png "保持角色风格,细镜头描述" -r 9:16 -o scene1/shot_02.png &
|
||||
wait
|
||||
|
||||
# 第三层:生成配音+合成视频
|
||||
python .opencode/skills/video-creator/scripts/tts_generator.py --text "完整旁白" --output narration.mp3 --timestamps
|
||||
python .opencode/skills/video-creator/scripts/video_maker.py video_config.yaml --srt subtitles.srt --bgm epic
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 视频配置文件格式
|
||||
|
||||
```yaml
|
||||
# video_config.yaml
|
||||
ratio: "9:16" # 必须加引号!避免YAML解析错误
|
||||
bgm_volume: 0.12
|
||||
outro: true
|
||||
|
||||
scenes:
|
||||
- audio: narration.mp3
|
||||
images:
|
||||
# 按场景顺序排列所有细镜头
|
||||
- file: scene1/shot_01.png
|
||||
duration: 4.34
|
||||
- file: scene1/shot_02.png
|
||||
duration: 4.88
|
||||
- file: scene2/shot_01.png
|
||||
duration: 2.15
|
||||
# ...
|
||||
```
|
||||
|
||||
**注意**:`ratio` 必须用引号包裹,如 `"9:16"`,否则 YAML 会解析成时间格式。
|
||||
|
||||
---
|
||||
|
||||
## 时长分配规范(铁律!)
|
||||
|
||||
**生成 video_config.yaml 前,必须严格按以下流程计算 duration:**
|
||||
|
||||
### 步骤1:读取时间戳文件
|
||||
|
||||
```python
|
||||
import json
|
||||
with open("narration.json", "r") as f:
|
||||
timestamps = json.load(f)
|
||||
audio_duration = timestamps[-1]["end"]
|
||||
print(f"音频总时长: {audio_duration:.1f}s")
|
||||
```
|
||||
|
||||
### 步骤2:按内容语义划分场景
|
||||
|
||||
根据解说词内容,确定每张图对应的时间段:
|
||||
|
||||
```python
|
||||
# 示例:根据解说词内容划分
|
||||
# 找到每个主题切换点的时间戳
|
||||
scenes = [
|
||||
("cover.png", 0, 12.5), # 开场到第一个主题切换
|
||||
("scene01.png", 12.5, 26), # 第二段内容
|
||||
# ...根据 narration.json 中的句子边界精确划分
|
||||
]
|
||||
```
|
||||
|
||||
### 步骤3:计算每张图的 duration
|
||||
|
||||
```python
|
||||
for file, start, end in scenes:
|
||||
duration = end - start
|
||||
print(f"{file}: {duration:.1f}s")
|
||||
```
|
||||
|
||||
### 步骤4:校验总时长
|
||||
|
||||
```python
|
||||
total_duration = sum(duration for _, _, duration in scenes)
|
||||
assert abs(total_duration - audio_duration) < 1.0, \
|
||||
f"时长不匹配!图片总时长{total_duration}s vs 音频{audio_duration}s"
|
||||
```
|
||||
|
||||
### 铁律
|
||||
|
||||
1. **必须先读取 narration.json 时间戳**,不能凭感觉估算
|
||||
2. **按句子语义边界划分**,不能平均分配
|
||||
3. **生成配置前必须校验**,确保图片总时长 ≈ 音频总时长(误差<1秒)
|
||||
4. **禁止让脚本自动拉伸**,音画不同步的视频不合格
|
||||
|
||||
### 时长分配表模板
|
||||
|
||||
生成配置前,先输出分配表让用户确认:
|
||||
|
||||
```markdown
|
||||
| 场景图 | 对应内容 | 开始 | 结束 | 时长 |
|
||||
|--------|----------|------|------|------|
|
||||
| cover.png | 开场引入 | 0s | 12.5s | 12.5s |
|
||||
| scene01.png | AI Agent时代 | 12.5s | 26s | 13.5s |
|
||||
| ... | ... | ... | ... | ... |
|
||||
| **合计** | | | | **{total}s** |
|
||||
|
||||
音频总时长:{audio_duration}s
|
||||
差值:{diff}s ✅/❌
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 字幕规范
|
||||
|
||||
字幕使用 ASS 格式,**强制底部居中固定位置**:
|
||||
|
||||
- 位置:底部居中(Alignment=2)
|
||||
- 字体:PingFang SC
|
||||
- 大小:屏幕高度 / 40
|
||||
- 描边:2px 黑色描边 + 1px 阴影
|
||||
- 底边距:屏幕高度 / 20
|
||||
|
||||
**禁止**:字幕乱跑、大小不一、位置不固定
|
||||
|
||||
---
|
||||
|
||||
## 脚本参数说明
|
||||
|
||||
### video_maker.py
|
||||
|
||||
```bash
|
||||
python video_maker.py config.yaml [options]
|
||||
```
|
||||
|
||||
| 参数 | 说明 | 默认值 |
|
||||
|------|------|--------|
|
||||
| `--no-outro` | 不添加片尾 | 添加 |
|
||||
| `--no-bgm` | 不添加BGM | 添加 |
|
||||
| `--fade` | 转场时长(秒) | 0.5 |
|
||||
| `--bgm-volume` | BGM音量 | 0.08 |
|
||||
| `--bgm` | 自定义BGM(可选: epic) | 默认科技风 |
|
||||
| `--ratio` | 视频比例 | 16:9(会被配置文件覆盖) |
|
||||
| `--srt` | 字幕文件路径 | 无 |
|
||||
|
||||
### tts_generator.py
|
||||
|
||||
```bash
|
||||
python tts_generator.py --text "文本" --output audio.mp3 [options]
|
||||
```
|
||||
|
||||
| 参数 | 说明 | 默认值 |
|
||||
|------|------|--------|
|
||||
| `--voice` | 音色 | zh-CN-YunxiNeural |
|
||||
| `--rate` | 语速 | +0% |
|
||||
| `--timestamps` | 输出时间戳JSON | 否 |
|
||||
|
||||
---
|
||||
|
||||
## 支持的视频比例
|
||||
|
||||
与 `image-service` 生图服务保持一致,支持 **10 种比例**:
|
||||
|
||||
| 比例 | 分辨率 | 适用场景 |
|
||||
|------|--------|----------|
|
||||
| 1:1 | 1024×1024 | 正方形,朋友圈 |
|
||||
| 2:3 | 832×1248 | 竖版海报 |
|
||||
| 3:2 | 1248×832 | 横版海报 |
|
||||
| 3:4 | 1080×1440 | 小红书、朋友圈 |
|
||||
| 4:3 | 1440×1080 | 传统显示器 |
|
||||
| 4:5 | 864×1080 | Instagram |
|
||||
| 5:4 | 1080×864 | 横版照片 |
|
||||
| 9:16 | 1080×1920 | 抖音、视频号、竖屏 |
|
||||
| 16:9 | 1920×1080 | B站、YouTube、横屏 |
|
||||
| 21:9 | 1536×672 | 超宽屏电影 |
|
||||
|
||||
---
|
||||
|
||||
## 片尾规范
|
||||
|
||||
**铁律:所有视频必须自动拼接对应尺寸的片尾!**
|
||||
|
||||
片尾匹配顺序:
|
||||
1. 精确匹配:`outro_{ratio}.mp4`
|
||||
2. 方向匹配:竖版→`outro_9x16.mp4`,横版→`outro_16x9.mp4`
|
||||
3. 兜底:`outro.mp4`
|
||||
|
||||
---
|
||||
|
||||
## BGM 资源
|
||||
|
||||
| 文件 | 风格 | 适用场景 |
|
||||
|------|------|----------|
|
||||
| `bgm_technology.mp3` | 科技感 | 技术教程、产品介绍 |
|
||||
| `bgm_epic.mp3` | 热血史诗 | 故事、战斗、励志 |
|
||||
|
||||
使用:`--bgm epic` 或 `--bgm /path/to/bgm.mp3`
|
||||
|
||||
---
|
||||
|
||||
## 常用音色
|
||||
|
||||
| 音色 ID | 风格 |
|
||||
|---------|------|
|
||||
| zh-CN-YunyangNeural | 男声,新闻播报 |
|
||||
| zh-CN-YunxiNeural | 男声,阳光活泼 |
|
||||
| zh-CN-XiaoxiaoNeural | 女声,温暖自然 |
|
||||
| zh-CN-XiaoyiNeural | 女声,活泼可爱 |
|
||||
|
||||
---
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
video-creator/
|
||||
├── SKILL.md
|
||||
├── scripts/
|
||||
│ ├── video_maker.py # 主脚本:图片+音频→视频
|
||||
│ ├── tts_generator.py # TTS 语音生成
|
||||
│ └── scene_splitter.py # 场景拆分器(可选)
|
||||
├── assets/
|
||||
│ ├── outro.mp4 # 通用片尾(16:9)
|
||||
│ ├── outro_9x16.mp4 # 竖版片尾
|
||||
│ ├── outro_3x4.mp4 # 3:4片尾
|
||||
│ ├── bgm_technology.mp3 # 默认BGM
|
||||
│ └── bgm_epic.mp3 # 热血BGM
|
||||
└── references/
|
||||
└── edge_tts_voices.md
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 依赖
|
||||
|
||||
```bash
|
||||
# 系统依赖
|
||||
brew install ffmpeg # Mac
|
||||
|
||||
# Python 依赖
|
||||
pip install edge-tts pyyaml
|
||||
```
|
||||
0
video-creator/assets/.gitkeep
Normal file
BIN
video-creator/assets/bgm_epic.mp3
Normal file
BIN
video-creator/assets/bgm_technology.mp3
Normal file
73
video-creator/assets/default_config.yaml
Normal file
@@ -0,0 +1,73 @@
|
||||
# 默认配置 - 所有项目继承此配置
|
||||
# 项目配置只需写差异部分即可
|
||||
|
||||
# 视频基础配置
|
||||
resolution: [1080, 1920] # 默认竖版
|
||||
fps: 30
|
||||
|
||||
# 语音默认配置(项目可覆盖)
|
||||
voice:
|
||||
name: "zh-CN-YunxiNeural" # 默认男声
|
||||
rate: "+0%"
|
||||
pitch: "+0Hz"
|
||||
|
||||
# 可选音色参考:
|
||||
# 男声: zh-CN-YunxiNeural, zh-CN-YunyangNeural, zh-CN-YunjianNeural
|
||||
# 女声: zh-CN-XiaoxiaoNeural, zh-CN-XiaoyiNeural, zh-CN-XiaohanNeural
|
||||
# 方言: zh-CN-liaoning-XiaobeiNeural, zh-CN-shaanxi-XiaoniNeural
|
||||
|
||||
# 样式默认配置
|
||||
style:
|
||||
font: "PingFang SC"
|
||||
font_size: 42
|
||||
font_color: "#FFFFFF"
|
||||
stroke_color: "#000000"
|
||||
stroke_width: 2
|
||||
subtitle_position: "bottom" # bottom/top/center
|
||||
subtitle_bg: true
|
||||
subtitle_bg_color: "#000000"
|
||||
subtitle_bg_opacity: 0.7
|
||||
highlight_color: "#FFD700"
|
||||
|
||||
# 动画默认配置
|
||||
animation:
|
||||
ken_burns: true
|
||||
default_animation: "zoom_in"
|
||||
animation_intensity: 0.15 # 动画幅度 0.1~0.3
|
||||
|
||||
# 可选动画效果:
|
||||
# 缩放: zoom_in, zoom_out
|
||||
# 平移: pan_left, pan_right, pan_up, pan_down
|
||||
# 组合: zoom_pan_left, zoom_pan_right, zoom_pan_up, zoom_pan_down
|
||||
|
||||
# 转场默认配置
|
||||
transition:
|
||||
type: "fade"
|
||||
duration: 0.5
|
||||
|
||||
# 可选转场效果:
|
||||
# 基础: fade, dissolve
|
||||
# 滑动: slide_left, slide_right, slide_up, slide_down
|
||||
# 特效: zoom_blur, wipe_left, wipe_right
|
||||
|
||||
# 片头默认配置
|
||||
intro:
|
||||
enabled: true
|
||||
duration: 3
|
||||
title_animation: "scale_in" # scale_in/fade_in/typewriter
|
||||
background_color: "#0d1117"
|
||||
title_color: "#FFFFFF"
|
||||
|
||||
# 片尾默认配置
|
||||
outro:
|
||||
enabled: true
|
||||
duration: 4
|
||||
logo: "assets/logo.jpg"
|
||||
logo_animation: "scale_in"
|
||||
background_color: "#0d1117"
|
||||
text: "点点关注,学习更多的AI知识"
|
||||
text_color: "#CCCCCC"
|
||||
|
||||
# 背景音乐(项目可配置)
|
||||
# background_music: "path/to/music.mp3"
|
||||
# music_volume: 0.1 # 0.0~1.0
|
||||
73
video-creator/assets/example_config.yaml
Normal file
@@ -0,0 +1,73 @@
|
||||
# 视频创作器 - 示例配置
|
||||
# 使用方法: python scripts/video_generator.py assets/example_config.yaml
|
||||
|
||||
# 视频基础配置
|
||||
title: "AI 前沿速递"
|
||||
output: "output/ai_news.mp4"
|
||||
resolution: [1920, 1080] # 横版 16:9
|
||||
|
||||
# 语音配置
|
||||
voice:
|
||||
name: "zh-CN-YunxiNeural" # 男声新闻风
|
||||
rate: "+0%"
|
||||
pitch: "+0Hz"
|
||||
|
||||
# 样式配置
|
||||
style:
|
||||
font: "PingFang SC"
|
||||
font_size: 48
|
||||
font_color: "#FFFFFF"
|
||||
stroke_color: "#000000"
|
||||
stroke_width: 2
|
||||
subtitle_position: "bottom"
|
||||
subtitle_bg: true
|
||||
subtitle_bg_color: "#000000"
|
||||
subtitle_bg_opacity: 0.6
|
||||
|
||||
# 动画配置
|
||||
animation:
|
||||
ken_burns: true
|
||||
default_animation: "zoom_in"
|
||||
animation_intensity: 0.1
|
||||
|
||||
# 转场配置
|
||||
transition:
|
||||
type: "fade"
|
||||
duration: 0.5
|
||||
|
||||
# 片头配置
|
||||
intro:
|
||||
enabled: true
|
||||
duration: 3
|
||||
title_animation: "fade_up"
|
||||
background_color: "#1a1a2e"
|
||||
title_color: "#FFFFFF"
|
||||
|
||||
# 片尾配置
|
||||
outro:
|
||||
enabled: true
|
||||
duration: 3
|
||||
logo: "assets/logo.jpg" # 微信公众号 Logo
|
||||
logo_animation: "bounce_in"
|
||||
background_color: "#1a1a2e"
|
||||
text: "点点关注,学习更多的AI知识"
|
||||
text_color: "#CCCCCC"
|
||||
|
||||
# 场景列表
|
||||
scenes:
|
||||
- image: "assets/scene_01.png"
|
||||
text: "今天我们来聊聊 AI Agent 的最新进展"
|
||||
animation: "zoom_in"
|
||||
|
||||
- image: "assets/scene_02.png"
|
||||
text: "首先是 OpenAI 发布的 Operator"
|
||||
animation: "pan_right"
|
||||
highlight: ["Operator"]
|
||||
|
||||
- image: "assets/scene_03.png"
|
||||
text: "它能自动操作浏览器完成复杂任务"
|
||||
animation: "zoom_out"
|
||||
|
||||
- image: "assets/scene_04.png"
|
||||
text: "这意味着 AI 正在从对话走向行动"
|
||||
animation: "pan_left"
|
||||
BIN
video-creator/assets/logo.jpg
Normal file
|
After Width: | Height: | Size: 39 KiB |
54
video-creator/assets/media/texts/25a546dbcb230f7f.svg
Normal file
@@ -0,0 +1,54 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1248" height="832" viewBox="0 0 1248 832">
|
||||
<defs>
|
||||
<g>
|
||||
<g id="glyph-0-0">
|
||||
<path d="M 5.722656 -10.414062 L 5.722656 -6.683594 L 2.25 -6.683594 L 2.25 -2.582031 L 10.707031 -2.582031 L 10.707031 -6.683594 L 6.632812 -6.683594 L 6.632812 -8.304688 L 11.589844 -8.304688 L 11.589844 -9.175781 L 6.632812 -9.175781 L 6.632812 -10.414062 Z M 9.789062 -3.4375 L 3.167969 -3.4375 L 3.167969 -5.8125 L 9.789062 -5.8125 Z M 2.222656 -2.019531 C 1.761719 -0.972656 1.214844 -0.0625 0.574219 0.691406 L 1.328125 1.238281 C 1.96875 0.421875 2.515625 -0.535156 3.003906 -1.648438 Z M 5.007812 -1.648438 L 4.152344 -1.46875 C 4.394531 -0.664062 4.601562 0.253906 4.777344 1.304688 L 5.699219 1.097656 C 5.496094 0.113281 5.265625 -0.804688 5.007812 -1.648438 Z M 7.679688 -1.777344 L 6.808594 -1.597656 C 7.195312 -0.765625 7.511719 0.191406 7.769531 1.277344 L 8.675781 1.074219 C 8.394531 0.0507812 8.0625 -0.90625 7.679688 -1.777344 Z M 10.296875 -2.03125 L 9.558594 -1.558594 C 10.335938 -0.511719 10.949219 0.433594 11.398438 1.304688 L 12.164062 0.765625 C 11.730469 -0.0273438 11.117188 -0.957031 10.296875 -2.03125 Z "/>
|
||||
</g>
|
||||
<g id="glyph-0-1">
|
||||
<path d="M 3.960938 -10.386719 L 3.117188 -9.980469 C 3.640625 -9.339844 4.128906 -8.613281 4.585938 -7.792969 L 5.417969 -8.214844 C 4.957031 -9.007812 4.472656 -9.722656 3.960938 -10.386719 Z M 8.765625 -10.503906 C 8.382812 -9.429688 7.960938 -8.523438 7.476562 -7.78125 L 1.636719 -7.78125 L 1.636719 -6.886719 L 5.914062 -6.886719 L 5.914062 -6.285156 C 5.902344 -5.660156 5.839844 -5.074219 5.722656 -4.511719 L 0.90625 -4.511719 L 0.90625 -3.617188 L 5.496094 -3.617188 C 5.316406 -3.054688 5.074219 -2.554688 4.765625 -2.09375 C 4 -1.074219 2.632812 -0.242188 0.675781 0.382812 L 1.175781 1.1875 C 3.144531 0.5625 4.5625 -0.320312 5.429688 -1.445312 C 5.839844 -1.992188 6.160156 -2.632812 6.402344 -3.359375 C 7.089844 -1.417969 8.828125 0.128906 11.640625 1.265625 L 12.175781 0.410156 C 9.492188 -0.550781 7.84375 -1.890625 7.21875 -3.617188 L 11.871094 -3.617188 L 11.871094 -4.511719 L 6.683594 -4.511719 C 6.785156 -5.046875 6.835938 -5.648438 6.863281 -6.285156 L 6.863281 -6.886719 L 11.128906 -6.886719 L 11.128906 -7.78125 L 8.496094 -7.78125 C 8.957031 -8.507812 9.339844 -9.339844 9.648438 -10.261719 Z "/>
|
||||
</g>
|
||||
<g id="glyph-0-2">
|
||||
<path d="M 1.675781 -10.246094 L 1.007812 -9.609375 C 1.953125 -8.945312 2.707031 -8.304688 3.246094 -7.703125 L 3.898438 -8.355469 C 3.296875 -8.984375 2.554688 -9.609375 1.675781 -10.246094 Z M 1.355469 -7.230469 L 0.703125 -6.605469 C 1.597656 -5.914062 2.300781 -5.277344 2.8125 -4.6875 L 3.460938 -5.339844 C 2.902344 -5.980469 2.199219 -6.605469 1.355469 -7.230469 Z M 2.964844 -3.707031 C 2.390625 -2.171875 1.710938 -0.675781 0.945312 0.78125 L 1.867188 1.175781 C 2.566406 -0.242188 3.207031 -1.761719 3.78125 -3.375 Z M 4.394531 -8.28125 L 4.394531 -7.371094 L 7.71875 -7.371094 L 7.71875 -4.304688 L 4.753906 -4.304688 L 4.753906 -3.425781 L 7.71875 -3.425781 L 7.71875 -0.0625 L 4.074219 -0.0625 L 4.074219 0.84375 L 12.191406 0.84375 L 12.191406 -0.0625 L 8.648438 -0.0625 L 8.648438 -3.425781 L 11.601562 -3.425781 L 11.601562 -4.304688 L 8.648438 -4.304688 L 8.648438 -7.371094 L 11.945312 -7.371094 L 11.945312 -8.28125 L 8.140625 -8.28125 L 8.664062 -8.484375 C 8.472656 -9.097656 8.191406 -9.761719 7.832031 -10.503906 L 6.964844 -10.261719 C 7.296875 -9.621094 7.578125 -8.96875 7.832031 -8.28125 Z "/>
|
||||
</g>
|
||||
<g id="glyph-0-3">
|
||||
<rect x="0" y="0" width="0" height="0" mask="url(#mask-0)"/>
|
||||
</g>
|
||||
<g id="glyph-0-4">
|
||||
<path d="M 0.640625 -5.148438 L 0.640625 -4.230469 L 12.125 -4.230469 L 12.125 -5.148438 Z "/>
|
||||
</g>
|
||||
<g id="glyph-0-5">
|
||||
<path d="M 12.011719 -3.539062 L 11.140625 -3.832031 C 11.089844 -3.09375 11.027344 -2.59375 10.9375 -2.351562 C 10.824219 -2.082031 10.578125 -1.941406 10.222656 -1.941406 L 8.484375 -1.941406 C 8.125 -1.953125 7.949219 -2.132812 7.949219 -2.480469 L 7.949219 -5.417969 L 11.398438 -5.417969 L 11.398438 -9.621094 L 6.808594 -9.621094 L 6.808594 -8.765625 L 10.503906 -8.765625 L 10.503906 -6.300781 L 7.027344 -6.300781 L 7.027344 -2.148438 C 7.027344 -1.417969 7.386719 -1.046875 8.113281 -1.046875 L 10.386719 -1.046875 C 10.988281 -1.046875 11.382812 -1.214844 11.578125 -1.519531 C 11.78125 -1.828125 11.933594 -2.503906 12.011719 -3.539062 Z M 1.546875 -4.355469 C 1.480469 -2.503906 1.125 -0.832031 0.484375 0.652344 L 1.1875 1.277344 C 1.519531 0.535156 1.777344 -0.253906 1.980469 -1.113281 C 2.464844 -0.34375 3.039062 0.203125 3.691406 0.511719 C 4.433594 0.855469 6.3125 1.035156 9.300781 1.035156 L 12.113281 1.035156 L 12.265625 0.140625 C 11.578125 0.179688 10.515625 0.203125 9.097656 0.203125 C 6.757812 0.203125 5.1875 0.0898438 4.394531 -0.128906 L 4.394531 -2.53125 L 6.210938 -2.53125 L 6.210938 -3.375 L 4.394531 -3.375 L 4.394531 -5.175781 L 6.363281 -5.175781 L 6.363281 -6.019531 L 4.152344 -6.019531 L 4.152344 -7.894531 L 6.195312 -7.894531 L 6.195312 -8.738281 L 4.152344 -8.738281 L 4.152344 -10.464844 L 3.234375 -10.464844 L 3.234375 -8.738281 L 1.125 -8.738281 L 1.125 -7.894531 L 3.234375 -7.894531 L 3.234375 -6.019531 L 0.894531 -6.019531 L 0.894531 -5.175781 L 3.515625 -5.175781 L 3.515625 -0.5625 C 3.003906 -0.921875 2.566406 -1.457031 2.183594 -2.171875 C 2.289062 -2.835938 2.351562 -3.527344 2.375 -4.253906 Z "/>
|
||||
</g>
|
||||
<g id="glyph-0-6">
|
||||
<path d="M 11.78125 -7.84375 L 9.316406 -7.84375 C 9.800781 -8.484375 10.261719 -9.25 10.707031 -10.171875 L 9.773438 -10.515625 C 9.367188 -9.558594 8.878906 -8.664062 8.316406 -7.84375 L 0.996094 -7.84375 L 0.996094 -4.984375 L 1.890625 -4.984375 L 1.890625 -6.964844 L 10.875 -6.964844 L 10.875 -4.984375 L 11.78125 -4.984375 Z M 2.953125 -5.878906 L 2.953125 -5.058594 L 8.523438 -5.058594 C 7.960938 -4.753906 7.15625 -4.371094 6.082031 -3.898438 L 6.082031 -2.835938 L 0.90625 -2.835938 L 0.90625 -1.96875 L 6.082031 -1.96875 L 6.082031 -0.128906 C 6.082031 0.230469 5.914062 0.410156 5.609375 0.410156 C 5.058594 0.410156 4.546875 0.394531 4.089844 0.382812 L 4.304688 1.226562 L 5.929688 1.226562 C 6.644531 1.226562 7.015625 0.882812 7.015625 0.191406 L 7.015625 -1.96875 L 11.882812 -1.96875 L 11.882812 -2.835938 L 7.015625 -2.835938 L 7.015625 -3.488281 C 8.0625 -4 8.996094 -4.523438 9.839844 -5.058594 L 9.839844 -5.878906 Z M 2.8125 -10.324219 L 1.980469 -9.914062 C 2.453125 -9.328125 2.902344 -8.664062 3.335938 -7.921875 L 4.128906 -8.316406 C 3.71875 -9.046875 3.269531 -9.710938 2.8125 -10.324219 Z M 5.902344 -10.566406 L 5.074219 -10.15625 C 5.507812 -9.542969 5.914062 -8.84375 6.300781 -8.050781 L 7.117188 -8.457031 C 6.734375 -9.226562 6.324219 -9.929688 5.902344 -10.566406 Z "/>
|
||||
</g>
|
||||
<g id="glyph-0-7">
|
||||
<path d="M 3.589844 -9.121094 L 0 0 L 1.125 0 L 2.09375 -2.554688 L 6.285156 -2.554688 L 7.257812 0 L 8.394531 0 L 4.804688 -9.121094 Z M 2.425781 -3.4375 L 4.179688 -8 L 4.230469 -8 L 5.953125 -3.4375 Z "/>
|
||||
</g>
|
||||
<g id="glyph-0-8">
|
||||
<path d="M 0.996094 -9.121094 L 0.996094 0 L 2.03125 0 L 2.03125 -9.121094 Z "/>
|
||||
</g>
|
||||
</g>
|
||||
<image id="source-8" x="0" y="0" width="0" height="0"/>
|
||||
<mask id="mask-0">
|
||||
<use xlink:href="#source-8"/>
|
||||
</mask>
|
||||
</defs>
|
||||
<g fill="rgb(100%, 100%, 100%)" fill-opacity="1">
|
||||
<use xlink:href="#glyph-0-0" x="30" y="33.543945"/>
|
||||
<use xlink:href="#glyph-0-0" x="43" y="33.543945"/>
|
||||
<use xlink:href="#glyph-0-1" x="56" y="33.543945"/>
|
||||
<use xlink:href="#glyph-0-2" x="69" y="33.543945"/>
|
||||
<use xlink:href="#glyph-0-3" x="82" y="33.543945"/>
|
||||
<use xlink:href="#glyph-0-3" x="86" y="33.543945"/>
|
||||
<use xlink:href="#glyph-0-4" x="90" y="33.543945"/>
|
||||
<use xlink:href="#glyph-0-5" x="103" y="33.543945"/>
|
||||
<use xlink:href="#glyph-0-6" x="116" y="33.543945"/>
|
||||
<use xlink:href="#glyph-0-3" x="129" y="33.543945"/>
|
||||
</g>
|
||||
<g fill="rgb(100%, 100%, 100%)" fill-opacity="1">
|
||||
<use xlink:href="#glyph-0-7" x="133" y="33.543945"/>
|
||||
<use xlink:href="#glyph-0-8" x="141" y="33.543945"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.8 KiB |
54
video-creator/assets/media/texts/60350aa7fd09283e.svg
Normal file
@@ -0,0 +1,54 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1536" height="672" viewBox="0 0 1536 672">
|
||||
<defs>
|
||||
<g>
|
||||
<g id="glyph-0-0">
|
||||
<path d="M 6.472656 -11.773438 L 6.472656 -7.554688 L 2.542969 -7.554688 L 2.542969 -2.917969 L 12.105469 -2.917969 L 12.105469 -7.554688 L 7.496094 -7.554688 L 7.496094 -9.390625 L 13.101562 -9.390625 L 13.101562 -10.371094 L 7.496094 -10.371094 L 7.496094 -11.773438 Z M 11.0625 -3.886719 L 3.582031 -3.886719 L 3.582031 -6.570312 L 11.0625 -6.570312 Z M 2.511719 -2.28125 C 1.992188 -1.097656 1.371094 -0.0703125 0.648438 0.78125 L 1.503906 1.402344 C 2.222656 0.476562 2.84375 -0.605469 3.394531 -1.863281 Z M 5.664062 -1.863281 L 4.695312 -1.660156 C 4.96875 -0.75 5.199219 0.289062 5.402344 1.472656 L 6.441406 1.242188 C 6.210938 0.128906 5.949219 -0.910156 5.664062 -1.863281 Z M 8.679688 -2.007812 L 7.699219 -1.804688 C 8.132812 -0.867188 8.492188 0.214844 8.78125 1.445312 L 9.808594 1.214844 C 9.488281 0.0585938 9.113281 -1.027344 8.679688 -2.007812 Z M 11.640625 -2.296875 L 10.804688 -1.761719 C 11.683594 -0.578125 12.378906 0.492188 12.882812 1.472656 L 13.75 0.867188 C 13.261719 -0.0273438 12.566406 -1.082031 11.640625 -2.296875 Z "/>
|
||||
</g>
|
||||
<g id="glyph-0-1">
|
||||
<path d="M 4.476562 -11.742188 L 3.523438 -11.28125 C 4.117188 -10.558594 4.664062 -9.734375 5.183594 -8.8125 L 6.125 -9.289062 C 5.605469 -10.183594 5.054688 -10.992188 4.476562 -11.742188 Z M 9.910156 -11.875 C 9.476562 -10.660156 9 -9.632812 8.449219 -8.796875 L 1.847656 -8.796875 L 1.847656 -7.785156 L 6.6875 -7.785156 L 6.6875 -7.105469 C 6.671875 -6.398438 6.601562 -5.734375 6.472656 -5.097656 L 1.027344 -5.097656 L 1.027344 -4.085938 L 6.210938 -4.085938 C 6.007812 -3.453125 5.734375 -2.890625 5.386719 -2.367188 C 4.519531 -1.214844 2.976562 -0.273438 0.765625 0.433594 L 1.328125 1.34375 C 3.554688 0.636719 5.15625 -0.359375 6.140625 -1.632812 C 6.601562 -2.253906 6.960938 -2.976562 7.238281 -3.800781 C 8.015625 -1.601562 9.980469 0.144531 13.160156 1.429688 L 13.765625 0.460938 C 10.730469 -0.621094 8.867188 -2.136719 8.160156 -4.085938 L 13.417969 -4.085938 L 13.417969 -5.097656 L 7.554688 -5.097656 C 7.671875 -5.707031 7.726562 -6.382812 7.757812 -7.105469 L 7.757812 -7.785156 L 12.582031 -7.785156 L 12.582031 -8.796875 L 9.605469 -8.796875 C 10.125 -9.621094 10.558594 -10.558594 10.90625 -11.597656 Z "/>
|
||||
</g>
|
||||
<g id="glyph-0-2">
|
||||
<path d="M 1.890625 -11.585938 L 1.140625 -10.863281 C 2.210938 -10.109375 3.0625 -9.390625 3.667969 -8.710938 L 4.40625 -9.445312 C 3.726562 -10.15625 2.890625 -10.863281 1.890625 -11.585938 Z M 1.53125 -8.175781 L 0.792969 -7.46875 C 1.804688 -6.6875 2.601562 -5.964844 3.179688 -5.300781 L 3.914062 -6.039062 C 3.277344 -6.761719 2.484375 -7.46875 1.53125 -8.175781 Z M 3.351562 -4.1875 C 2.699219 -2.457031 1.933594 -0.765625 1.070312 0.882812 L 2.109375 1.328125 C 2.902344 -0.273438 3.625 -1.992188 4.277344 -3.8125 Z M 4.96875 -9.359375 L 4.96875 -8.335938 L 8.722656 -8.335938 L 8.722656 -4.867188 L 5.375 -4.867188 L 5.375 -3.871094 L 8.722656 -3.871094 L 8.722656 -0.0703125 L 4.609375 -0.0703125 L 4.609375 0.953125 L 13.78125 0.953125 L 13.78125 -0.0703125 L 9.777344 -0.0703125 L 9.777344 -3.871094 L 13.117188 -3.871094 L 13.117188 -4.867188 L 9.777344 -4.867188 L 9.777344 -8.335938 L 13.503906 -8.335938 L 13.503906 -9.359375 L 9.199219 -9.359375 L 9.792969 -9.589844 C 9.578125 -10.285156 9.257812 -11.035156 8.855469 -11.875 L 7.871094 -11.597656 C 8.246094 -10.875 8.566406 -10.140625 8.855469 -9.359375 Z "/>
|
||||
</g>
|
||||
<g id="glyph-0-3">
|
||||
<rect x="0" y="0" width="0" height="0" mask="url(#mask-0)"/>
|
||||
</g>
|
||||
<g id="glyph-0-4">
|
||||
<path d="M 0.722656 -5.820312 L 0.722656 -4.78125 L 13.707031 -4.78125 L 13.707031 -5.820312 Z "/>
|
||||
</g>
|
||||
<g id="glyph-0-5">
|
||||
<path d="M 13.578125 -4 L 12.59375 -4.332031 C 12.539062 -3.496094 12.464844 -2.933594 12.363281 -2.65625 C 12.234375 -2.355469 11.960938 -2.195312 11.554688 -2.195312 L 9.589844 -2.195312 C 9.1875 -2.210938 8.984375 -2.414062 8.984375 -2.800781 L 8.984375 -6.125 L 12.882812 -6.125 L 12.882812 -10.875 L 7.699219 -10.875 L 7.699219 -9.910156 L 11.875 -9.910156 L 11.875 -7.121094 L 7.945312 -7.121094 L 7.945312 -2.425781 C 7.945312 -1.601562 8.347656 -1.183594 9.171875 -1.183594 L 11.742188 -1.183594 C 12.421875 -1.183594 12.871094 -1.371094 13.085938 -1.71875 C 13.316406 -2.066406 13.492188 -2.832031 13.578125 -4 Z M 1.746094 -4.925781 C 1.675781 -2.832031 1.269531 -0.9375 0.550781 0.738281 L 1.34375 1.445312 C 1.71875 0.605469 2.007812 -0.289062 2.238281 -1.257812 C 2.789062 -0.390625 3.4375 0.230469 4.175781 0.578125 C 5.011719 0.96875 7.136719 1.171875 10.515625 1.171875 L 13.691406 1.171875 L 13.867188 0.160156 C 13.085938 0.203125 11.886719 0.230469 10.285156 0.230469 C 7.640625 0.230469 5.863281 0.101562 4.96875 -0.144531 L 4.96875 -2.859375 L 7.019531 -2.859375 L 7.019531 -3.8125 L 4.96875 -3.8125 L 4.96875 -5.851562 L 7.191406 -5.851562 L 7.191406 -6.804688 L 4.695312 -6.804688 L 4.695312 -8.925781 L 7.003906 -8.925781 L 7.003906 -9.878906 L 4.695312 -9.878906 L 4.695312 -11.828125 L 3.65625 -11.828125 L 3.65625 -9.878906 L 1.269531 -9.878906 L 1.269531 -8.925781 L 3.65625 -8.925781 L 3.65625 -6.804688 L 1.011719 -6.804688 L 1.011719 -5.851562 L 3.972656 -5.851562 L 3.972656 -0.636719 C 3.394531 -1.039062 2.902344 -1.648438 2.46875 -2.457031 C 2.585938 -3.207031 2.65625 -3.988281 2.6875 -4.808594 Z "/>
|
||||
</g>
|
||||
<g id="glyph-0-6">
|
||||
<path d="M 13.316406 -8.867188 L 10.53125 -8.867188 C 11.078125 -9.589844 11.597656 -10.457031 12.105469 -11.496094 L 11.050781 -11.886719 C 10.585938 -10.804688 10.039062 -9.792969 9.402344 -8.867188 L 1.125 -8.867188 L 1.125 -5.632812 L 2.136719 -5.632812 L 2.136719 -7.871094 L 12.292969 -7.871094 L 12.292969 -5.632812 L 13.316406 -5.632812 Z M 3.335938 -6.644531 L 3.335938 -5.71875 L 9.632812 -5.71875 C 9 -5.375 8.089844 -4.941406 6.875 -4.40625 L 6.875 -3.207031 L 1.027344 -3.207031 L 1.027344 -2.222656 L 6.875 -2.222656 L 6.875 -0.144531 C 6.875 0.261719 6.6875 0.460938 6.339844 0.460938 C 5.71875 0.460938 5.140625 0.449219 4.621094 0.433594 L 4.867188 1.386719 L 6.703125 1.386719 C 7.511719 1.386719 7.929688 0.996094 7.929688 0.214844 L 7.929688 -2.222656 L 13.433594 -2.222656 L 13.433594 -3.207031 L 7.929688 -3.207031 L 7.929688 -3.941406 C 9.113281 -4.519531 10.167969 -5.113281 11.121094 -5.71875 L 11.121094 -6.644531 Z M 3.179688 -11.671875 L 2.238281 -11.207031 C 2.773438 -10.542969 3.277344 -9.792969 3.769531 -8.957031 L 4.664062 -9.402344 C 4.203125 -10.226562 3.699219 -10.976562 3.179688 -11.671875 Z M 6.671875 -11.945312 L 5.734375 -11.484375 C 6.226562 -10.789062 6.6875 -9.996094 7.121094 -9.101562 L 8.046875 -9.5625 C 7.613281 -10.429688 7.148438 -11.222656 6.671875 -11.945312 Z "/>
|
||||
</g>
|
||||
<g id="glyph-0-7">
|
||||
<path d="M 4.058594 -10.3125 L 0 0 L 1.269531 0 L 2.367188 -2.890625 L 7.105469 -2.890625 L 8.203125 0 L 9.488281 0 L 5.429688 -10.3125 Z M 2.746094 -3.886719 L 4.722656 -9.042969 L 4.78125 -9.042969 L 6.730469 -3.886719 Z "/>
|
||||
</g>
|
||||
<g id="glyph-0-8">
|
||||
<path d="M 1.125 -10.3125 L 1.125 0 L 2.296875 0 L 2.296875 -10.3125 Z "/>
|
||||
</g>
|
||||
</g>
|
||||
<image id="source-8" x="0" y="0" width="0" height="0"/>
|
||||
<mask id="mask-0">
|
||||
<use xlink:href="#source-8"/>
|
||||
</mask>
|
||||
</defs>
|
||||
<g fill="rgb(100%, 100%, 100%)" fill-opacity="1">
|
||||
<use xlink:href="#glyph-0-0" x="30" y="35.310547"/>
|
||||
<use xlink:href="#glyph-0-0" x="44" y="35.310547"/>
|
||||
<use xlink:href="#glyph-0-1" x="58" y="35.310547"/>
|
||||
<use xlink:href="#glyph-0-2" x="72" y="35.310547"/>
|
||||
<use xlink:href="#glyph-0-3" x="86" y="35.310547"/>
|
||||
<use xlink:href="#glyph-0-3" x="91" y="35.310547"/>
|
||||
<use xlink:href="#glyph-0-4" x="96" y="35.310547"/>
|
||||
<use xlink:href="#glyph-0-5" x="110" y="35.310547"/>
|
||||
<use xlink:href="#glyph-0-6" x="124" y="35.310547"/>
|
||||
<use xlink:href="#glyph-0-3" x="138" y="35.310547"/>
|
||||
</g>
|
||||
<g fill="rgb(100%, 100%, 100%)" fill-opacity="1">
|
||||
<use xlink:href="#glyph-0-7" x="143" y="35.310547"/>
|
||||
<use xlink:href="#glyph-0-8" x="152" y="35.310547"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.8 KiB |
54
video-creator/assets/media/texts/6bc75dd1367e9f40.svg
Normal file
@@ -0,0 +1,54 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="832" height="1248" viewBox="0 0 832 1248">
|
||||
<defs>
|
||||
<g>
|
||||
<g id="glyph-0-0">
|
||||
<path d="M 4.730469 -8.601562 L 4.730469 -5.519531 L 1.859375 -5.519531 L 1.859375 -2.132812 L 8.84375 -2.132812 L 8.84375 -5.519531 L 5.476562 -5.519531 L 5.476562 -6.859375 L 9.574219 -6.859375 L 9.574219 -7.578125 L 5.476562 -7.578125 L 5.476562 -8.601562 Z M 8.085938 -2.839844 L 2.617188 -2.839844 L 2.617188 -4.804688 L 8.085938 -4.804688 Z M 1.835938 -1.667969 C 1.457031 -0.800781 1.003906 -0.0546875 0.476562 0.570312 L 1.097656 1.023438 C 1.625 0.347656 2.078125 -0.441406 2.480469 -1.363281 Z M 4.136719 -1.363281 L 3.429688 -1.214844 C 3.632812 -0.550781 3.800781 0.210938 3.949219 1.078125 L 4.707031 0.90625 C 4.539062 0.09375 4.347656 -0.664062 4.136719 -1.363281 Z M 6.34375 -1.46875 L 5.625 -1.320312 C 5.941406 -0.632812 6.207031 0.160156 6.417969 1.054688 L 7.167969 0.886719 C 6.933594 0.0429688 6.660156 -0.75 6.34375 -1.46875 Z M 8.507812 -1.679688 L 7.894531 -1.289062 C 8.539062 -0.421875 9.046875 0.359375 9.414062 1.078125 L 10.050781 0.632812 C 9.691406 -0.0195312 9.183594 -0.792969 8.507812 -1.679688 Z "/>
|
||||
</g>
|
||||
<g id="glyph-0-1">
|
||||
<path d="M 3.273438 -8.582031 L 2.574219 -8.242188 C 3.007812 -7.714844 3.410156 -7.113281 3.789062 -6.4375 L 4.476562 -6.789062 C 4.09375 -7.441406 3.695312 -8.03125 3.273438 -8.582031 Z M 7.242188 -8.675781 C 6.925781 -7.789062 6.578125 -7.039062 6.175781 -6.429688 L 1.351562 -6.429688 L 1.351562 -5.691406 L 4.886719 -5.691406 L 4.886719 -5.195312 C 4.875 -4.675781 4.824219 -4.191406 4.730469 -3.726562 L 0.75 -3.726562 L 0.75 -2.988281 L 4.539062 -2.988281 C 4.390625 -2.523438 4.191406 -2.109375 3.9375 -1.730469 C 3.304688 -0.886719 2.175781 -0.199219 0.558594 0.316406 L 0.972656 0.980469 C 2.597656 0.464844 3.769531 -0.265625 4.484375 -1.191406 C 4.824219 -1.648438 5.085938 -2.175781 5.289062 -2.777344 C 5.859375 -1.171875 7.292969 0.105469 9.617188 1.046875 L 10.058594 0.335938 C 7.84375 -0.453125 6.480469 -1.5625 5.964844 -2.988281 L 9.804688 -2.988281 L 9.804688 -3.726562 L 5.519531 -3.726562 C 5.605469 -4.167969 5.648438 -4.664062 5.667969 -5.195312 L 5.667969 -5.691406 L 9.195312 -5.691406 L 9.195312 -6.429688 L 7.019531 -6.429688 C 7.398438 -7.03125 7.714844 -7.714844 7.96875 -8.476562 Z "/>
|
||||
</g>
|
||||
<g id="glyph-0-2">
|
||||
<path d="M 1.382812 -8.464844 L 0.832031 -7.9375 C 1.613281 -7.390625 2.238281 -6.859375 2.679688 -6.363281 L 3.21875 -6.902344 C 2.722656 -7.421875 2.109375 -7.9375 1.382812 -8.464844 Z M 1.117188 -5.972656 L 0.582031 -5.457031 C 1.320312 -4.886719 1.898438 -4.359375 2.320312 -3.875 L 2.859375 -4.414062 C 2.394531 -4.941406 1.816406 -5.457031 1.117188 -5.972656 Z M 2.449219 -3.0625 C 1.972656 -1.792969 1.414062 -0.558594 0.78125 0.644531 L 1.542969 0.972656 C 2.121094 -0.199219 2.648438 -1.457031 3.125 -2.785156 Z M 3.632812 -6.839844 L 3.632812 -6.089844 L 6.375 -6.089844 L 6.375 -3.558594 L 3.925781 -3.558594 L 3.925781 -2.828125 L 6.375 -2.828125 L 6.375 -0.0546875 L 3.367188 -0.0546875 L 3.367188 0.695312 L 10.070312 0.695312 L 10.070312 -0.0546875 L 7.144531 -0.0546875 L 7.144531 -2.828125 L 9.585938 -2.828125 L 9.585938 -3.558594 L 7.144531 -3.558594 L 7.144531 -6.089844 L 9.871094 -6.089844 L 9.871094 -6.839844 L 6.722656 -6.839844 L 7.15625 -7.007812 C 7 -7.515625 6.765625 -8.066406 6.46875 -8.675781 L 5.753906 -8.476562 C 6.027344 -7.949219 6.257812 -7.410156 6.46875 -6.839844 Z "/>
|
||||
</g>
|
||||
<g id="glyph-0-3">
|
||||
<rect x="0" y="0" width="0" height="0" mask="url(#mask-0)"/>
|
||||
</g>
|
||||
<g id="glyph-0-4">
|
||||
<path d="M 0.527344 -4.253906 L 0.527344 -3.492188 L 10.015625 -3.492188 L 10.015625 -4.253906 Z "/>
|
||||
</g>
|
||||
<g id="glyph-0-5">
|
||||
<path d="M 9.921875 -2.925781 L 9.203125 -3.167969 C 9.164062 -2.554688 9.109375 -2.144531 9.035156 -1.941406 C 8.941406 -1.71875 8.738281 -1.605469 8.445312 -1.605469 L 7.007812 -1.605469 C 6.714844 -1.613281 6.566406 -1.761719 6.566406 -2.046875 L 6.566406 -4.476562 L 9.414062 -4.476562 L 9.414062 -7.949219 L 5.625 -7.949219 L 5.625 -7.242188 L 8.675781 -7.242188 L 8.675781 -5.203125 L 5.804688 -5.203125 L 5.804688 -1.773438 C 5.804688 -1.171875 6.101562 -0.867188 6.703125 -0.867188 L 8.582031 -0.867188 C 9.078125 -0.867188 9.40625 -1.003906 9.5625 -1.257812 C 9.730469 -1.507812 9.859375 -2.070312 9.921875 -2.925781 Z M 1.277344 -3.597656 C 1.222656 -2.070312 0.929688 -0.6875 0.402344 0.539062 L 0.980469 1.054688 C 1.257812 0.441406 1.46875 -0.210938 1.636719 -0.917969 C 2.039062 -0.285156 2.511719 0.167969 3.050781 0.421875 C 3.664062 0.707031 5.214844 0.855469 7.683594 0.855469 L 10.007812 0.855469 L 10.132812 0.117188 C 9.5625 0.148438 8.6875 0.167969 7.515625 0.167969 C 5.582031 0.167969 4.285156 0.0742188 3.632812 -0.105469 L 3.632812 -2.089844 L 5.128906 -2.089844 L 5.128906 -2.785156 L 3.632812 -2.785156 L 3.632812 -4.273438 L 5.257812 -4.273438 L 5.257812 -4.972656 L 3.429688 -4.972656 L 3.429688 -6.523438 L 5.121094 -6.523438 L 5.121094 -7.21875 L 3.429688 -7.21875 L 3.429688 -8.644531 L 2.671875 -8.644531 L 2.671875 -7.21875 L 0.929688 -7.21875 L 0.929688 -6.523438 L 2.671875 -6.523438 L 2.671875 -4.972656 L 0.738281 -4.972656 L 0.738281 -4.273438 L 2.902344 -4.273438 L 2.902344 -0.464844 C 2.480469 -0.761719 2.121094 -1.203125 1.804688 -1.792969 C 1.890625 -2.34375 1.941406 -2.914062 1.964844 -3.515625 Z "/>
|
||||
</g>
|
||||
<g id="glyph-0-6">
|
||||
<path d="M 9.730469 -6.480469 L 7.695312 -6.480469 C 8.097656 -7.007812 8.476562 -7.640625 8.84375 -8.402344 L 8.074219 -8.6875 C 7.738281 -7.894531 7.335938 -7.15625 6.871094 -6.480469 L 0.824219 -6.480469 L 0.824219 -4.117188 L 1.5625 -4.117188 L 1.5625 -5.753906 L 8.984375 -5.753906 L 8.984375 -4.117188 L 9.730469 -4.117188 Z M 2.4375 -4.855469 L 2.4375 -4.179688 L 7.039062 -4.179688 C 6.578125 -3.925781 5.910156 -3.609375 5.023438 -3.21875 L 5.023438 -2.34375 L 0.75 -2.34375 L 0.75 -1.625 L 5.023438 -1.625 L 5.023438 -0.105469 C 5.023438 0.191406 4.886719 0.335938 4.632812 0.335938 C 4.179688 0.335938 3.757812 0.328125 3.378906 0.316406 L 3.558594 1.011719 L 4.898438 1.011719 C 5.488281 1.011719 5.796875 0.726562 5.796875 0.160156 L 5.796875 -1.625 L 9.816406 -1.625 L 9.816406 -2.34375 L 5.796875 -2.34375 L 5.796875 -2.882812 C 6.660156 -3.304688 7.429688 -3.738281 8.128906 -4.179688 L 8.128906 -4.855469 Z M 2.320312 -8.527344 L 1.636719 -8.191406 C 2.027344 -7.707031 2.394531 -7.15625 2.753906 -6.542969 L 3.410156 -6.871094 C 3.070312 -7.472656 2.703125 -8.023438 2.320312 -8.527344 Z M 4.875 -8.730469 L 4.191406 -8.390625 C 4.550781 -7.886719 4.886719 -7.304688 5.203125 -6.648438 L 5.878906 -6.988281 C 5.5625 -7.621094 5.226562 -8.203125 4.875 -8.730469 Z "/>
|
||||
</g>
|
||||
<g id="glyph-0-7">
|
||||
<path d="M 2.964844 -7.535156 L 0 0 L 0.929688 0 L 1.730469 -2.109375 L 5.195312 -2.109375 L 5.996094 0 L 6.933594 0 L 3.96875 -7.535156 Z M 2.003906 -2.839844 L 3.453125 -6.609375 L 3.492188 -6.609375 L 4.917969 -2.839844 Z "/>
|
||||
</g>
|
||||
<g id="glyph-0-8">
|
||||
<path d="M 0.824219 -7.535156 L 0.824219 0 L 1.679688 0 L 1.679688 -7.535156 Z "/>
|
||||
</g>
|
||||
</g>
|
||||
<image id="source-8" x="0" y="0" width="0" height="0"/>
|
||||
<mask id="mask-0">
|
||||
<use xlink:href="#source-8"/>
|
||||
</mask>
|
||||
</defs>
|
||||
<g fill="rgb(100%, 100%, 100%)" fill-opacity="1">
|
||||
<use xlink:href="#glyph-0-0" x="30" y="31.189453"/>
|
||||
<use xlink:href="#glyph-0-0" x="41" y="31.189453"/>
|
||||
<use xlink:href="#glyph-0-1" x="52" y="31.189453"/>
|
||||
<use xlink:href="#glyph-0-2" x="63" y="31.189453"/>
|
||||
<use xlink:href="#glyph-0-3" x="74" y="31.189453"/>
|
||||
<use xlink:href="#glyph-0-3" x="78" y="31.189453"/>
|
||||
<use xlink:href="#glyph-0-4" x="82" y="31.189453"/>
|
||||
<use xlink:href="#glyph-0-5" x="93" y="31.189453"/>
|
||||
<use xlink:href="#glyph-0-6" x="104" y="31.189453"/>
|
||||
<use xlink:href="#glyph-0-3" x="115" y="31.189453"/>
|
||||
</g>
|
||||
<g fill="rgb(100%, 100%, 100%)" fill-opacity="1">
|
||||
<use xlink:href="#glyph-0-7" x="119" y="31.189453"/>
|
||||
<use xlink:href="#glyph-0-8" x="126" y="31.189453"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.8 KiB |
54
video-creator/assets/media/texts/94ee77f0d4c0bc26.svg
Normal file
@@ -0,0 +1,54 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1080" height="1920" viewBox="0 0 1080 1920">
|
||||
<defs>
|
||||
<g>
|
||||
<g id="glyph-0-0">
|
||||
<path d="M 4.976562 -9.054688 L 4.976562 -5.8125 L 1.957031 -5.8125 L 1.957031 -2.246094 L 9.308594 -2.246094 L 9.308594 -5.8125 L 5.765625 -5.8125 L 5.765625 -7.222656 L 10.078125 -7.222656 L 10.078125 -7.976562 L 5.765625 -7.976562 L 5.765625 -9.054688 Z M 8.511719 -2.988281 L 2.753906 -2.988281 L 2.753906 -5.054688 L 8.511719 -5.054688 Z M 1.933594 -1.753906 C 1.535156 -0.84375 1.054688 -0.0546875 0.5 0.601562 L 1.15625 1.078125 C 1.710938 0.367188 2.1875 -0.464844 2.609375 -1.433594 Z M 4.355469 -1.433594 L 3.609375 -1.277344 C 3.820312 -0.578125 4 0.222656 4.15625 1.132812 L 4.957031 0.957031 C 4.777344 0.101562 4.578125 -0.699219 4.355469 -1.433594 Z M 6.675781 -1.542969 L 5.921875 -1.390625 C 6.253906 -0.667969 6.53125 0.167969 6.753906 1.109375 L 7.542969 0.933594 C 7.300781 0.0429688 7.011719 -0.789062 6.675781 -1.542969 Z M 8.953125 -1.765625 L 8.308594 -1.355469 C 8.988281 -0.445312 9.523438 0.378906 9.910156 1.132812 L 10.578125 0.667969 C 10.199219 -0.0234375 9.664062 -0.832031 8.953125 -1.765625 Z "/>
|
||||
</g>
|
||||
<g id="glyph-0-1">
|
||||
<path d="M 3.445312 -9.03125 L 2.710938 -8.675781 C 3.167969 -8.121094 3.589844 -7.488281 3.988281 -6.777344 L 4.710938 -7.144531 C 4.3125 -7.832031 3.886719 -8.453125 3.445312 -9.03125 Z M 7.621094 -9.132812 C 7.289062 -8.199219 6.921875 -7.410156 6.5 -6.765625 L 1.421875 -6.765625 L 1.421875 -5.988281 L 5.144531 -5.988281 L 5.144531 -5.464844 C 5.132812 -4.921875 5.078125 -4.410156 4.976562 -3.921875 L 0.789062 -3.921875 L 0.789062 -3.144531 L 4.777344 -3.144531 C 4.621094 -2.65625 4.410156 -2.222656 4.144531 -1.820312 C 3.476562 -0.933594 2.289062 -0.210938 0.589844 0.332031 L 1.023438 1.035156 C 2.734375 0.488281 3.964844 -0.277344 4.722656 -1.253906 C 5.078125 -1.734375 5.355469 -2.289062 5.566406 -2.921875 C 6.167969 -1.234375 7.675781 0.109375 10.121094 1.101562 L 10.589844 0.355469 C 8.253906 -0.476562 6.820312 -1.644531 6.277344 -3.144531 L 10.320312 -3.144531 L 10.320312 -3.921875 L 5.8125 -3.921875 C 5.898438 -4.386719 5.945312 -4.910156 5.964844 -5.464844 L 5.964844 -5.988281 L 9.675781 -5.988281 L 9.675781 -6.765625 L 7.386719 -6.765625 C 7.789062 -7.398438 8.121094 -8.121094 8.386719 -8.921875 Z "/>
|
||||
</g>
|
||||
<g id="glyph-0-2">
|
||||
<path d="M 1.457031 -8.910156 L 0.878906 -8.355469 C 1.699219 -7.777344 2.355469 -7.222656 2.820312 -6.699219 L 3.386719 -7.265625 C 2.867188 -7.8125 2.222656 -8.355469 1.457031 -8.910156 Z M 1.175781 -6.289062 L 0.609375 -5.742188 C 1.390625 -5.144531 2 -4.589844 2.445312 -4.078125 L 3.011719 -4.644531 C 2.523438 -5.199219 1.910156 -5.742188 1.175781 -6.289062 Z M 2.578125 -3.222656 C 2.078125 -1.890625 1.488281 -0.589844 0.820312 0.675781 L 1.621094 1.023438 C 2.234375 -0.210938 2.789062 -1.535156 3.289062 -2.933594 Z M 3.820312 -7.199219 L 3.820312 -6.410156 L 6.710938 -6.410156 L 6.710938 -3.746094 L 4.132812 -3.746094 L 4.132812 -2.976562 L 6.710938 -2.976562 L 6.710938 -0.0546875 L 3.542969 -0.0546875 L 3.542969 0.734375 L 10.597656 0.734375 L 10.597656 -0.0546875 L 7.523438 -0.0546875 L 7.523438 -2.976562 L 10.089844 -2.976562 L 10.089844 -3.746094 L 7.523438 -3.746094 L 7.523438 -6.410156 L 10.386719 -6.410156 L 10.386719 -7.199219 L 7.078125 -7.199219 L 7.53125 -7.378906 C 7.367188 -7.910156 7.121094 -8.488281 6.8125 -9.132812 L 6.054688 -8.921875 C 6.34375 -8.367188 6.589844 -7.800781 6.8125 -7.199219 Z "/>
|
||||
</g>
|
||||
<g id="glyph-0-3">
|
||||
<rect x="0" y="0" width="0" height="0" mask="url(#mask-0)"/>
|
||||
</g>
|
||||
<g id="glyph-0-4">
|
||||
<path d="M 0.554688 -4.476562 L 0.554688 -3.675781 L 10.542969 -3.675781 L 10.542969 -4.476562 Z "/>
|
||||
</g>
|
||||
<g id="glyph-0-5">
|
||||
<path d="M 10.445312 -3.078125 L 9.6875 -3.332031 C 9.644531 -2.6875 9.589844 -2.253906 9.511719 -2.042969 C 9.410156 -1.8125 9.199219 -1.6875 8.886719 -1.6875 L 7.378906 -1.6875 C 7.066406 -1.699219 6.910156 -1.855469 6.910156 -2.15625 L 6.910156 -4.710938 L 9.910156 -4.710938 L 9.910156 -8.367188 L 5.921875 -8.367188 L 5.921875 -7.621094 L 9.132812 -7.621094 L 9.132812 -5.476562 L 6.109375 -5.476562 L 6.109375 -1.867188 C 6.109375 -1.234375 6.421875 -0.910156 7.054688 -0.910156 L 9.03125 -0.910156 C 9.554688 -0.910156 9.898438 -1.054688 10.066406 -1.320312 C 10.242188 -1.589844 10.378906 -2.175781 10.445312 -3.078125 Z M 1.34375 -3.789062 C 1.289062 -2.175781 0.976562 -0.722656 0.421875 0.566406 L 1.035156 1.109375 C 1.320312 0.464844 1.542969 -0.222656 1.722656 -0.964844 C 2.144531 -0.300781 2.644531 0.179688 3.210938 0.445312 C 3.855469 0.746094 5.488281 0.898438 8.089844 0.898438 L 10.53125 0.898438 L 10.664062 0.121094 C 10.066406 0.15625 9.144531 0.179688 7.910156 0.179688 C 5.878906 0.179688 4.511719 0.078125 3.820312 -0.109375 L 3.820312 -2.199219 L 5.398438 -2.199219 L 5.398438 -2.933594 L 3.820312 -2.933594 L 3.820312 -4.5 L 5.53125 -4.5 L 5.53125 -5.234375 L 3.609375 -5.234375 L 3.609375 -6.867188 L 5.386719 -6.867188 L 5.386719 -7.597656 L 3.609375 -7.597656 L 3.609375 -9.097656 L 2.8125 -9.097656 L 2.8125 -7.597656 L 0.976562 -7.597656 L 0.976562 -6.867188 L 2.8125 -6.867188 L 2.8125 -5.234375 L 0.777344 -5.234375 L 0.777344 -4.5 L 3.054688 -4.5 L 3.054688 -0.488281 C 2.609375 -0.800781 2.234375 -1.265625 1.898438 -1.890625 C 1.988281 -2.464844 2.042969 -3.066406 2.066406 -3.699219 Z "/>
|
||||
</g>
|
||||
<g id="glyph-0-6">
|
||||
<path d="M 10.242188 -6.820312 L 8.097656 -6.820312 C 8.523438 -7.378906 8.921875 -8.042969 9.308594 -8.84375 L 8.5 -9.144531 C 8.144531 -8.308594 7.722656 -7.53125 7.234375 -6.820312 L 0.867188 -6.820312 L 0.867188 -4.332031 L 1.644531 -4.332031 L 1.644531 -6.054688 L 9.453125 -6.054688 L 9.453125 -4.332031 L 10.242188 -4.332031 Z M 2.566406 -5.109375 L 2.566406 -4.398438 L 7.410156 -4.398438 C 6.921875 -4.132812 6.222656 -3.800781 5.289062 -3.386719 L 5.289062 -2.464844 L 0.789062 -2.464844 L 0.789062 -1.710938 L 5.289062 -1.710938 L 5.289062 -0.109375 C 5.289062 0.199219 5.144531 0.355469 4.878906 0.355469 C 4.398438 0.355469 3.957031 0.34375 3.554688 0.332031 L 3.746094 1.066406 L 5.15625 1.066406 C 5.777344 1.066406 6.097656 0.765625 6.097656 0.167969 L 6.097656 -1.710938 L 10.332031 -1.710938 L 10.332031 -2.464844 L 6.097656 -2.464844 L 6.097656 -3.03125 C 7.011719 -3.476562 7.820312 -3.933594 8.554688 -4.398438 L 8.554688 -5.109375 Z M 2.445312 -8.976562 L 1.722656 -8.621094 C 2.132812 -8.109375 2.523438 -7.53125 2.898438 -6.886719 L 3.589844 -7.234375 C 3.234375 -7.867188 2.84375 -8.445312 2.445312 -8.976562 Z M 5.132812 -9.1875 L 4.410156 -8.832031 C 4.789062 -8.300781 5.144531 -7.6875 5.476562 -7 L 6.1875 -7.355469 C 5.855469 -8.023438 5.5 -8.632812 5.132812 -9.1875 Z "/>
|
||||
</g>
|
||||
<g id="glyph-0-7">
|
||||
<path d="M 3.121094 -7.933594 L 0 0 L 0.976562 0 L 1.820312 -2.222656 L 5.464844 -2.222656 L 6.3125 0 L 7.300781 0 L 4.175781 -7.933594 Z M 2.109375 -2.988281 L 3.632812 -6.957031 L 3.675781 -6.957031 L 5.175781 -2.988281 Z "/>
|
||||
</g>
|
||||
<g id="glyph-0-8">
|
||||
<path d="M 0.867188 -7.933594 L 0.867188 0 L 1.765625 0 L 1.765625 -7.933594 Z "/>
|
||||
</g>
|
||||
</g>
|
||||
<image id="source-8" x="0" y="0" width="0" height="0"/>
|
||||
<mask id="mask-0">
|
||||
<use xlink:href="#source-8"/>
|
||||
</mask>
|
||||
</defs>
|
||||
<g fill="rgb(100%, 100%, 100%)" fill-opacity="1">
|
||||
<use xlink:href="#glyph-0-0" x="30" y="31.777344"/>
|
||||
<use xlink:href="#glyph-0-0" x="41" y="31.777344"/>
|
||||
<use xlink:href="#glyph-0-1" x="52" y="31.777344"/>
|
||||
<use xlink:href="#glyph-0-2" x="63" y="31.777344"/>
|
||||
<use xlink:href="#glyph-0-3" x="74" y="31.777344"/>
|
||||
<use xlink:href="#glyph-0-3" x="78" y="31.777344"/>
|
||||
<use xlink:href="#glyph-0-4" x="82" y="31.777344"/>
|
||||
<use xlink:href="#glyph-0-5" x="93" y="31.777344"/>
|
||||
<use xlink:href="#glyph-0-6" x="104" y="31.777344"/>
|
||||
<use xlink:href="#glyph-0-3" x="115" y="31.777344"/>
|
||||
</g>
|
||||
<g fill="rgb(100%, 100%, 100%)" fill-opacity="1">
|
||||
<use xlink:href="#glyph-0-7" x="119" y="31.777344"/>
|
||||
<use xlink:href="#glyph-0-8" x="126" y="31.777344"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.8 KiB |
54
video-creator/assets/media/texts/9fded5d4bc94afcd.svg
Normal file
@@ -0,0 +1,54 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="864" height="1080" viewBox="0 0 864 1080">
|
||||
<defs>
|
||||
<g>
|
||||
<g id="glyph-0-0">
|
||||
<path d="M 4.480469 -8.148438 L 4.480469 -5.230469 L 1.761719 -5.230469 L 1.761719 -2.019531 L 8.378906 -2.019531 L 8.378906 -5.230469 L 5.191406 -5.230469 L 5.191406 -6.5 L 9.070312 -6.5 L 9.070312 -7.179688 L 5.191406 -7.179688 L 5.191406 -8.148438 Z M 7.660156 -2.691406 L 2.480469 -2.691406 L 2.480469 -4.550781 L 7.660156 -4.550781 Z M 1.738281 -1.578125 C 1.378906 -0.761719 0.949219 -0.0507812 0.449219 0.539062 L 1.039062 0.96875 C 1.539062 0.328125 1.96875 -0.421875 2.351562 -1.289062 Z M 3.921875 -1.289062 L 3.25 -1.148438 C 3.441406 -0.519531 3.601562 0.199219 3.738281 1.019531 L 4.460938 0.859375 C 4.300781 0.0898438 4.121094 -0.628906 3.921875 -1.289062 Z M 6.011719 -1.390625 L 5.328125 -1.25 C 5.628906 -0.601562 5.878906 0.148438 6.078125 1 L 6.789062 0.839844 C 6.570312 0.0390625 6.308594 -0.710938 6.011719 -1.390625 Z M 8.058594 -1.589844 L 7.480469 -1.21875 C 8.089844 -0.398438 8.570312 0.339844 8.921875 1.019531 L 9.519531 0.601562 C 9.179688 -0.0195312 8.699219 -0.75 8.058594 -1.589844 Z "/>
|
||||
</g>
|
||||
<g id="glyph-0-1">
|
||||
<path d="M 3.101562 -8.128906 L 2.441406 -7.808594 C 2.851562 -7.308594 3.230469 -6.738281 3.589844 -6.101562 L 4.238281 -6.429688 C 3.878906 -7.050781 3.5 -7.609375 3.101562 -8.128906 Z M 6.859375 -8.21875 C 6.558594 -7.378906 6.230469 -6.671875 5.851562 -6.089844 L 1.28125 -6.089844 L 1.28125 -5.390625 L 4.628906 -5.390625 L 4.628906 -4.921875 C 4.621094 -4.429688 4.570312 -3.96875 4.480469 -3.53125 L 0.710938 -3.53125 L 0.710938 -2.828125 L 4.300781 -2.828125 C 4.160156 -2.390625 3.96875 -2 3.730469 -1.640625 C 3.128906 -0.839844 2.058594 -0.191406 0.53125 0.300781 L 0.921875 0.929688 C 2.460938 0.441406 3.570312 -0.25 4.25 -1.128906 C 4.570312 -1.558594 4.820312 -2.058594 5.011719 -2.628906 C 5.550781 -1.109375 6.910156 0.101562 9.109375 0.988281 L 9.53125 0.320312 C 7.429688 -0.429688 6.140625 -1.480469 5.648438 -2.828125 L 9.289062 -2.828125 L 9.289062 -3.53125 L 5.230469 -3.53125 C 5.308594 -3.949219 5.351562 -4.421875 5.371094 -4.921875 L 5.371094 -5.390625 L 8.710938 -5.390625 L 8.710938 -6.089844 L 6.648438 -6.089844 C 7.011719 -6.660156 7.308594 -7.308594 7.550781 -8.03125 Z "/>
|
||||
</g>
|
||||
<g id="glyph-0-2">
|
||||
<path d="M 1.308594 -8.019531 L 0.789062 -7.519531 C 1.53125 -7 2.121094 -6.5 2.539062 -6.03125 L 3.050781 -6.539062 C 2.578125 -7.03125 2 -7.519531 1.308594 -8.019531 Z M 1.058594 -5.660156 L 0.550781 -5.171875 C 1.25 -4.628906 1.800781 -4.128906 2.199219 -3.671875 L 2.710938 -4.179688 C 2.269531 -4.679688 1.71875 -5.171875 1.058594 -5.660156 Z M 2.320312 -2.898438 C 1.871094 -1.699219 1.339844 -0.53125 0.738281 0.609375 L 1.460938 0.921875 C 2.011719 -0.191406 2.511719 -1.378906 2.960938 -2.640625 Z M 3.441406 -6.480469 L 3.441406 -5.769531 L 6.039062 -5.769531 L 6.039062 -3.371094 L 3.71875 -3.371094 L 3.71875 -2.679688 L 6.039062 -2.679688 L 6.039062 -0.0507812 L 3.191406 -0.0507812 L 3.191406 0.660156 L 9.539062 0.660156 L 9.539062 -0.0507812 L 6.769531 -0.0507812 L 6.769531 -2.679688 L 9.078125 -2.679688 L 9.078125 -3.371094 L 6.769531 -3.371094 L 6.769531 -5.769531 L 9.351562 -5.769531 L 9.351562 -6.480469 L 6.371094 -6.480469 L 6.78125 -6.640625 C 6.628906 -7.121094 6.410156 -7.640625 6.128906 -8.21875 L 5.449219 -8.03125 C 5.710938 -7.53125 5.929688 -7.019531 6.128906 -6.480469 Z "/>
|
||||
</g>
|
||||
<g id="glyph-0-3">
|
||||
<rect x="0" y="0" width="0" height="0" mask="url(#mask-0)"/>
|
||||
</g>
|
||||
<g id="glyph-0-4">
|
||||
<path d="M 0.5 -4.03125 L 0.5 -3.308594 L 9.488281 -3.308594 L 9.488281 -4.03125 Z "/>
|
||||
</g>
|
||||
<g id="glyph-0-5">
|
||||
<path d="M 9.398438 -2.769531 L 8.71875 -3 C 8.679688 -2.421875 8.628906 -2.03125 8.558594 -1.839844 C 8.46875 -1.628906 8.28125 -1.519531 8 -1.519531 L 6.640625 -1.519531 C 6.359375 -1.53125 6.21875 -1.671875 6.21875 -1.941406 L 6.21875 -4.238281 L 8.921875 -4.238281 L 8.921875 -7.53125 L 5.328125 -7.53125 L 5.328125 -6.859375 L 8.21875 -6.859375 L 8.21875 -4.929688 L 5.5 -4.929688 L 5.5 -1.679688 C 5.5 -1.109375 5.78125 -0.820312 6.351562 -0.820312 L 8.128906 -0.820312 C 8.601562 -0.820312 8.910156 -0.949219 9.058594 -1.191406 C 9.21875 -1.429688 9.339844 -1.960938 9.398438 -2.769531 Z M 1.210938 -3.410156 C 1.160156 -1.960938 0.878906 -0.648438 0.378906 0.511719 L 0.929688 1 C 1.191406 0.421875 1.390625 -0.199219 1.550781 -0.871094 C 1.929688 -0.269531 2.378906 0.160156 2.890625 0.398438 C 3.46875 0.671875 4.941406 0.808594 7.28125 0.808594 L 9.480469 0.808594 L 9.601562 0.109375 C 9.058594 0.140625 8.230469 0.160156 7.121094 0.160156 C 5.289062 0.160156 4.058594 0.0703125 3.441406 -0.101562 L 3.441406 -1.980469 L 4.859375 -1.980469 L 4.859375 -2.640625 L 3.441406 -2.640625 L 3.441406 -4.050781 L 4.980469 -4.050781 L 4.980469 -4.710938 L 3.25 -4.710938 L 3.25 -6.179688 L 4.851562 -6.179688 L 4.851562 -6.839844 L 3.25 -6.839844 L 3.25 -8.191406 L 2.53125 -8.191406 L 2.53125 -6.839844 L 0.878906 -6.839844 L 0.878906 -6.179688 L 2.53125 -6.179688 L 2.53125 -4.710938 L 0.699219 -4.710938 L 0.699219 -4.050781 L 2.75 -4.050781 L 2.75 -0.441406 C 2.351562 -0.71875 2.011719 -1.140625 1.710938 -1.699219 C 1.789062 -2.21875 1.839844 -2.761719 1.859375 -3.328125 Z "/>
|
||||
</g>
|
||||
<g id="glyph-0-6">
|
||||
<path d="M 9.21875 -6.140625 L 7.289062 -6.140625 C 7.671875 -6.640625 8.03125 -7.238281 8.378906 -7.960938 L 7.648438 -8.230469 C 7.328125 -7.480469 6.949219 -6.78125 6.511719 -6.140625 L 0.78125 -6.140625 L 0.78125 -3.898438 L 1.480469 -3.898438 L 1.480469 -5.449219 L 8.511719 -5.449219 L 8.511719 -3.898438 L 9.21875 -3.898438 Z M 2.308594 -4.601562 L 2.308594 -3.960938 L 6.671875 -3.960938 C 6.230469 -3.71875 5.601562 -3.421875 4.761719 -3.050781 L 4.761719 -2.21875 L 0.710938 -2.21875 L 0.710938 -1.539062 L 4.761719 -1.539062 L 4.761719 -0.101562 C 4.761719 0.179688 4.628906 0.320312 4.390625 0.320312 C 3.960938 0.320312 3.558594 0.308594 3.199219 0.300781 L 3.371094 0.960938 L 4.640625 0.960938 C 5.199219 0.960938 5.488281 0.691406 5.488281 0.148438 L 5.488281 -1.539062 L 9.300781 -1.539062 L 9.300781 -2.21875 L 5.488281 -2.21875 L 5.488281 -2.730469 C 6.308594 -3.128906 7.039062 -3.539062 7.699219 -3.960938 L 7.699219 -4.601562 Z M 2.199219 -8.078125 L 1.550781 -7.761719 C 1.921875 -7.300781 2.269531 -6.78125 2.609375 -6.199219 L 3.230469 -6.511719 C 2.910156 -7.078125 2.558594 -7.601562 2.199219 -8.078125 Z M 4.621094 -8.269531 L 3.96875 -7.949219 C 4.308594 -7.46875 4.628906 -6.921875 4.929688 -6.300781 L 5.570312 -6.621094 C 5.269531 -7.21875 4.949219 -7.769531 4.621094 -8.269531 Z "/>
|
||||
</g>
|
||||
<g id="glyph-0-7">
|
||||
<path d="M 2.808594 -7.140625 L 0 0 L 0.878906 0 L 1.640625 -2 L 4.921875 -2 L 5.679688 0 L 6.570312 0 L 3.761719 -7.140625 Z M 1.898438 -2.691406 L 3.269531 -6.261719 L 3.308594 -6.261719 L 4.660156 -2.691406 Z "/>
|
||||
</g>
|
||||
<g id="glyph-0-8">
|
||||
<path d="M 0.78125 -7.140625 L 0.78125 0 L 1.589844 0 L 1.589844 -7.140625 Z "/>
|
||||
</g>
|
||||
</g>
|
||||
<image id="source-8" x="0" y="0" width="0" height="0"/>
|
||||
<mask id="mask-0">
|
||||
<use xlink:href="#source-8"/>
|
||||
</mask>
|
||||
</defs>
|
||||
<g fill="rgb(100%, 100%, 100%)" fill-opacity="1">
|
||||
<use xlink:href="#glyph-0-0" x="30" y="30.599609"/>
|
||||
<use xlink:href="#glyph-0-0" x="40" y="30.599609"/>
|
||||
<use xlink:href="#glyph-0-1" x="50" y="30.599609"/>
|
||||
<use xlink:href="#glyph-0-2" x="60" y="30.599609"/>
|
||||
<use xlink:href="#glyph-0-3" x="70" y="30.599609"/>
|
||||
<use xlink:href="#glyph-0-3" x="73" y="30.599609"/>
|
||||
<use xlink:href="#glyph-0-4" x="76" y="30.599609"/>
|
||||
<use xlink:href="#glyph-0-5" x="86" y="30.599609"/>
|
||||
<use xlink:href="#glyph-0-6" x="96" y="30.599609"/>
|
||||
<use xlink:href="#glyph-0-3" x="106" y="30.599609"/>
|
||||
</g>
|
||||
<g fill="rgb(100%, 100%, 100%)" fill-opacity="1">
|
||||
<use xlink:href="#glyph-0-7" x="109" y="30.599609"/>
|
||||
<use xlink:href="#glyph-0-8" x="116" y="30.599609"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.7 KiB |
54
video-creator/assets/media/texts/a47357e721583a39.svg
Normal file
@@ -0,0 +1,54 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1024" height="1024" viewBox="0 0 1024 1024">
|
||||
<defs>
|
||||
<g>
|
||||
<g id="glyph-0-0">
|
||||
<path d="M 5.476562 -9.960938 L 5.476562 -6.390625 L 2.152344 -6.390625 L 2.152344 -2.46875 L 10.242188 -2.46875 L 10.242188 -6.390625 L 6.34375 -6.390625 L 6.34375 -7.945312 L 11.085938 -7.945312 L 11.085938 -8.777344 L 6.34375 -8.777344 L 6.34375 -9.960938 Z M 9.363281 -3.289062 L 3.03125 -3.289062 L 3.03125 -5.5625 L 9.363281 -5.5625 Z M 2.125 -1.929688 C 1.6875 -0.929688 1.160156 -0.0625 0.550781 0.660156 L 1.269531 1.1875 C 1.882812 0.402344 2.40625 -0.511719 2.871094 -1.578125 Z M 4.792969 -1.578125 L 3.972656 -1.40625 C 4.203125 -0.636719 4.398438 0.246094 4.570312 1.246094 L 5.453125 1.050781 C 5.253906 0.109375 5.035156 -0.769531 4.792969 -1.578125 Z M 7.347656 -1.699219 L 6.515625 -1.527344 C 6.882812 -0.734375 7.1875 0.183594 7.429688 1.222656 L 8.300781 1.027344 C 8.03125 0.0507812 7.710938 -0.867188 7.347656 -1.699219 Z M 9.851562 -1.945312 L 9.140625 -1.492188 C 9.886719 -0.488281 10.476562 0.414062 10.902344 1.246094 L 11.636719 0.734375 C 11.21875 -0.0234375 10.632812 -0.917969 9.851562 -1.945312 Z "/>
|
||||
</g>
|
||||
<g id="glyph-0-1">
|
||||
<path d="M 3.789062 -9.9375 L 2.980469 -9.546875 C 3.484375 -8.933594 3.949219 -8.238281 4.386719 -7.457031 L 5.183594 -7.859375 C 4.742188 -8.617188 4.277344 -9.300781 3.789062 -9.9375 Z M 8.382812 -10.046875 C 8.019531 -9.019531 7.613281 -8.152344 7.148438 -7.445312 L 1.566406 -7.445312 L 1.566406 -6.589844 L 5.660156 -6.589844 L 5.660156 -6.011719 C 5.648438 -5.414062 5.585938 -4.851562 5.476562 -4.316406 L 0.867188 -4.316406 L 0.867188 -3.460938 L 5.253906 -3.460938 C 5.085938 -2.921875 4.851562 -2.445312 4.558594 -2.003906 C 3.824219 -1.027344 2.519531 -0.230469 0.648438 0.367188 L 1.125 1.136719 C 3.007812 0.539062 4.363281 -0.304688 5.195312 -1.382812 C 5.585938 -1.90625 5.890625 -2.519531 6.125 -3.214844 C 6.785156 -1.355469 8.445312 0.121094 11.136719 1.210938 L 11.648438 0.390625 C 9.082031 -0.527344 7.503906 -1.808594 6.90625 -3.460938 L 11.355469 -3.460938 L 11.355469 -4.316406 L 6.390625 -4.316406 C 6.488281 -4.828125 6.539062 -5.402344 6.5625 -6.011719 L 6.5625 -6.589844 L 10.644531 -6.589844 L 10.644531 -7.445312 L 8.128906 -7.445312 C 8.566406 -8.140625 8.933594 -8.933594 9.226562 -9.816406 Z "/>
|
||||
</g>
|
||||
<g id="glyph-0-2">
|
||||
<path d="M 1.601562 -9.800781 L 0.964844 -9.191406 C 1.871094 -8.554688 2.589844 -7.945312 3.105469 -7.371094 L 3.726562 -7.992188 C 3.152344 -8.59375 2.445312 -9.191406 1.601562 -9.800781 Z M 1.296875 -6.917969 L 0.671875 -6.320312 C 1.527344 -5.660156 2.199219 -5.046875 2.6875 -4.484375 L 3.3125 -5.109375 C 2.773438 -5.71875 2.101562 -6.320312 1.296875 -6.917969 Z M 2.835938 -3.542969 C 2.285156 -2.078125 1.636719 -0.648438 0.90625 0.746094 L 1.785156 1.125 C 2.457031 -0.230469 3.066406 -1.6875 3.617188 -3.226562 Z M 4.203125 -7.921875 L 4.203125 -7.050781 L 7.382812 -7.050781 L 7.382812 -4.117188 L 4.546875 -4.117188 L 4.546875 -3.277344 L 7.382812 -3.277344 L 7.382812 -0.0625 L 3.898438 -0.0625 L 3.898438 0.808594 L 11.660156 0.808594 L 11.660156 -0.0625 L 8.273438 -0.0625 L 8.273438 -3.277344 L 11.097656 -3.277344 L 11.097656 -4.117188 L 8.273438 -4.117188 L 8.273438 -7.050781 L 11.429688 -7.050781 L 11.429688 -7.921875 L 7.785156 -7.921875 L 8.285156 -8.117188 C 8.105469 -8.703125 7.835938 -9.339844 7.492188 -10.046875 L 6.660156 -9.816406 C 6.980469 -9.203125 7.246094 -8.582031 7.492188 -7.921875 Z "/>
|
||||
</g>
|
||||
<g id="glyph-0-3">
|
||||
<rect x="0" y="0" width="0" height="0" mask="url(#mask-0)"/>
|
||||
</g>
|
||||
<g id="glyph-0-4">
|
||||
<path d="M 0.609375 -4.925781 L 0.609375 -4.046875 L 11.597656 -4.046875 L 11.597656 -4.925781 Z "/>
|
||||
</g>
|
||||
<g id="glyph-0-5">
|
||||
<path d="M 11.488281 -3.386719 L 10.65625 -3.667969 C 10.609375 -2.957031 10.546875 -2.480469 10.460938 -2.25 C 10.351562 -1.992188 10.121094 -1.859375 9.777344 -1.859375 L 8.117188 -1.859375 C 7.773438 -1.871094 7.601562 -2.042969 7.601562 -2.371094 L 7.601562 -5.183594 L 10.902344 -5.183594 L 10.902344 -9.203125 L 6.515625 -9.203125 L 6.515625 -8.382812 L 10.046875 -8.382812 L 10.046875 -6.027344 L 6.722656 -6.027344 L 6.722656 -2.054688 C 6.722656 -1.355469 7.066406 -1.003906 7.761719 -1.003906 L 9.9375 -1.003906 C 10.511719 -1.003906 10.890625 -1.160156 11.074219 -1.453125 C 11.269531 -1.746094 11.414062 -2.394531 11.488281 -3.386719 Z M 1.480469 -4.167969 C 1.417969 -2.394531 1.074219 -0.792969 0.464844 0.625 L 1.136719 1.222656 C 1.453125 0.511719 1.699219 -0.246094 1.894531 -1.0625 C 2.359375 -0.328125 2.910156 0.195312 3.53125 0.488281 C 4.242188 0.820312 6.039062 0.988281 8.898438 0.988281 L 11.585938 0.988281 L 11.734375 0.132812 C 11.074219 0.171875 10.058594 0.195312 8.703125 0.195312 C 6.464844 0.195312 4.960938 0.0859375 4.203125 -0.121094 L 4.203125 -2.421875 L 5.941406 -2.421875 L 5.941406 -3.226562 L 4.203125 -3.226562 L 4.203125 -4.949219 L 6.085938 -4.949219 L 6.085938 -5.757812 L 3.972656 -5.757812 L 3.972656 -7.554688 L 5.929688 -7.554688 L 5.929688 -8.359375 L 3.972656 -8.359375 L 3.972656 -10.011719 L 3.09375 -10.011719 L 3.09375 -8.359375 L 1.074219 -8.359375 L 1.074219 -7.554688 L 3.09375 -7.554688 L 3.09375 -5.757812 L 0.855469 -5.757812 L 0.855469 -4.949219 L 3.359375 -4.949219 L 3.359375 -0.539062 C 2.871094 -0.878906 2.457031 -1.394531 2.089844 -2.078125 C 2.1875 -2.714844 2.25 -3.375 2.273438 -4.070312 Z "/>
|
||||
</g>
|
||||
<g id="glyph-0-6">
|
||||
<path d="M 11.269531 -7.503906 L 8.910156 -7.503906 C 9.375 -8.117188 9.816406 -8.847656 10.242188 -9.730469 L 9.351562 -10.058594 C 8.960938 -9.140625 8.496094 -8.285156 7.957031 -7.503906 L 0.953125 -7.503906 L 0.953125 -4.765625 L 1.808594 -4.765625 L 1.808594 -6.660156 L 10.402344 -6.660156 L 10.402344 -4.765625 L 11.269531 -4.765625 Z M 2.824219 -5.621094 L 2.824219 -4.839844 L 8.152344 -4.839844 C 7.613281 -4.546875 6.84375 -4.179688 5.816406 -3.726562 L 5.816406 -2.714844 L 0.867188 -2.714844 L 0.867188 -1.882812 L 5.816406 -1.882812 L 5.816406 -0.121094 C 5.816406 0.21875 5.660156 0.390625 5.367188 0.390625 C 4.839844 0.390625 4.351562 0.378906 3.910156 0.367188 L 4.117188 1.171875 L 5.671875 1.171875 C 6.355469 1.171875 6.710938 0.84375 6.710938 0.183594 L 6.710938 -1.882812 L 11.367188 -1.882812 L 11.367188 -2.714844 L 6.710938 -2.714844 L 6.710938 -3.335938 C 7.710938 -3.824219 8.605469 -4.328125 9.410156 -4.839844 L 9.410156 -5.621094 Z M 2.6875 -9.875 L 1.894531 -9.484375 C 2.347656 -8.921875 2.773438 -8.285156 3.191406 -7.578125 L 3.949219 -7.957031 C 3.558594 -8.652344 3.128906 -9.289062 2.6875 -9.875 Z M 5.648438 -10.109375 L 4.851562 -9.71875 C 5.269531 -9.128906 5.660156 -8.457031 6.027344 -7.699219 L 6.808594 -8.089844 C 6.441406 -8.824219 6.050781 -9.496094 5.648438 -10.109375 Z "/>
|
||||
</g>
|
||||
<g id="glyph-0-7">
|
||||
<path d="M 3.433594 -8.726562 L 0 0 L 1.074219 0 L 2.003906 -2.445312 L 6.011719 -2.445312 L 6.941406 0 L 8.03125 0 L 4.597656 -8.726562 Z M 2.324219 -3.289062 L 3.996094 -7.652344 L 4.046875 -7.652344 L 5.695312 -3.289062 Z "/>
|
||||
</g>
|
||||
<g id="glyph-0-8">
|
||||
<path d="M 0.953125 -8.726562 L 0.953125 0 L 1.945312 0 L 1.945312 -8.726562 Z "/>
|
||||
</g>
|
||||
</g>
|
||||
<image id="source-8" x="0" y="0" width="0" height="0"/>
|
||||
<mask id="mask-0">
|
||||
<use xlink:href="#source-8"/>
|
||||
</mask>
|
||||
</defs>
|
||||
<g fill="rgb(100%, 100%, 100%)" fill-opacity="1">
|
||||
<use xlink:href="#glyph-0-0" x="30" y="32.956055"/>
|
||||
<use xlink:href="#glyph-0-0" x="42" y="32.956055"/>
|
||||
<use xlink:href="#glyph-0-1" x="54" y="32.956055"/>
|
||||
<use xlink:href="#glyph-0-2" x="66" y="32.956055"/>
|
||||
<use xlink:href="#glyph-0-3" x="78" y="32.956055"/>
|
||||
<use xlink:href="#glyph-0-3" x="82" y="32.956055"/>
|
||||
<use xlink:href="#glyph-0-4" x="86" y="32.956055"/>
|
||||
<use xlink:href="#glyph-0-5" x="98" y="32.956055"/>
|
||||
<use xlink:href="#glyph-0-6" x="110" y="32.956055"/>
|
||||
<use xlink:href="#glyph-0-3" x="122" y="32.956055"/>
|
||||
</g>
|
||||
<g fill="rgb(100%, 100%, 100%)" fill-opacity="1">
|
||||
<use xlink:href="#glyph-0-7" x="126" y="32.956055"/>
|
||||
<use xlink:href="#glyph-0-8" x="134" y="32.956055"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.8 KiB |
BIN
video-creator/assets/outro.mp4
Normal file
BIN
video-creator/assets/outro_1x1.mp4
Normal file
BIN
video-creator/assets/outro_21x9.mp4
Normal file
BIN
video-creator/assets/outro_2x3.mp4
Normal file
BIN
video-creator/assets/outro_3x2.mp4
Normal file
BIN
video-creator/assets/outro_3x4.mp4
Normal file
BIN
video-creator/assets/outro_4x3.mp4
Normal file
BIN
video-creator/assets/outro_4x5.mp4
Normal file
BIN
video-creator/assets/outro_5x4.mp4
Normal file
BIN
video-creator/assets/outro_9x16.mp4
Normal file
260
video-creator/assets/outro_generator.py
Normal file
@@ -0,0 +1,260 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
生成通用片尾动画
|
||||
支持三种尺寸:16:9、3:4、9:16
|
||||
|
||||
用法:
|
||||
# 16:9 横版(默认)
|
||||
manim -qh --format=mp4 --fps=30 -o outro.mp4 outro_generator.py OutroAnimation
|
||||
|
||||
# 3:4 竖版
|
||||
manim -qh --format=mp4 --fps=30 -o outro_3x4.mp4 outro_generator.py OutroAnimation3x4
|
||||
|
||||
# 9:16 竖版(手机全屏)
|
||||
manim -qh --format=mp4 --fps=30 -o outro_9x16.mp4 outro_generator.py OutroAnimation9x16
|
||||
|
||||
加语音(每种尺寸都要加):
|
||||
edge-tts --text "点关注,不迷路!" --voice zh-CN-YunxiNeural --rate="+10%" --write-media outro_voice.mp3
|
||||
ffmpeg -y -i outro.mp4 -i outro_voice.mp3 -filter_complex "[1:a]adelay=1000|1000,apad=whole_dur=5.2[aout]" -map 0:v -map "[aout]" -c:v copy -c:a aac outro_with_voice.mp4
|
||||
mv outro_with_voice.mp4 outro.mp4
|
||||
"""
|
||||
from manim import *
|
||||
import os
|
||||
|
||||
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
LOGO_PATH = os.path.join(SCRIPT_DIR, "logo.jpg")
|
||||
|
||||
|
||||
class OutroAnimation(Scene):
|
||||
"""16:9 横版片尾"""
|
||||
def construct(self):
|
||||
self.camera.background_color = "#1a1a2e"
|
||||
|
||||
qr_code = ImageMobject(LOGO_PATH)
|
||||
qr_code.scale(1.8)
|
||||
qr_code.shift(UP * 0.8)
|
||||
|
||||
title = Text("点点关注 一起学 AI", font="PingFang SC", font_size=48, color=WHITE)
|
||||
title.next_to(qr_code, DOWN, buff=0.6)
|
||||
|
||||
self.play(GrowFromCenter(qr_code), run_time=0.8)
|
||||
self.play(Write(title), run_time=1.0)
|
||||
self.play(qr_code.animate.scale(1.05), rate_func=there_and_back, run_time=0.4)
|
||||
self.wait(3)
|
||||
|
||||
|
||||
class OutroAnimation3x4(Scene):
|
||||
"""3:4 竖版片尾"""
|
||||
def construct(self):
|
||||
config.pixel_width = 1080
|
||||
config.pixel_height = 1440
|
||||
config.frame_width = 8
|
||||
config.frame_height = 10.67
|
||||
|
||||
self.camera.background_color = "#1a1a2e"
|
||||
|
||||
qr_code = ImageMobject(LOGO_PATH)
|
||||
qr_code.scale(2.2)
|
||||
qr_code.shift(UP * 1.5)
|
||||
|
||||
title = Text("点点关注 一起学 AI", font="PingFang SC", font_size=42, color=WHITE)
|
||||
title.next_to(qr_code, DOWN, buff=0.8)
|
||||
|
||||
self.play(GrowFromCenter(qr_code), run_time=0.8)
|
||||
self.play(Write(title), run_time=1.0)
|
||||
self.play(qr_code.animate.scale(1.05), rate_func=there_and_back, run_time=0.4)
|
||||
self.wait(3)
|
||||
|
||||
|
||||
class OutroAnimation9x16(Scene):
|
||||
"""9:16 竖版片尾(手机全屏)"""
|
||||
def construct(self):
|
||||
config.pixel_width = 1080
|
||||
config.pixel_height = 1920
|
||||
config.frame_width = 8
|
||||
config.frame_height = 14.22
|
||||
|
||||
self.camera.background_color = "#1a1a2e"
|
||||
|
||||
qr_code = ImageMobject(LOGO_PATH)
|
||||
qr_code.scale(2.5)
|
||||
qr_code.shift(UP * 2)
|
||||
|
||||
title = Text("点点关注 一起学 AI", font="PingFang SC", font_size=40, color=WHITE)
|
||||
title.next_to(qr_code, DOWN, buff=1.0)
|
||||
|
||||
self.play(GrowFromCenter(qr_code), run_time=0.8)
|
||||
self.play(Write(title), run_time=1.0)
|
||||
self.play(qr_code.animate.scale(1.05), rate_func=there_and_back, run_time=0.4)
|
||||
self.wait(3)
|
||||
|
||||
|
||||
class OutroAnimation1x1(Scene):
|
||||
"""1:1 正方形片尾"""
|
||||
def construct(self):
|
||||
config.pixel_width = 1024
|
||||
config.pixel_height = 1024
|
||||
config.frame_width = 8
|
||||
config.frame_height = 8
|
||||
|
||||
self.camera.background_color = "#1a1a2e"
|
||||
|
||||
qr_code = ImageMobject(LOGO_PATH)
|
||||
qr_code.scale(2.0)
|
||||
qr_code.shift(UP * 0.5)
|
||||
|
||||
title = Text("点点关注 一起学 AI", font="PingFang SC", font_size=44, color=WHITE)
|
||||
title.next_to(qr_code, DOWN, buff=0.6)
|
||||
|
||||
self.play(GrowFromCenter(qr_code), run_time=0.8)
|
||||
self.play(Write(title), run_time=1.0)
|
||||
self.play(qr_code.animate.scale(1.05), rate_func=there_and_back, run_time=0.4)
|
||||
self.wait(3)
|
||||
|
||||
|
||||
class OutroAnimation2x3(Scene):
|
||||
"""2:3 竖版片尾"""
|
||||
def construct(self):
|
||||
config.pixel_width = 832
|
||||
config.pixel_height = 1248
|
||||
config.frame_width = 8
|
||||
config.frame_height = 12
|
||||
|
||||
self.camera.background_color = "#1a1a2e"
|
||||
|
||||
qr_code = ImageMobject(LOGO_PATH)
|
||||
qr_code.scale(2.0)
|
||||
qr_code.shift(UP * 1.2)
|
||||
|
||||
title = Text("点点关注 一起学 AI", font="PingFang SC", font_size=38, color=WHITE)
|
||||
title.next_to(qr_code, DOWN, buff=0.8)
|
||||
|
||||
self.play(GrowFromCenter(qr_code), run_time=0.8)
|
||||
self.play(Write(title), run_time=1.0)
|
||||
self.play(qr_code.animate.scale(1.05), rate_func=there_and_back, run_time=0.4)
|
||||
self.wait(3)
|
||||
|
||||
|
||||
class OutroAnimation3x2(Scene):
|
||||
"""3:2 横版片尾"""
|
||||
def construct(self):
|
||||
config.pixel_width = 1248
|
||||
config.pixel_height = 832
|
||||
config.frame_width = 12
|
||||
config.frame_height = 8
|
||||
|
||||
self.camera.background_color = "#1a1a2e"
|
||||
|
||||
qr_code = ImageMobject(LOGO_PATH)
|
||||
qr_code.scale(1.6)
|
||||
qr_code.shift(UP * 0.6)
|
||||
|
||||
title = Text("点点关注 一起学 AI", font="PingFang SC", font_size=46, color=WHITE)
|
||||
title.next_to(qr_code, DOWN, buff=0.5)
|
||||
|
||||
self.play(GrowFromCenter(qr_code), run_time=0.8)
|
||||
self.play(Write(title), run_time=1.0)
|
||||
self.play(qr_code.animate.scale(1.05), rate_func=there_and_back, run_time=0.4)
|
||||
self.wait(3)
|
||||
|
||||
|
||||
class OutroAnimation4x3(Scene):
|
||||
"""4:3 横版片尾"""
|
||||
def construct(self):
|
||||
config.pixel_width = 1440
|
||||
config.pixel_height = 1080
|
||||
config.frame_width = 10.67
|
||||
config.frame_height = 8
|
||||
|
||||
self.camera.background_color = "#1a1a2e"
|
||||
|
||||
qr_code = ImageMobject(LOGO_PATH)
|
||||
qr_code.scale(1.8)
|
||||
qr_code.shift(UP * 0.7)
|
||||
|
||||
title = Text("点点关注 一起学 AI", font="PingFang SC", font_size=46, color=WHITE)
|
||||
title.next_to(qr_code, DOWN, buff=0.6)
|
||||
|
||||
self.play(GrowFromCenter(qr_code), run_time=0.8)
|
||||
self.play(Write(title), run_time=1.0)
|
||||
self.play(qr_code.animate.scale(1.05), rate_func=there_and_back, run_time=0.4)
|
||||
self.wait(3)
|
||||
|
||||
|
||||
class OutroAnimation4x5(Scene):
|
||||
"""4:5 竖版片尾(Instagram)"""
|
||||
def construct(self):
|
||||
config.pixel_width = 864
|
||||
config.pixel_height = 1080
|
||||
config.frame_width = 8
|
||||
config.frame_height = 10
|
||||
|
||||
self.camera.background_color = "#1a1a2e"
|
||||
|
||||
qr_code = ImageMobject(LOGO_PATH)
|
||||
qr_code.scale(2.0)
|
||||
qr_code.shift(UP * 1.0)
|
||||
|
||||
title = Text("点点关注 一起学 AI", font="PingFang SC", font_size=36, color=WHITE)
|
||||
title.next_to(qr_code, DOWN, buff=0.7)
|
||||
|
||||
self.play(GrowFromCenter(qr_code), run_time=0.8)
|
||||
self.play(Write(title), run_time=1.0)
|
||||
self.play(qr_code.animate.scale(1.05), rate_func=there_and_back, run_time=0.4)
|
||||
self.wait(3)
|
||||
|
||||
|
||||
class OutroAnimation5x4(Scene):
|
||||
"""5:4 横版片尾"""
|
||||
def construct(self):
|
||||
config.pixel_width = 1080
|
||||
config.pixel_height = 864
|
||||
config.frame_width = 10
|
||||
config.frame_height = 8
|
||||
|
||||
self.camera.background_color = "#1a1a2e"
|
||||
|
||||
qr_code = ImageMobject(LOGO_PATH)
|
||||
qr_code.scale(1.7)
|
||||
qr_code.shift(UP * 0.6)
|
||||
|
||||
title = Text("点点关注 一起学 AI", font="PingFang SC", font_size=44, color=WHITE)
|
||||
title.next_to(qr_code, DOWN, buff=0.5)
|
||||
|
||||
self.play(GrowFromCenter(qr_code), run_time=0.8)
|
||||
self.play(Write(title), run_time=1.0)
|
||||
self.play(qr_code.animate.scale(1.05), rate_func=there_and_back, run_time=0.4)
|
||||
self.wait(3)
|
||||
|
||||
|
||||
class OutroAnimation21x9(Scene):
|
||||
"""21:9 超宽屏片尾"""
|
||||
def construct(self):
|
||||
config.pixel_width = 1536
|
||||
config.pixel_height = 672
|
||||
config.frame_width = 18.29
|
||||
config.frame_height = 8
|
||||
|
||||
self.camera.background_color = "#1a1a2e"
|
||||
|
||||
qr_code = ImageMobject(LOGO_PATH)
|
||||
qr_code.scale(1.3)
|
||||
qr_code.shift(LEFT * 4)
|
||||
|
||||
title = Text("点点关注 一起学 AI", font="PingFang SC", font_size=52, color=WHITE)
|
||||
title.shift(RIGHT * 2)
|
||||
|
||||
self.play(GrowFromCenter(qr_code), run_time=0.8)
|
||||
self.play(Write(title), run_time=1.0)
|
||||
self.play(qr_code.animate.scale(1.05), rate_func=there_and_back, run_time=0.4)
|
||||
self.wait(3)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("生成片尾动画...")
|
||||
print("\n16:9 横版:")
|
||||
print(" manim -qh --format=mp4 --fps=30 -o outro.mp4 outro_generator.py OutroAnimation")
|
||||
print("\n3:4 竖版:")
|
||||
print(" manim -qh --format=mp4 --fps=30 -o outro_3x4.mp4 outro_generator.py OutroAnimation3x4")
|
||||
print("\n9:16 竖版:")
|
||||
print(" manim -qh --format=mp4 --fps=30 -o outro_9x16.mp4 outro_generator.py OutroAnimation9x16")
|
||||
BIN
video-creator/assets/outro_voice.mp3
Normal file
72
video-creator/references/edge_tts_voices.md
Normal file
@@ -0,0 +1,72 @@
|
||||
# Edge-TTS 中文音色参考
|
||||
|
||||
## 常用音色推荐
|
||||
|
||||
| 音色 ID | 性别 | 风格 | 适用场景 |
|
||||
|---------|------|------|----------|
|
||||
| zh-CN-YunxiNeural | 男 | 新闻播报风 | 资讯类、技术分享 |
|
||||
| zh-CN-YunyangNeural | 男 | 温和亲切 | 教程、讲解 |
|
||||
| zh-CN-XiaoxiaoNeural | 女 | 活泼自然 | 日常分享、生活类 |
|
||||
| zh-CN-XiaoyiNeural | 女 | 温柔知性 | 文艺、情感类 |
|
||||
| zh-CN-YunjianNeural | 男 | 浑厚大气 | 宣传片、正式场合 |
|
||||
| zh-CN-XiaochenNeural | 女 | 甜美可爱 | 儿童内容、轻松话题 |
|
||||
|
||||
## 全部中文音色列表
|
||||
|
||||
### 普通话(大陆)
|
||||
- zh-CN-XiaoxiaoNeural (女)
|
||||
- zh-CN-XiaoyiNeural (女)
|
||||
- zh-CN-YunjianNeural (男)
|
||||
- zh-CN-YunxiNeural (男)
|
||||
- zh-CN-YunyangNeural (男)
|
||||
- zh-CN-XiaochenNeural (女)
|
||||
- zh-CN-XiaohanNeural (女)
|
||||
- zh-CN-XiaomengNeural (女)
|
||||
- zh-CN-XiaomoNeural (女)
|
||||
- zh-CN-XiaoqiuNeural (女)
|
||||
- zh-CN-XiaoruiNeural (女)
|
||||
- zh-CN-XiaoshuangNeural (女)
|
||||
- zh-CN-XiaoxuanNeural (女)
|
||||
- zh-CN-XiaoyanNeural (女)
|
||||
- zh-CN-XiaoyouNeural (女)
|
||||
- zh-CN-XiaozhenNeural (女)
|
||||
- zh-CN-YunfengNeural (男)
|
||||
- zh-CN-YunhaoNeural (男)
|
||||
- zh-CN-YunzeNeural (男)
|
||||
|
||||
### 粤语(香港)
|
||||
- zh-HK-HiuGaaiNeural (女)
|
||||
- zh-HK-HiuMaanNeural (女)
|
||||
- zh-HK-WanLungNeural (男)
|
||||
|
||||
### 台湾
|
||||
- zh-TW-HsiaoChenNeural (女)
|
||||
- zh-TW-HsiaoYuNeural (女)
|
||||
- zh-TW-YunJheNeural (男)
|
||||
|
||||
## 语速和音调调整
|
||||
|
||||
### 语速 (rate)
|
||||
- 加快: `+10%`, `+20%`, `+50%`
|
||||
- 减慢: `-10%`, `-20%`, `-30%`
|
||||
- 默认: `+0%`
|
||||
|
||||
### 音调 (pitch)
|
||||
- 升高: `+5Hz`, `+10Hz`, `+20Hz`
|
||||
- 降低: `-5Hz`, `-10Hz`, `-20Hz`
|
||||
- 默认: `+0Hz`
|
||||
|
||||
### 配置示例
|
||||
|
||||
```yaml
|
||||
voice:
|
||||
name: "zh-CN-YunxiNeural"
|
||||
rate: "+10%" # 稍快一点
|
||||
pitch: "-5Hz" # 稍低沉一点
|
||||
```
|
||||
|
||||
## 查看所有可用音色
|
||||
|
||||
```bash
|
||||
python scripts/tts_generator.py --list-voices
|
||||
```
|
||||
221
video-creator/scripts/scene_splitter.py
Normal file
@@ -0,0 +1,221 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
场景拆分器 - 将口播文本拆分成细镜头
|
||||
基于时间戳对齐图片和字幕
|
||||
"""
|
||||
|
||||
import json
|
||||
import re
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
from typing import List, Dict
|
||||
|
||||
|
||||
def split_by_sentence_timestamps(timestamps: List[Dict]) -> List[Dict]:
|
||||
"""
|
||||
直接使用 TTS 的 SentenceBoundary 时间戳作为镜头分割
|
||||
|
||||
Args:
|
||||
timestamps: TTS 输出的时间戳(包含 sentence 类型)
|
||||
|
||||
Returns:
|
||||
每个镜头的信息:text, start, end, duration
|
||||
"""
|
||||
shots = []
|
||||
for ts in timestamps:
|
||||
if ts.get("type") == "sentence":
|
||||
shots.append({
|
||||
"text": ts["text"],
|
||||
"start": ts["start"],
|
||||
"end": ts["end"],
|
||||
"duration": round(ts["end"] - ts["start"], 2)
|
||||
})
|
||||
|
||||
if not shots and timestamps:
|
||||
total_start = timestamps[0]["start"]
|
||||
total_end = timestamps[-1]["end"]
|
||||
full_text = "".join(ts["text"] for ts in timestamps)
|
||||
shots.append({
|
||||
"text": full_text,
|
||||
"start": total_start,
|
||||
"end": total_end,
|
||||
"duration": round(total_end - total_start, 2)
|
||||
})
|
||||
|
||||
return shots
|
||||
|
||||
|
||||
def split_long_shots(shots: List[Dict], max_duration: float = 6.0) -> List[Dict]:
|
||||
"""
|
||||
将过长的镜头按标点符号进一步拆分
|
||||
|
||||
Args:
|
||||
shots: 镜头列表
|
||||
max_duration: 最大镜头时长
|
||||
|
||||
Returns:
|
||||
拆分后的镜头列表
|
||||
"""
|
||||
result = []
|
||||
|
||||
for shot in shots:
|
||||
if shot["duration"] <= max_duration:
|
||||
result.append(shot)
|
||||
continue
|
||||
|
||||
text = shot["text"]
|
||||
splits = re.split(r'([,。!?,!?])', text)
|
||||
|
||||
sub_texts = []
|
||||
current = ""
|
||||
for i, part in enumerate(splits):
|
||||
current += part
|
||||
if i % 2 == 1 and current.strip():
|
||||
sub_texts.append(current.strip())
|
||||
current = ""
|
||||
if current.strip():
|
||||
sub_texts.append(current.strip())
|
||||
|
||||
if len(sub_texts) <= 1:
|
||||
result.append(shot)
|
||||
continue
|
||||
|
||||
total_chars = sum(len(t) for t in sub_texts)
|
||||
current_time = shot["start"]
|
||||
|
||||
for sub_text in sub_texts:
|
||||
ratio = len(sub_text) / total_chars
|
||||
sub_duration = shot["duration"] * ratio
|
||||
result.append({
|
||||
"text": sub_text,
|
||||
"start": round(current_time, 2),
|
||||
"end": round(current_time + sub_duration, 2),
|
||||
"duration": round(sub_duration, 2)
|
||||
})
|
||||
current_time += sub_duration
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def merge_short_shots(shots: List[Dict], min_duration: float = 2.5) -> List[Dict]:
|
||||
"""合并过短的镜头"""
|
||||
if not shots:
|
||||
return shots
|
||||
|
||||
merged = []
|
||||
current = shots[0].copy()
|
||||
|
||||
for shot in shots[1:]:
|
||||
if current["duration"] < min_duration:
|
||||
current["text"] += shot["text"]
|
||||
current["end"] = shot["end"]
|
||||
current["duration"] = round(current["end"] - current["start"], 2)
|
||||
else:
|
||||
merged.append(current)
|
||||
current = shot.copy()
|
||||
|
||||
merged.append(current)
|
||||
return merged
|
||||
|
||||
|
||||
def generate_shot_prompts(shots: List[Dict], style: str, context: str = "") -> List[Dict]:
|
||||
"""
|
||||
为每个镜头生成图片提示词
|
||||
|
||||
Args:
|
||||
shots: 镜头列表
|
||||
style: 画风描述
|
||||
context: 上下文(如角色描述)
|
||||
|
||||
Returns:
|
||||
带有图片提示词的镜头列表
|
||||
"""
|
||||
for i, shot in enumerate(shots):
|
||||
shot["image_prompt"] = f"{style},{context},画面:{shot['text']}。禁止出现任何文字"
|
||||
shot["index"] = i + 1
|
||||
|
||||
return shots
|
||||
|
||||
|
||||
def generate_srt(shots: List[Dict], output_path: str):
|
||||
"""生成 SRT 字幕文件"""
|
||||
def format_time(seconds: float) -> str:
|
||||
hours = int(seconds // 3600)
|
||||
minutes = int((seconds % 3600) // 60)
|
||||
secs = int(seconds % 60)
|
||||
millis = int((seconds % 1) * 1000)
|
||||
return f"{hours:02d}:{minutes:02d}:{secs:02d},{millis:03d}"
|
||||
|
||||
with open(output_path, "w", encoding="utf-8") as f:
|
||||
for i, shot in enumerate(shots, 1):
|
||||
f.write(f"{i}\n")
|
||||
f.write(f"{format_time(shot['start'])} --> {format_time(shot['end'])}\n")
|
||||
f.write(f"{shot['text']}\n\n")
|
||||
|
||||
print(f" ✓ 字幕: {output_path}")
|
||||
|
||||
|
||||
def process_scene(text: str, timestamps_path: str, style: str, context: str = "", output_dir: str = ".") -> Dict:
|
||||
"""
|
||||
处理单个场景,输出镜头配置
|
||||
|
||||
Args:
|
||||
text: 场景口播文本
|
||||
timestamps_path: TTS 时间戳 JSON 文件
|
||||
style: 画风
|
||||
context: 上下文
|
||||
output_dir: 输出目录
|
||||
|
||||
Returns:
|
||||
场景配置字典
|
||||
"""
|
||||
with open(timestamps_path, "r", encoding="utf-8") as f:
|
||||
timestamps = json.load(f)
|
||||
|
||||
shots = split_by_sentence_timestamps(timestamps)
|
||||
|
||||
shots = split_long_shots(shots, max_duration=6.0)
|
||||
|
||||
shots = merge_short_shots(shots, min_duration=2.5)
|
||||
|
||||
shots = generate_shot_prompts(shots, style, context)
|
||||
|
||||
output_path = Path(output_dir)
|
||||
output_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
srt_path = output_path / "subtitles.srt"
|
||||
generate_srt(shots, str(srt_path))
|
||||
|
||||
config_path = output_path / "shots.json"
|
||||
with open(config_path, "w", encoding="utf-8") as f:
|
||||
json.dump(shots, f, ensure_ascii=False, indent=2)
|
||||
print(f" ✓ 镜头配置: {config_path}")
|
||||
|
||||
return {"shots": shots, "srt_path": str(srt_path)}
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='场景拆分器')
|
||||
parser.add_argument('--text', type=str, required=True, help='口播文本')
|
||||
parser.add_argument('--timestamps', type=str, required=True, help='TTS时间戳JSON文件')
|
||||
parser.add_argument('--style', type=str, default='', help='画风描述')
|
||||
parser.add_argument('--context', type=str, default='', help='上下文(角色等)')
|
||||
parser.add_argument('--output-dir', type=str, default='.', help='输出目录')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
result = process_scene(
|
||||
text=args.text,
|
||||
timestamps_path=args.timestamps,
|
||||
style=args.style,
|
||||
context=args.context,
|
||||
output_dir=args.output_dir
|
||||
)
|
||||
|
||||
print(f"\n拆分完成,共 {len(result['shots'])} 个镜头:")
|
||||
for shot in result["shots"]:
|
||||
print(f" [{shot['duration']:.1f}s] {shot['text']}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
123
video-creator/scripts/tts_generator.py
Normal file
@@ -0,0 +1,123 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
TTS 语音生成器 - 使用 edge-tts
|
||||
支持时间戳输出,用于字幕同步和镜头切换
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import argparse
|
||||
import os
|
||||
import json
|
||||
import yaml
|
||||
import edge_tts
|
||||
|
||||
|
||||
async def generate_tts(text: str, voice: str, output_path: str, rate: str = "+0%", pitch: str = "+0Hz", with_timestamps: bool = False):
|
||||
"""生成单条语音,可选输出时间戳"""
|
||||
communicate = edge_tts.Communicate(text, voice, rate=rate, pitch=pitch)
|
||||
|
||||
if with_timestamps:
|
||||
timestamps = []
|
||||
audio_chunks = []
|
||||
|
||||
async for chunk in communicate.stream():
|
||||
chunk_type = chunk.get("type", "")
|
||||
if chunk_type == "audio":
|
||||
audio_chunks.append(chunk.get("data", b""))
|
||||
elif chunk_type == "WordBoundary":
|
||||
timestamps.append({
|
||||
"text": chunk.get("text", ""),
|
||||
"start": chunk.get("offset", 0) / 10000000,
|
||||
"end": (chunk.get("offset", 0) + chunk.get("duration", 0)) / 10000000
|
||||
})
|
||||
elif chunk_type == "SentenceBoundary":
|
||||
timestamps.append({
|
||||
"text": chunk.get("text", ""),
|
||||
"start": chunk.get("offset", 0) / 10000000,
|
||||
"end": (chunk.get("offset", 0) + chunk.get("duration", 0)) / 10000000,
|
||||
"type": "sentence"
|
||||
})
|
||||
|
||||
with open(output_path, "wb") as f:
|
||||
for data in audio_chunks:
|
||||
f.write(data)
|
||||
|
||||
ts_path = output_path.rsplit(".", 1)[0] + ".json"
|
||||
with open(ts_path, "w", encoding="utf-8") as f:
|
||||
json.dump(timestamps, f, ensure_ascii=False, indent=2)
|
||||
|
||||
print(f" ✓ 生成: {output_path} + 时间戳")
|
||||
return timestamps
|
||||
else:
|
||||
await communicate.save(output_path)
|
||||
print(f" ✓ 生成: {output_path}")
|
||||
return None
|
||||
|
||||
|
||||
async def generate_batch(config_path: str, output_dir: str):
|
||||
"""批量生成语音"""
|
||||
with open(config_path, 'r', encoding='utf-8') as f:
|
||||
config = yaml.safe_load(f)
|
||||
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
|
||||
voice_config = config.get('voice', {})
|
||||
voice_name = voice_config.get('name', 'zh-CN-YunxiNeural')
|
||||
rate = voice_config.get('rate', '+0%')
|
||||
pitch = voice_config.get('pitch', '+0Hz')
|
||||
|
||||
scenes = config.get('scenes', [])
|
||||
tasks = []
|
||||
|
||||
for i, scene in enumerate(scenes):
|
||||
text = scene.get('text', '')
|
||||
if not text:
|
||||
continue
|
||||
output_path = os.path.join(output_dir, f"{i:03d}.mp3")
|
||||
tasks.append(generate_tts(text, voice_name, output_path, rate, pitch))
|
||||
|
||||
print(f"开始生成 {len(tasks)} 条语音...")
|
||||
await asyncio.gather(*tasks)
|
||||
print(f"✓ 完成!语音文件保存在: {output_dir}")
|
||||
|
||||
|
||||
async def list_voices():
|
||||
"""列出所有可用音色"""
|
||||
voices = await edge_tts.list_voices()
|
||||
zh_voices = [v for v in voices if v['Locale'].startswith('zh')]
|
||||
|
||||
print("\n中文可用音色:")
|
||||
print("-" * 60)
|
||||
for v in zh_voices:
|
||||
gender = "♂" if v['Gender'] == 'Male' else "♀"
|
||||
print(f"{gender} {v['ShortName']:<30} {v['Locale']}")
|
||||
print("-" * 60)
|
||||
print(f"共 {len(zh_voices)} 个中文音色")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='Edge-TTS 语音生成器')
|
||||
parser.add_argument('--text', type=str, help='要转换的文本')
|
||||
parser.add_argument('--voice', type=str, default='zh-CN-YunxiNeural', help='音色名称')
|
||||
parser.add_argument('--rate', type=str, default='+0%', help='语速调整')
|
||||
parser.add_argument('--pitch', type=str, default='+0Hz', help='音调调整')
|
||||
parser.add_argument('--output', type=str, help='输出文件路径')
|
||||
parser.add_argument('--timestamps', action='store_true', help='输出时间戳JSON文件')
|
||||
parser.add_argument('--config', type=str, help='配置文件路径(批量生成)')
|
||||
parser.add_argument('--output-dir', type=str, default='temp/audio', help='批量输出目录')
|
||||
parser.add_argument('--list-voices', action='store_true', help='列出可用音色')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.list_voices:
|
||||
asyncio.run(list_voices())
|
||||
elif args.config:
|
||||
asyncio.run(generate_batch(args.config, args.output_dir))
|
||||
elif args.text and args.output:
|
||||
asyncio.run(generate_tts(args.text, args.voice, args.output, args.rate, args.pitch, args.timestamps))
|
||||
else:
|
||||
parser.print_help()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
530
video-creator/scripts/video_maker.py
Normal file
@@ -0,0 +1,530 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
视频生成器 - 图片+音频合成视频
|
||||
支持:淡入淡出转场、自动拼接片尾、添加BGM
|
||||
|
||||
用法:
|
||||
python video_maker.py config.yaml
|
||||
python video_maker.py config.yaml --no-outro # 不加片尾
|
||||
python video_maker.py config.yaml --no-bgm # 不加BGM
|
||||
"""
|
||||
import argparse
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import yaml
|
||||
from pathlib import Path
|
||||
|
||||
SCRIPT_DIR = Path(__file__).parent
|
||||
SKILL_DIR = SCRIPT_DIR.parent
|
||||
ASSETS_DIR = SKILL_DIR / "assets"
|
||||
BGM_DEFAULT = ASSETS_DIR / "bgm_technology.mp3"
|
||||
BGM_EPIC = ASSETS_DIR / "bgm_epic.mp3"
|
||||
|
||||
VALID_ASPECT_RATIOS = [
|
||||
"1:1", "2:3", "3:2", "3:4", "4:3", "4:5", "5:4", "9:16", "16:9", "21:9"
|
||||
]
|
||||
|
||||
RATIO_TO_SIZE = {
|
||||
"1:1": (1024, 1024),
|
||||
"2:3": (832, 1248),
|
||||
"3:2": (1248, 832),
|
||||
"3:4": (1080, 1440),
|
||||
"4:3": (1440, 1080),
|
||||
"4:5": (864, 1080),
|
||||
"5:4": (1080, 864),
|
||||
"9:16": (1080, 1920),
|
||||
"16:9": (1920, 1080),
|
||||
"21:9": (1536, 672),
|
||||
}
|
||||
|
||||
def get_outro_path(ratio):
|
||||
"""根据比例获取片尾路径,优先精确匹配,否则按方向匹配,最后兜底"""
|
||||
ratio_file = ASSETS_DIR / f"outro_{ratio.replace(':', 'x')}.mp4"
|
||||
if ratio_file.exists():
|
||||
return ratio_file
|
||||
|
||||
w, h = RATIO_TO_SIZE.get(ratio, (1920, 1080))
|
||||
if h > w:
|
||||
candidates = ["outro_9x16.mp4", "outro_3x4.mp4"]
|
||||
elif w > h:
|
||||
candidates = ["outro.mp4", "outro_3x4.mp4"]
|
||||
else:
|
||||
candidates = ["outro_1x1.mp4", "outro.mp4"]
|
||||
|
||||
for name in candidates:
|
||||
fallback = ASSETS_DIR / name
|
||||
if fallback.exists():
|
||||
return fallback
|
||||
|
||||
return ASSETS_DIR / "outro.mp4"
|
||||
|
||||
|
||||
def run_cmd(cmd, desc=""):
|
||||
"""执行命令并返回结果"""
|
||||
if desc:
|
||||
print(f" {desc}...")
|
||||
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||
if result.returncode != 0:
|
||||
print(f"错误: {result.stderr[-1000:]}")
|
||||
sys.exit(1)
|
||||
return result
|
||||
|
||||
|
||||
def get_duration(file_path):
|
||||
"""获取音视频时长"""
|
||||
result = subprocess.run([
|
||||
'ffprobe', '-v', 'error', '-show_entries', 'format=duration',
|
||||
'-of', 'csv=p=0', str(file_path)
|
||||
], capture_output=True, text=True)
|
||||
return float(result.stdout.strip())
|
||||
|
||||
|
||||
def generate_video_with_transitions(images, durations, output_path, fade_duration=0.5, ratio="16:9"):
|
||||
"""生成带转场的视频"""
|
||||
print(f"\n[1/4] 生成主视频 ({len(images)}张图片, {fade_duration}秒转场)")
|
||||
|
||||
width, height = RATIO_TO_SIZE.get(ratio, (1920, 1080))
|
||||
|
||||
display_durations = []
|
||||
for i, dur in enumerate(durations):
|
||||
if i < len(durations) - 1:
|
||||
display_durations.append(dur + fade_duration)
|
||||
else:
|
||||
display_durations.append(dur)
|
||||
|
||||
inputs = []
|
||||
for img, dur in zip(images, display_durations):
|
||||
inputs.extend(['-loop', '1', '-t', str(dur), '-i', str(img)])
|
||||
|
||||
filter_parts = []
|
||||
for i in range(len(images)):
|
||||
filter_parts.append(
|
||||
f"[{i}:v]scale={width}:{height}:force_original_aspect_ratio=decrease,"
|
||||
f"pad={width}:{height}:(ow-iw)/2:(oh-ih)/2,setsar=1,fps=30[v{i}];"
|
||||
)
|
||||
|
||||
offset = 0
|
||||
for i in range(len(images) - 1):
|
||||
if i == 0:
|
||||
offset = display_durations[0] - fade_duration
|
||||
filter_parts.append(
|
||||
f"[v0][v1]xfade=transition=fade:duration={fade_duration}:offset={offset}[xf1];"
|
||||
)
|
||||
else:
|
||||
offset += display_durations[i] - fade_duration
|
||||
filter_parts.append(
|
||||
f"[xf{i}][v{i+1}]xfade=transition=fade:duration={fade_duration}:offset={offset}[xf{i+1}];"
|
||||
)
|
||||
|
||||
last_xf = f"xf{len(images)-1}"
|
||||
filter_complex = ''.join(filter_parts).rstrip(';')
|
||||
|
||||
cmd = ['ffmpeg', '-y'] + inputs + [
|
||||
'-filter_complex', filter_complex,
|
||||
'-map', f'[{last_xf}]',
|
||||
'-c:v', 'libx264', '-preset', 'fast', '-crf', '20', '-pix_fmt', 'yuv420p',
|
||||
str(output_path)
|
||||
]
|
||||
|
||||
run_cmd(cmd, f"合成{len(images)}张图片")
|
||||
print(f" ✓ 主视频: {get_duration(output_path):.1f}秒")
|
||||
|
||||
|
||||
def merge_audio(audio_files, output_path):
|
||||
"""合并音频文件"""
|
||||
print(f"\n[2/4] 合并音频 ({len(audio_files)}个文件)")
|
||||
|
||||
concat_file = output_path.parent / "audio_concat.txt"
|
||||
with open(concat_file, 'w') as f:
|
||||
for audio in audio_files:
|
||||
f.write(f"file '{audio.absolute()}'\n")
|
||||
|
||||
cmd = [
|
||||
'ffmpeg', '-y', '-f', 'concat', '-safe', '0', '-i', str(concat_file),
|
||||
'-af', 'aresample=44100', '-c:a', 'aac', '-b:a', '192k', str(output_path)
|
||||
]
|
||||
run_cmd(cmd, "合并音频")
|
||||
concat_file.unlink()
|
||||
print(f" ✓ 音频: {get_duration(output_path):.1f}秒")
|
||||
|
||||
|
||||
def combine_video_audio(video_path, audio_path, output_path):
|
||||
"""合并视频和音频"""
|
||||
cmd = [
|
||||
'ffmpeg', '-y', '-i', str(video_path), '-i', str(audio_path),
|
||||
'-c:v', 'copy', '-c:a', 'copy', '-shortest', str(output_path)
|
||||
]
|
||||
run_cmd(cmd, "合并视频音频")
|
||||
|
||||
|
||||
def append_outro(video_path, output_path, fade_duration=0.5, ratio="16:9"):
|
||||
"""拼接片尾,自动缩放片尾到主视频分辨率"""
|
||||
print(f"\n[3/4] 拼接片尾")
|
||||
|
||||
outro_file = get_outro_path(ratio)
|
||||
if not outro_file.exists():
|
||||
print(f" ⚠ 片尾文件不存在: {outro_file}")
|
||||
return video_path
|
||||
|
||||
width, height = RATIO_TO_SIZE.get(ratio, (1920, 1080))
|
||||
|
||||
outro_ready = output_path.parent / "outro_ready.mp4"
|
||||
cmd = [
|
||||
'ffmpeg', '-y', '-i', str(outro_file),
|
||||
'-vf', f'scale={width}:{height}:force_original_aspect_ratio=decrease,pad={width}:{height}:(ow-iw)/2:(oh-ih)/2,setsar=1',
|
||||
'-c:v', 'libx264', '-preset', 'fast', '-crf', '20',
|
||||
'-c:a', 'aac', '-ar', '44100', str(outro_ready)
|
||||
]
|
||||
run_cmd(cmd, "准备片尾")
|
||||
|
||||
video_duration = get_duration(video_path)
|
||||
fade_start = video_duration - fade_duration
|
||||
|
||||
cmd = [
|
||||
'ffmpeg', '-y', '-i', str(video_path), '-i', str(outro_ready),
|
||||
'-filter_complex',
|
||||
f"[0:v]fade=t=out:st={fade_start}:d={fade_duration}[v0];"
|
||||
f"[1:v]fade=t=in:st=0:d={fade_duration}[v1];"
|
||||
f"[v0][v1]concat=n=2:v=1:a=0[vout];"
|
||||
f"[0:a][1:a]concat=n=2:v=0:a=1[aout]",
|
||||
'-map', '[vout]', '-map', '[aout]',
|
||||
'-c:v', 'libx264', '-preset', 'fast', '-crf', '20',
|
||||
'-c:a', 'aac', '-b:a', '192k', str(output_path)
|
||||
]
|
||||
run_cmd(cmd, "拼接片尾")
|
||||
outro_ready.unlink()
|
||||
print(f" ✓ 含片尾: {get_duration(output_path):.1f}秒")
|
||||
return output_path
|
||||
|
||||
|
||||
def burn_subtitles(video_path, srt_path, output_path, ratio="16:9"):
|
||||
"""烧录字幕到视频:底部居中固定位置"""
|
||||
print(f"\n[字幕] 烧录字幕")
|
||||
|
||||
if not Path(srt_path).exists():
|
||||
print(f" ⚠ 字幕文件不存在: {srt_path}")
|
||||
return video_path
|
||||
|
||||
width, height = RATIO_TO_SIZE.get(ratio, (1920, 1080))
|
||||
# 字体大小:高度/25,16:9时约43px,9:16时约77px
|
||||
font_size = max(36, int(height / 25))
|
||||
margin_bottom = int(height / 15)
|
||||
|
||||
ass_path = Path(srt_path).with_suffix('.ass')
|
||||
srt_to_ass(srt_path, ass_path, width, height, font_size, margin_bottom)
|
||||
|
||||
ass_escaped = str(ass_path).replace(":", r"\:").replace("'", r"\'")
|
||||
|
||||
cmd = [
|
||||
'ffmpeg', '-y', '-i', str(video_path),
|
||||
'-vf', f"ass='{ass_escaped}'",
|
||||
'-c:v', 'libx264', '-preset', 'fast', '-crf', '20',
|
||||
'-c:a', 'copy', str(output_path)
|
||||
]
|
||||
run_cmd(cmd, "烧录字幕")
|
||||
print(f" ✓ 含字幕: {get_duration(output_path):.1f}秒")
|
||||
return output_path
|
||||
|
||||
|
||||
def srt_to_ass(srt_path, ass_path, width, height, font_size, margin_bottom):
|
||||
"""将 SRT 转换为 ASS 格式,固定底部居中,自动换行"""
|
||||
import re
|
||||
|
||||
with open(srt_path, 'r', encoding='utf-8') as f:
|
||||
srt_content = f.read()
|
||||
|
||||
# 每行字数规则表(按分辨率宽度固定)
|
||||
CHARS_PER_LINE_MAP = {
|
||||
1024: 20, # 1:1
|
||||
832: 14, # 2:3
|
||||
1248: 32, # 3:2
|
||||
1080: 16, # 3:4, 4:5, 5:4, 9:16 (竖版统一16字)
|
||||
1440: 28, # 4:3
|
||||
864: 17, # 4:5
|
||||
1920: 38, # 16:9
|
||||
1536: 48, # 21:9
|
||||
}
|
||||
# 查表,找不到则按公式计算
|
||||
MAX_CHARS = CHARS_PER_LINE_MAP.get(width)
|
||||
if MAX_CHARS is None:
|
||||
# 兜底:按宽度和字体大小估算
|
||||
MAX_CHARS = max(12, int(width / (font_size * 1.2)))
|
||||
|
||||
# 标点符号(不能放行首)
|
||||
PUNCTUATION = ',。、:;?!,.:;?!)】」》\'\"'
|
||||
|
||||
def find_break_point(text, max_pos):
|
||||
"""找到合适的断点位置,优先在空格处断开"""
|
||||
if max_pos >= len(text):
|
||||
return len(text)
|
||||
|
||||
# 从max_pos往前找空格断点
|
||||
for i in range(max_pos, max(max_pos // 2, 1), -1):
|
||||
if text[i] == ' ':
|
||||
return i
|
||||
# 没找到空格就直接断
|
||||
return max_pos
|
||||
|
||||
def wrap_text_2lines(text):
|
||||
"""换行:严格2行,返回单个2行字幕块"""
|
||||
text = text.strip()
|
||||
if len(text) <= MAX_CHARS:
|
||||
return text + r'\N '
|
||||
|
||||
# 找第一行断点
|
||||
break1 = find_break_point(text, MAX_CHARS)
|
||||
line1 = text[:break1].strip()
|
||||
line2 = text[break1:].strip()
|
||||
|
||||
# 第二行也限制长度
|
||||
if len(line2) > MAX_CHARS:
|
||||
break2 = find_break_point(line2, MAX_CHARS)
|
||||
line2 = line2[:break2].strip()
|
||||
|
||||
return line1 + r'\N' + line2
|
||||
|
||||
def split_long_text(text, start_sec, end_sec):
|
||||
"""长文本拆成多条字幕,每条严格2行,时间均分"""
|
||||
text = text.strip()
|
||||
|
||||
# 先模拟换行,计算实际需要几条字幕
|
||||
blocks = []
|
||||
remaining = text
|
||||
while remaining:
|
||||
# 第一行
|
||||
if len(remaining) <= MAX_CHARS:
|
||||
blocks.append(remaining)
|
||||
break
|
||||
break1 = find_break_point(remaining, MAX_CHARS)
|
||||
line1 = remaining[:break1].strip()
|
||||
rest = remaining[break1:].strip()
|
||||
|
||||
# 第二行
|
||||
if len(rest) <= MAX_CHARS:
|
||||
blocks.append(line1 + ' ' + rest)
|
||||
break
|
||||
break2 = find_break_point(rest, MAX_CHARS)
|
||||
line2 = rest[:break2].strip()
|
||||
blocks.append(line1 + ' ' + line2)
|
||||
remaining = rest[break2:].strip()
|
||||
|
||||
# 时间均分
|
||||
duration = end_sec - start_sec
|
||||
time_per_block = duration / len(blocks)
|
||||
|
||||
result = []
|
||||
for i, block in enumerate(blocks):
|
||||
block_start = start_sec + i * time_per_block
|
||||
block_end = start_sec + (i + 1) * time_per_block
|
||||
result.append((block, block_start, block_end))
|
||||
|
||||
return result
|
||||
|
||||
ass_header = f"""[Script Info]
|
||||
Title: Subtitles
|
||||
ScriptType: v4.00+
|
||||
PlayResX: {width}
|
||||
PlayResY: {height}
|
||||
WrapStyle: 0
|
||||
|
||||
[V4+ Styles]
|
||||
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
|
||||
Style: Default,PingFang SC,{font_size},&H00FFFFFF,&H000000FF,&H00000000,&H80000000,0,0,0,0,100,100,0,0,1,2,1,2,10,10,{margin_bottom},1
|
||||
|
||||
[Events]
|
||||
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
|
||||
"""
|
||||
|
||||
def sec_to_ass_time(sec):
|
||||
"""秒数转ASS时间格式"""
|
||||
h = int(sec // 3600)
|
||||
m = int((sec % 3600) // 60)
|
||||
s = int(sec % 60)
|
||||
cs = int((sec % 1) * 100)
|
||||
return f"{h}:{m:02d}:{s:02d}.{cs:02d}"
|
||||
|
||||
events = []
|
||||
blocks = re.split(r'\n\n+', srt_content.strip())
|
||||
|
||||
for block in blocks:
|
||||
lines = block.strip().split('\n')
|
||||
if len(lines) >= 3:
|
||||
time_line = lines[1]
|
||||
text = ' '.join(lines[2:]).replace('\n', ' ')
|
||||
# 标点符号替换为空格,便于换行分割
|
||||
text = re.sub(r'[,。、:;?!,.:;?!""''「」『』【】()()《》]', ' ', text)
|
||||
# 合并多个空格为一个
|
||||
text = re.sub(r'\s+', ' ', text).strip()
|
||||
|
||||
match = re.match(r'(\d{2}):(\d{2}):(\d{2}),(\d{3}) --> (\d{2}):(\d{2}):(\d{2}),(\d{3})', time_line)
|
||||
if match:
|
||||
sh, sm, ss, sms = match.groups()[:4]
|
||||
eh, em, es, ems = match.groups()[4:]
|
||||
start_sec = int(sh) * 3600 + int(sm) * 60 + int(ss) + int(sms) / 1000
|
||||
end_sec = int(eh) * 3600 + int(em) * 60 + int(es) + int(ems) / 1000
|
||||
|
||||
# 长文本拆成多条字幕
|
||||
sub_blocks = split_long_text(text, start_sec, end_sec)
|
||||
for sub_text, sub_start, sub_end in sub_blocks:
|
||||
formatted_text = wrap_text_2lines(sub_text)
|
||||
start = sec_to_ass_time(sub_start)
|
||||
end = sec_to_ass_time(sub_end)
|
||||
events.append(f"Dialogue: 0,{start},{end},Default,,0,0,0,,{formatted_text}")
|
||||
|
||||
with open(ass_path, 'w', encoding='utf-8') as f:
|
||||
f.write(ass_header + '\n'.join(events))
|
||||
|
||||
|
||||
def add_bgm(video_path, output_path, volume=0.08, bgm_path=None):
|
||||
"""添加背景音乐"""
|
||||
print(f"\n[4/4] 添加BGM")
|
||||
|
||||
if bgm_path is None:
|
||||
bgm_path = BGM_DEFAULT
|
||||
bgm_path = Path(bgm_path)
|
||||
|
||||
if not bgm_path.exists():
|
||||
print(f" ⚠ BGM文件不存在: {bgm_path}")
|
||||
return video_path
|
||||
|
||||
cmd = [
|
||||
'ffmpeg', '-y', '-i', str(video_path),
|
||||
'-stream_loop', '-1', '-i', str(bgm_path),
|
||||
'-filter_complex',
|
||||
f"[1:a]volume={volume}[bgm];[0:a][bgm]amix=inputs=2:duration=first[aout]",
|
||||
'-map', '0:v', '-map', '[aout]',
|
||||
'-c:v', 'copy', '-c:a', 'aac', '-b:a', '192k', str(output_path)
|
||||
]
|
||||
run_cmd(cmd, "添加BGM")
|
||||
print(f" ✓ 最终视频: {get_duration(output_path):.1f}秒")
|
||||
return output_path
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='视频生成器')
|
||||
parser.add_argument('config', help='配置文件路径 (YAML)')
|
||||
parser.add_argument('--no-outro', action='store_true', help='不添加片尾')
|
||||
parser.add_argument('--no-bgm', action='store_true', help='不添加BGM')
|
||||
parser.add_argument('--fade', type=float, default=0.5, help='转场时长(秒)')
|
||||
parser.add_argument('--bgm-volume', type=float, default=0.08, help='BGM音量')
|
||||
parser.add_argument('--bgm', type=str, default=None, help='自定义BGM路径,可选: epic')
|
||||
parser.add_argument('--ratio', type=str, default='16:9',
|
||||
help=f'视频比例,支持: {", ".join(VALID_ASPECT_RATIOS)}')
|
||||
parser.add_argument('--srt', type=str, default=None, help='字幕文件路径(SRT格式)')
|
||||
args = parser.parse_args()
|
||||
|
||||
config_path = Path(args.config)
|
||||
if not config_path.exists():
|
||||
print(f"配置文件不存在: {config_path}")
|
||||
sys.exit(1)
|
||||
|
||||
with open(config_path) as f:
|
||||
config = yaml.safe_load(f)
|
||||
|
||||
work_dir = config_path.parent
|
||||
output_path = work_dir / config.get('output', 'output.mp4')
|
||||
|
||||
if args.ratio == '16:9' and 'ratio' in config:
|
||||
args.ratio = config['ratio']
|
||||
|
||||
if 'bgm_volume' in config and args.bgm_volume == 0.08:
|
||||
args.bgm_volume = config['bgm_volume']
|
||||
|
||||
if args.ratio not in VALID_ASPECT_RATIOS:
|
||||
print(f"错误: 不支持的比例 '{args.ratio}'")
|
||||
print(f"支持的比例: {', '.join(VALID_ASPECT_RATIOS)}")
|
||||
sys.exit(1)
|
||||
|
||||
scenes = config.get('scenes', [])
|
||||
if not scenes:
|
||||
print("配置文件中没有 scenes")
|
||||
sys.exit(1)
|
||||
|
||||
images = []
|
||||
durations = []
|
||||
audio_files = []
|
||||
|
||||
for scene in scenes:
|
||||
audio = work_dir / scene['audio']
|
||||
if not audio.exists():
|
||||
print(f"音频不存在: {audio}")
|
||||
sys.exit(1)
|
||||
audio_files.append(audio)
|
||||
|
||||
if 'images' in scene:
|
||||
for img_cfg in scene['images']:
|
||||
img = work_dir / img_cfg['file']
|
||||
if not img.exists():
|
||||
print(f"图片不存在: {img}")
|
||||
sys.exit(1)
|
||||
images.append(img)
|
||||
durations.append(img_cfg['duration'])
|
||||
else:
|
||||
img = work_dir / scene['image']
|
||||
if not img.exists():
|
||||
print(f"图片不存在: {img}")
|
||||
sys.exit(1)
|
||||
images.append(img)
|
||||
durations.append(get_duration(audio))
|
||||
|
||||
total_audio_duration = sum(get_duration(af) for af in audio_files)
|
||||
total_image_duration = sum(durations)
|
||||
|
||||
if total_image_duration < total_audio_duration:
|
||||
gap = total_audio_duration - total_image_duration + 0.5
|
||||
durations[-1] += gap
|
||||
print(f"\n⚠ 图片时长({total_image_duration:.1f}s) < 音频时长({total_audio_duration:.1f}s)")
|
||||
print(f" 自动拉伸最后一张图片 +{gap:.1f}s")
|
||||
|
||||
print(f"\n{'='*50}")
|
||||
print(f"视频生成器")
|
||||
print(f"{'='*50}")
|
||||
print(f"场景数: {len(scenes)}")
|
||||
print(f"音频时长: {total_audio_duration:.1f}秒")
|
||||
print(f"视频时长: {sum(durations):.1f}秒")
|
||||
print(f"转场: {args.fade}秒 淡入淡出")
|
||||
print(f"片尾: {'是' if not args.no_outro else '否'}")
|
||||
print(f"BGM: {'是' if not args.no_bgm else '否'}")
|
||||
|
||||
temp_dir = work_dir / "temp"
|
||||
temp_dir.mkdir(exist_ok=True)
|
||||
|
||||
video_only = temp_dir / "video_only.mp4"
|
||||
generate_video_with_transitions(images, durations, video_only, args.fade, args.ratio)
|
||||
|
||||
audio_merged = temp_dir / "audio_merged.m4a"
|
||||
merge_audio(audio_files, audio_merged)
|
||||
|
||||
video_with_audio = temp_dir / "video_with_audio.mp4"
|
||||
combine_video_audio(video_only, audio_merged, video_with_audio)
|
||||
|
||||
current_video = video_with_audio
|
||||
|
||||
if args.srt:
|
||||
srt_path = work_dir / args.srt if not Path(args.srt).is_absolute() else Path(args.srt)
|
||||
video_with_subs = temp_dir / "video_with_subs.mp4"
|
||||
current_video = burn_subtitles(current_video, srt_path, video_with_subs, args.ratio)
|
||||
|
||||
if not args.no_outro:
|
||||
video_with_outro = temp_dir / "video_with_outro.mp4"
|
||||
current_video = append_outro(current_video, video_with_outro, args.fade, args.ratio)
|
||||
|
||||
if not args.no_bgm:
|
||||
bgm_path = None
|
||||
if args.bgm:
|
||||
if args.bgm == 'epic':
|
||||
bgm_path = BGM_EPIC
|
||||
else:
|
||||
bgm_path = Path(args.bgm)
|
||||
add_bgm(current_video, output_path, args.bgm_volume, bgm_path)
|
||||
else:
|
||||
subprocess.run(['cp', str(current_video), str(output_path)])
|
||||
|
||||
print(f"\n{'='*50}")
|
||||
print(f"✅ 完成: {output_path}")
|
||||
print(f"{'='*50}\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||