我现在一步步教你,针对你说的这个主题「江西理工大学被留学生群殴」来举例。
第 1 步:准备素材
1.1 写一段解说文案(避免直接传播暴力画面)
比如:
近日,江西理工大学发生一起冲突事件,几名留学生与本地学生发生争执,引起社会广泛关注。目前,学校和警方已介入调查,相关人员已被控制。
1.2 准备背景图片
可以找一些江西理工大学校园图片、中立场景图,避免使用暴力画面。
-
比如:
campus1.jpg
,campus2.jpg
,campus3.jpg
第 2 步:微软 Edge TTS(永久免费)
微软的云TTS,但通过Edge接口走,不需要SecretId,也不用开通服务。适合你这种快速、免费搞短视频的场景。
第 3 步:自动加文字字幕(提升视频质量)
源代码:
import os
import subprocess
import asyncio
import edge_tts
from datetime import datetime
# === 配置部分 ===
VOICE_TYPE = "zh-CN-XiaoxiaoNeural"
TEXT_CONTENT = "近日,江西理工大学发生一起冲突事件,几名留学生与本地学生发生争执,引起社会广泛关注。目前,学校和警方已介入调查,相关人员已被控制。"
IMAGES = ["img/campus1.jpg", "img/campus2.jpg", "img/campus3.jpg"]
OUTPUT_VIDEO = "final_video.mp4"
TITLE_TEXT = "微信公众号 AI创造财富"
# === 步骤1: 用Edge TTS生成配音 ===
async def edge_tts_generate(text, output_file="audio.mp3"):
print("🎤 开始调用Edge TTS生成语音...")
communicate = edge_tts.Communicate(text, VOICE_TYPE)
await communicate.save(output_file)
print(f"✅ 语音已保存为 {output_file}")
def generate_audio(text, output_file="audio.mp3"):
asyncio.run(edge_tts_generate(text, output_file))
# === 字幕生成 ===
def generate_srt(text, srt_file="subtitles.srt", audio_file="audio.mp3"):
import math
sentences = [s.strip() for s in text.replace('。', '。\n').replace(',', ',\n').split('\n') if s.strip()]
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())
sentence_duration = audio_duration / len(sentences)
with open(srt_file, 'w', encoding='utf-8', newline='\n') as f:
for i, sentence in enumerate(sentences, 1):
start_time = (i - 1) * sentence_duration
end_time = min(i * sentence_duration, audio_duration)
start_h = int(start_time // 3600)
start_m = int((start_time % 3600) // 60)
start_s = int(start_time % 60)
start_ms = int((start_time % 1) * 1000)
end_h = int(end_time // 3600)
end_m = int((end_time % 3600) // 60)
end_s = int(end_time % 60)
end_ms = int((end_time % 1) * 1000)
f.write(f"{i}\n")
f.write(f"{start_h:02}:{start_m:02}:{start_s:02},{start_ms:03} --> "
f"{end_h:02}:{end_m:02}:{end_s:02},{end_ms:03}\n")
f.write(f"{sentence}\n\n")
print(f"✅ 已生成字幕文件: {srt_file}")
with open(srt_file, 'r', encoding='utf-8') as f:
print("📄 字幕内容:\n", f.read())
# === 图片预处理 ===
def check_write_permission(file_path):
test_file = os.path.join(os.path.dirname(file_path), "test_write.txt")
try:
with open(test_file, 'w') as f:
f.write("test")
os.remove(test_file)
print(f"✅ 目录可写: {os.path.dirname(file_path)}")
except PermissionError as e:
raise PermissionError(f"无权限写入目录: {os.path.dirname(file_path)}, 错误: {e}")
def preprocess_images():
processed = []
os.makedirs("processed", exist_ok=True)
for i, img_path in enumerate(IMAGES):
if not os.path.exists(img_path):
raise FileNotFoundError(f"图片不存在: {img_path}")
output_path = f"processed/img_{i}_converted.jpg"
try:
run_ffmpeg([
'ffmpeg', '-y', '-i', img_path,
'-c:v', 'mjpeg', '-q:v', '2', '-pix_fmt', 'yuv420p',
'-vf', 'scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2',
output_path
])
if not os.path.exists(output_path) or os.path.getsize(output_path) < 1000:
raise RuntimeError(f"图片生成失败: {output_path}")
print(f"✅ 生成图片: {output_path}")
test_output = f"processed/test_img_{i}_converted.mp4"
run_ffmpeg([
'ffmpeg', '-y', '-loop', '1', '-i', output_path,
'-t', '3', '-pix_fmt', 'yuv420p', test_output
])
print(f"✅ 单张图片测试视频: {test_output}")
processed.append(output_path)
except subprocess.CalledProcessError as e:
print(f"❌ 图片处理失败: {img_path}, 错误: {e.stderr}")
raise
return processed
# === 图片合成步骤 ===
def create_images_txt(images, duration=3, txt_file="images.txt"):
with open(txt_file, 'w', encoding='utf-8', newline='\n') as f:
for img in images:
img_path = os.path.abspath(img).replace('\\', '/')
if not os.path.exists(img):
raise FileNotFoundError(f"图片文件未找到: {img}")
f.write(f"file '{img_path}'\n")
f.write(f"duration {duration}\n")
print(f"✅ 已生成图片列表文件: {txt_file}")
with open(txt_file, 'r', encoding='utf-8') as f:
content = f.read()
print("📄 images.txt内容:\n", content)
def run_ffmpeg(commands):
print(f"▶️ 运行FFmpeg命令: {' '.join(commands)}")
try:
result = subprocess.run(commands, check=True, capture_output=True, text=True)
print(result.stdout)
except subprocess.CalledProcessError as e:
print(f"❌ FFmpeg命令失败,错误输出:\n{e.stderr}")
raise
def create_video_from_images(images_txt, temp_video, audio_file="audio.mp3"):
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())
image_duration = 3
required_images = max(len(open(images_txt, 'r').readlines()) // 2, int(audio_duration / image_duration) + 1)
with open(images_txt, 'r', encoding='utf-8') as f:
lines = f.readlines()
last_image = lines[-2].strip()
with open(images_txt, 'w', encoding='utf-8', newline='\n') as f:
f.writelines(lines)
for _ in range(required_images - len(lines) // 2):
f.write(f"{last_image}\n")
f.write(f"duration {image_duration}\n")
try:
run_ffmpeg([
'ffmpeg', '-y', '-f', 'concat', '-safe', '0', '-i', images_txt,
'-vsync', 'cfr', '-framerate', f'1/{image_duration}', '-pix_fmt', 'yuv420p', temp_video
])
except subprocess.CalledProcessError as e:
print(f"❌ FFmpeg命令失败,错误输出:\n{e.stderr}")
raise
def add_audio_to_video(temp_video, audio_file, final_video):
run_ffmpeg([
'ffmpeg', '-y', '-i', temp_video, '-i', audio_file,
'-c:v', 'copy', '-c:a', 'aac', final_video
])
def add_text_to_video(input_video, output_video, text, srt_file="subtitles.srt"):
srt_path = os.path.abspath(srt_file).replace('\\', '\\\\').replace(':', '\\:')
escaped_text = text.encode('utf-8').decode('utf-8')
run_ffmpeg([
'ffmpeg', '-y', '-i', input_video,
'-vf', (
f"subtitles='{srt_path}':force_style='FontName=Microsoft YaHei,FontSize=24,PrimaryColour=&H0000FFFF,"
f"OutlineColour=&HFF000000,BackColour=&HFF000000,BorderStyle=1,Alignment=2,MarginV=50',"
f"drawtext=text='{escaped_text}':fontcolor=red:fontsize=36:x=(w-text_w)/2:y=50:font=Microsoft YaHei"
),
'-codec:a', 'copy', output_video
])
print(f"✅ 已添加字幕和标题: {output_video}")
# === 主流程 ===
def generate_video():
global OUTPUT_VIDEO
OUTPUT_VIDEO = os.path.normpath(OUTPUT_VIDEO)
temp_video = os.path.normpath("temp_video.mp4")
check_write_permission(temp_video)
processed_images = preprocess_images()
generate_audio(TEXT_CONTENT, output_file="audio.mp3")
generate_srt(TEXT_CONTENT, srt_file="subtitles.srt", audio_file="audio.mp3")
create_images_txt(processed_images)
create_video_from_images("images.txt", temp_video, audio_file="audio.mp3")
add_audio_to_video(temp_video, "audio.mp3", OUTPUT_VIDEO)
timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
output_with_text = os.path.normpath(f"final_video_with_text_{timestamp}.mp4")
add_text_to_video(OUTPUT_VIDEO, output_with_text, TITLE_TEXT)
print(f"🎉 ✅ 视频已生成: {output_with_text}")
if __name__ == "__main__":
try:
generate_video()
except Exception as e:
print(f"❌ 程序异常: {e}")
更多源代码,请关注 微信公众号。