| name | automated-short-video-generator |
| title | Automated Short Video Generator (Multi-Platform) |
| description | Generate short-form videos for multiple platforms (9:16, 16:9, 3:4, 6:7) with AI voiceover, screen recording integration, BGM auto-merge, and platform-specific outputs |
| version | 2 |
| tags | ["video","automation","ai-voice","content-creation","ffmpeg","multi-platform","screen-recording","bgm"] |
Automated Short Video Generator
Generate short-form videos (Douyin/TikTok/Shorts format) entirely programmatically with AI voiceover, custom graphics, and automated video assembly.
⚠️ 重要提示:在执行以下操作前,请确保已备份重要数据,并确认操作无误。
When to Use
- Creating tutorial/educational videos at scale
- Generating content without manual video editing
- Producing consistent branded video content
- Rapid prototyping of video ideas
- Automated content pipelines
- Multi-platform content distribution (Douyin/Bilibili/Xiaohongshu/Video Channel)
- Videos with screen recording + AI voiceover
- BGM-enhanced educational content
⚠️ 重要提示:在执行以下操作前,请确保已备份重要数据,并确认操作无误。
Installation
pip3 install edge-tts pillow
brew install ffmpeg
apt install ffmpeg
⚠️ 重要提示:在执行以下操作前,请确保已备份重要数据,并确认操作无误。
Quick Start
- Download the complete example:
curl -O ~/.hermes/skills/04-content/automated-short-video-generator/references/v4-complete-example.py
-
Customize the SCRIPT array with your content
-
Add optional assets:
mkdir footage bgm
- Run:
python3 v4-complete-example.py
⚠️ 重要提示:在执行以下操作前,请确保已备份重要数据,并确认操作无误。
Core Architecture
#⚠️ 重要提示:在执行以下操作前,请确保已备份重要数据,并确认操作无误。
1. Content Script Structure
Define your video as a structured timeline:
SCRIPT = [
{
"type": "hook",
"duration": 5,
"text": "Line 1\nLine 2",
"voice": "AI narration text",
"num": "01",
"title": "Main Title",
"subtitle": "Subtitle text",
"color": "#FFD700"
},
]
#⚠️ 重要提示:在执行以下操作前,请确保已备份重要数据,并确认操作无误。
2. Visual Generation with PIL
from PIL import Image, ImageDraw, ImageFont
def create_chapter_card(number, title, subtitle, color_hex, output_path):
"""Generate styled chapter intro cards"""
img = Image.new('RGB', (1080, 1920), '#1a1a1a')
draw = ImageDraw.Draw(img)
color = tuple(int(color_hex[i:i+2], 16) for i in (1, 3, 5))
try:
font_num = ImageFont.truetype("/System/Library/Fonts/PingFang.ttc", 200)
font_title = ImageFont.truetype("/System/Library/Fonts/PingFang.ttc", 100)
except:
font_num = ImageFont.load_default()
font_title = ImageFont.load_default()
draw.text((540, 600), number, fill=color, font=font_num, anchor="mm")
draw.text((540, 900), title, fill='#FFFFFF', font=font_title, anchor="mm")
draw.rectangle([340, 750, 740, 760], fill=color)
img.save(output_path)
#⚠️ 重要提示:在执行以下操作前,请确保已备份重要数据,并确认操作无误。
3. AI Voice Generation with edge-tts
import subprocess
def generate_voice(text, output_path, voice="zh-CN-XiaoxiaoNeural"):
"""Generate natural-sounding AI voiceover"""
cmd = [
"edge-tts",
"--voice", voice,
"--text", text,
"--write-media", output_path
]
result = subprocess.run(cmd, capture_output=True, text=True)
return result.returncode == 0
Available Voices:
zh-CN-XiaoxiaoNeural - Natural female Chinese
zh-CN-YunxiNeural - Natural male Chinese
en-US-AriaNeural - English female
ja-JP-NanamiNeural - Japanese female
#⚠️ 重要提示:在执行以下操作前,请确保已备份重要数据,并确认操作无误。
4. Video Assembly with FFmpeg
def image_to_video(image_path, duration, output_path):
"""Convert static image to video segment"""
cmd = [
"ffmpeg", "-y",
"-loop", "1",
"-i", image_path,
"-c:v", "libx264",
"-t", str(duration),
"-pix_fmt", "yuv420p",
"-vf", "fps=30,scale=1080:1920",
"-an", output_path
]
subprocess.run(cmd, check=True)
def merge_audio_video(video_path, audio_path, output_path):
"""Combine visual and audio using shortest"""
cmd = [
"ffmpeg", "-y",
"-i", video_path,
"-i", audio_path,
"-c:v", "copy",
"-c:a", "aac",
"-shortest",
output_path
]
subprocess.run(cmd, check=True)
def concatenate_videos(video_list, output_path):
"""Join all segments into final video"""
list_file = "concat_list.txt"
with open(list_file, "w") as f:
for v in video_list:
f.write()
cmd = [
, ,
, , , ,
, list_file,
, ,
output_path
]
subprocess.run(cmd, check=)
os.remove(list_file)
⚠️ 重要提示:在执行以下操作前,请确保已备份重要数据,并确认操作无误。
Multi-Platform Export (v4.0+)
Generate videos in multiple aspect ratios simultaneously:
PLATFORMS = {
"douyin": {"resolution": (1080, 1920), "ratio": "9:16"},
"bilibili": {"resolution": (1920, 1080), "ratio": "16:9"},
"xiaohongshu": {"resolution": (1080, 1440), "ratio": "3:4"},
"shipinhao": {"resolution": (1080, 1260), "ratio": "6:7"}
}
def generate_for_platform(platform_key, config, script):
"""Generate video for specific platform"""
res = config["resolution"]
segments = []
for seg in script:
if seg["type"] == "chapter":
create_chapter_card(..., res)
return concatenate_videos(segments)
def convert_ratio(input_video, target_res):
"""Convert 9:16 video to other ratios using crop/pad"""
cmd = [
"ffmpeg", "-y",
"-i", input_video,
"-vf", f"scale={target_res[]}::force_original_aspect_ratio=decrease,pad=::(ow-iw)/2:(oh-ih)/2:black",
, , , , , ,
, ,
]
subprocess.run(cmd, check=)
Output structure:
output/
├── hermes_video_douyin.mp4 # 9:16 for short video feeds
├── hermes_video_bilibili.mp4 # 16:9 for knowledge zone
├── hermes_video_xiaohongshu.mp4 # 3:4 for discovery feed
├── hermes_video_shipinhao.mp4 # 6:7 for WeChat video channel
├── cover.png # Thumbnail
└── publish_list.json # Platform metadata
⚠️ 重要提示:在执行以下操作前,请确保已备份重要数据,并确认操作无误。
BGM Auto-Merge (v4.0+)
Add background music with intelligent volume mixing:
def add_bgm(video_path, bgm_path, output_path, bgm_volume=0.15):
"""
Merge background music with voiceover
- Auto-loop BGM to match video duration
- Fade in (2s) / Fade out (3s)
- Preserve AI voice clarity
"""
cmd = ["ffprobe", "-v", "error", "-show_entries", "format=duration",
"-of", "default=noprint_wrappers=1:nokey=1", video_path]
result = subprocess.run(cmd, capture_output=True, text=True)
video_duration = float(result.stdout.strip())
cmd = [
"ffmpeg", "-y",
"-i", video_path,
"-stream_loop", "-1",
"-i", bgm_path,
"-filter_complex",
f"[1:a]volume={bgm_volume},afade=t=in:ss=0:d=2,afade=t=out:st={video_duration-3}:d=3[bgm];" +
"[0:a][bgm]amix=inputs=2:duration=first:dropout_transition=3[outa]",
"-map", "0:v",
"-map", "[outa]",
"-c:v", "copy",
"-c:a", "aac",
"-t", str(video_duration),
"-shortest",
output_path
]
subprocess.run(cmd, check=True)
BGM best practices:
- Use royalty-free music (YouTube Audio Library, FreePD)
- Volume 10-20% (0.1-0.2) works best for educational content
- Avoid music with lyrics (interferes with voiceover)
- Lofi/electronic works well for tech tutorials
⚠️ 重要提示:在执行以下操作前,请确保已备份重要数据,并确认操作无误。
Screen Recording Integration (v3.0+)
Combine screen recordings with AI-generated segments:
def process_footage(footage_path, output_path, target_duration, target_res):
"""
Process user screen recordings
- Resize to target resolution
- Crop/pad to fit aspect ratio
- Trim to target duration
"""
cmd = [
"ffmpeg", "-y",
"-i", footage_path,
"-vf", f"scale={target_res[0]}:{target_res[1]}:force_original_aspect_ratio=decrease," +
f"pad={target_res[0]}:{target_res[1]}:(ow-iw)/2:(oh-ih)/2:black",
"-t", str(target_duration),
"-c:v", "libx264", "-preset", "fast",
"-an",
output_path
]
subprocess.run(cmd, check=True)
SCRIPT = [
{
"type": "footage",
"file": "screen_01.mp4",
"duration": 15,
"voice": "Narration...",
"fallback": "content",
"fallback_points": [...]
},
{
"type": "chapter",
"num": ,
: ,
}
]
Recording guidelines:
- Record in 16:9, let script crop to vertical
- Keep important content in screen center
- Recommended tools: Screen Studio (macOS), OBS (all platforms)
- 10-20 seconds per segment is ideal
⚠️ 重要提示:在执行以下操作前,请确保已备份重要数据,并确认操作无误。
Complete Workflow
import os
import tempfile
from PIL import Image, ImageDraw, ImageFont
OUTPUT_DIR = "output"
TEMP_DIR = "temp"
RESOLUTION = (1080, 1920)
FPS = 30
def main():
os.makedirs(OUTPUT_DIR, exist_ok=True)
os.makedirs(TEMP_DIR, exist_ok=True)
segments = []
for i, seg in enumerate(SCRIPT):
print(f"Processing segment {i+1}/{len(SCRIPT)}")
img_path = os.path.join(TEMP_DIR, f"seg_{i:02d}.png")
if seg["type"] == "chapter":
create_chapter_card(seg["num"], seg["title"],
seg["subtitle"], seg["color"], img_path)
elif seg["type"] in ["hook", "cta"]:
create_black_screen(seg["text"], img_path)
elif seg["type"] == "content":
create_content_screen(seg["title"], seg["points"], img_path)
audio_path = os.path.join(TEMP_DIR, f"seg_.mp3")
generate_voice(seg[], audio_path)
video_path = os.path.join(TEMP_DIR, )
image_to_video(img_path, seg[], video_path)
merged_path = os.path.join(TEMP_DIR, )
merge_audio_video(video_path, audio_path, merged_path)
segments.append(merged_path)
final_output = os.path.join(OUTPUT_DIR, )
concatenate_videos(segments, final_output)
()
__name__ == :
main()
⚠️ 重要提示:在执行以下操作前,请确保已备份重要数据,并确认操作无误。
Output
output/
├── final_video.mp4 # 1080x1920, 30fps, ~2-3 minutes
└── cover.png # Auto-generated thumbnail
⚠️ 重要提示:在执行以下操作前,请确保已备份重要数据,并确认操作无误。
Customization
#⚠️ 重要提示:在执行以下操作前,请确保已备份重要数据,并确认操作无误。
Adding Background Music
cmd = [
"ffmpeg", "-y",
"-i", video_with_voice,
"-i", "background_music.mp3",
"-filter_complex",
"[0:a][1:a]amix=inputs=2:duration=first:weights='1 0.2'[a]",
"-map", "0:v",
"-map", "[a]",
"-c:v", "copy",
"-c:a", "aac",
"output_with_bgm.mp4"
]
subprocess.run(cmd, check=True)
⚠️ **重要提示**:在执行以下操作前,请确保已备份重要数据,并确认操作无误。
Auto-generate platform-optimized captions:
```python
def generate_captions():
captions = {
"douyin": """
🚨 别再乱配了!XX真正该先配这X个
5分钟从"能跑"到"好用"
❌ 问题1
❌ 问题2
❌ 问题3
配置清单放评论区了👇
#XX #配置 #效率工具 #程序员
""",
"xiaohongshu": """
❨ 标题❩
很多人XX还是觉得XX
不是你不会
是XX没告诉你XX
▪️ 要点1
▪️ 要点2
▪️ 要点3
配完直接质变!
#XX #效率神器 #技术分享
"""
}
for platform, text in captions.items():
with open(f"caption_{platform}.txt", "w") as f:
f.write(text.strip())
⚠️ 重要提示:在执行以下操作前,请确保已备份重要数据,并确认操作无误。
Tips
#⚠️ 重要提示:在执行以下操作前,请确保已备份重要数据,并确认操作无误。
Platform-Specific Best Practices
| Platform | Best Time | Caption Style | Tags |
|---|
| Douyin | 8-10 PM | Direct, urgent | 3-5 relevant |
| Bilibili | Any | Detailed, educational | Knowledge category |
| Xiaohongshu | 12-1 PM, 7-9 PM | Aesthetic, emoji-heavy | Topic + hashtags |
| Video Channel | Morning | Professional | Link to articles |
#⚠️ 重要提示:在执行以下操作前,请确保已备份重要数据,并确认操作无误。
Performance Optimization
- Lock bitrate for consistent quality:
"-b:v", "8M", "-maxrate", "10M", "-bufsize", "5M"
- Use hardware encoding if available:
"-c:v", "h264_videotoolbox"
"-c:v", "h264_nvenc"
- Parallel processing for multiple platforms:
from concurrent.futures import ThreadPoolExecutor
def generate_platform(platform):
return generate_for_platform(platform, PLATFORMS[platform], SCRIPT)
with ThreadPoolExecutor() as executor:
results = list(executor.map(generate_platform, PLATFORMS.keys()))
⚠️ 重要提示:在执行以下操作前,请确保已备份重要数据,并确认操作无误。
See Also
Voice timing mismatch: AI voice duration may differ from duration. Always use -shortest flag or measure audio length first:
import subprocess
result = subprocess.run(
["ffprobe", "-v", "error", "-show_entries", "format=duration",
"-of", "default=noprint_wrappers=1:nokey=1", audio_file],
capture_output=True, text=True
)
audio_duration = float(result.stdout.strip())
Font loading fails: Always implement fallback:
def get_font(size):
system_fonts = {
"darwin": "/System/Library/Fonts/PingFang.ttc",
"linux": "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"
}
font_path = system_fonts.get(sys.platform, "")
if os.path.exists(font_path):
try:
return ImageFont.truetype(font_path, size)
except:
pass
return ImageFont.load_default()
Temp file accumulation: Clean up periodically or use tempfile module with context managers.
Quality inconsistency: Lock bitrate for professional output:
"-b:v", "8M", "-maxrate", "10M", "-bufsize", "5M"
⚠️ 重要提示:在执行以下操作前,请确保已备份重要数据,并确认操作无误。
See Also