如何做一个简单的短视频(举例:江西理工大学冲突事件)

我现在一步步教你,针对你说的这个主题「江西理工大学被留学生群殴」来举例。

第 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}")

更多源代码,请关注 微信公众号。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值