NIUBOYI»Home › › 脚本分享 › 中文 | English
微信扫一扫 分享朋友圈

已有 734 人浏览分享

脚本分享    Batch mixed editing of short videos for a single account, AI copywriting, AI cloned voiceover, with subtitles, background audio, and high quality.

2024-9-18 11:53:11734 0
This only includes the video montage/compositing part. The copy and original video footage processing are prepared in advance; combining them together at present is not very reasonable.
All core capabilities involved in the script are built with local programs. There are no additional fees, no updates are needed, and once deployed, it can be used forever.
The computer at the moderator’s company is a GTX 2060; on average, it produces a 1-minute video in about 3 minutes.

During automatic production, the program produces videos one by one according to the copy in 文案.txt that we prepared in advance.
This version is only for a single project. For example, if you use one person as the video protagonist, for now you can only use him for everything, and it supports only his one account.

Script Process
1. Audio cloning
2. Generate audio from the copy
3. Synthesize audio into video
4. Add background audio to the video
5. Add subtitles to the video

Specific Script


```python
import os
import random
import subprocess
import requests
import re
from datetime import datetime
from moviepy.editor import VideoFileClip, TextClip, CompositeVideoClip, AudioFileClip, CompositeAudioClip

# 文件路径配置
base_dir = os.path.dirname(os.path.abspath(__file__))
wenan_file_path = os.path.join(base_dir, 'wenan', 'wenan.txt')
audio_save_folder = os.path.join(base_dir, 'yinpin', 'yonghu')
video_folders = {
    'kaitou': os.path.join(base_dir, 'shipin', 'yonghu', 'kaitou'),
    'zhongjian': os.path.join(base_dir, 'shipin', 'yonghu', 'zhongjian'),
    'jiewei': os.path.join(base_dir, 'shipin', 'yonghu', 'jiewei')
}
output_video_folder = os.path.join(base_dir, 'zuopin', 'yonghu')

def get_audio_duration(audio_path):
    """获取音频或视频的时长"""
    cmd = f'ffprobe -i "{audio_path}" -show_entries format=duration -v quiet -of csv="p=0"'
    try:
        return float(subprocess.check_output(cmd, shell=True).decode().strip())
    except subprocess.CalledProcessError:
        print("获取时长失败")
        return None

def select_video_paths(target_duration):
    """根据目标时长选择视频片段,拼接至总时长大于等于目标时长"""
    video_paths = []
    current_duration = 0

    # 添加开头片段
    video_paths.append(os.path.join(video_folders['kaitou'], random.choice(os.listdir(video_folders['kaitou']))))
    current_duration += get_audio_duration(video_paths[-1])

    # 添加中间片段直到时长超过目标
    while current_duration < target_duration:
        video_paths.append(os.path.join(video_folders['zhongjian'], random.choice(os.listdir(video_folders['zhongjian']))))
        current_duration += get_audio_duration(video_paths[-1])

    # 添加结尾片段
    video_paths.append(os.path.join(video_folders['jiewei'], random.choice(os.listdir(video_folders['jiewei']))))
    return video_paths, current_duration

def concat_and_trim_videos(video_paths, target_duration, output_path):
    """拼接视频片段并截取到目标时长"""
    print(f"正在拼接视频片段,总计 {len(video_paths)} 个片段。")
    concat_file = output_path + '_concat.txt'
    with open(concat_file, 'w') as f:
        for video_path in video_paths:
            f.write(f"file '{video_path}'\n")
    temp_video_path = output_path + '_temp.mp4'
    ffmpeg_concat_cmd = f'ffmpeg -f concat -safe 0 -i "{concat_file}" -vf "scale=720:1280" "{temp_video_path}"'
    try:
        subprocess.check_call(ffmpeg_concat_cmd, shell=True)
        if os.path.exists(temp_video_path):
            # 截取视频到目标时长
            trimmed_video_path = output_path + '_trimmed.mp4'
            ffmpeg_trim_cmd = f'ffmpeg -i "{temp_video_path}" -t {target_duration} -c copy "{trimmed_video_path}"'
            subprocess.check_call(ffmpeg_trim_cmd, shell=True)
            return trimmed_video_path
        else:
            print("拼接视频生成失败,文件不存在。")
            return None
    except subprocess.CalledProcessError:
        print("视频拼接失败")
        return None

def combine_audio_video(video_path, audio_path, output_path):
    """合并音频和视频,确保音视频时长一致"""
    audio_duration = get_audio_duration(audio_path)
    if audio_duration is None:
        print("无法获取音频时长")
        return None

    print(f"正在合成视频和音频,时长: {audio_duration}")
    ffmpeg_cmd = (
        f'ffmpeg -i "{video_path}" '
        f'-i "{audio_path}" -t {audio_duration} -c:a aac -vf "scale=720:1280" '
        f'-map 0:v -map 1:a -shortest -af "volume=2.0" "{output_path}"'
    )
    try:
        subprocess.check_call(ffmpeg_cmd, shell=True)
        print(f"音频与视频合成成功,输出视频路径:{output_path}")
    except subprocess.CalledProcessError:
        print("音频与视频合成失败")

def process_text_file():
    """读取文案文件并拆分文案"""
    print(f"正在读取文案文件,文案位置:{wenan_file_path}")
    try:
        with open(wenan_file_path, 'r', encoding='utf-8') as file:
            content = file.read()
        return content.split('|')
    except FileNotFoundError:
        print(f"文案文件 {wenan_file_path} 未找到")
        return []

def preprocess_subtitles(subtitle_path, video_duration):
    """读取并拆分字幕内容,按字符比例分配时间,仅保留问号"""
    processed_subtitles = []
    with open(subtitle_path, 'r', encoding='utf-8') as f:
        subtitles = f.read().strip().split('\n\n')

    for subtitle in subtitles:
        lines = subtitle.split('\n')
        if len(lines) >= 3:
            # 获取时间戳
            time_str = lines[1]
            start_time_str, end_time_str = time_str.split(' --> ')
            start_time_obj = datetime.strptime(start_time_str, '%H:%M:%S,%f')
            end_time_obj = datetime.strptime(end_time_str, '%H:%M:%S,%f')
            start_time = start_time_obj.hour * 3600 + start_time_obj.minute * 60 + start_time_obj.second + start_time_obj.microsecond / 1e6
            end_time = end_time_obj.hour * 3600 + end_time_obj.minute * 60 + end_time_obj.second + end_time_obj.microsecond / 1e6
            total_duration = end_time - start_time
            
            # 按逗号、句号、问号和叹号拆分为小句
            sentences = re.split(r'[,。?!]', lines[2])  # 按标点分割
            sentences = [s.strip() for s in sentences if s.strip()]  # 去除空句
            total_characters = sum(len(sentence) for sentence in sentences)
            
            # 为每一句分配时间,并确保显示时仅保留问号
            for sentence in sentences:
                # 移除除问号以外的标点符号
                sentence_display = re.sub(r'[,。!]', '', sentence)  # 删除逗号、句号、叹号,仅保留问号
               
                # 计算句子的显示时长
                sentence_duration = (len(sentence) / total_characters) * total_duration
                if start_time + sentence_duration > video_duration:
                    sentence_duration = video_duration - start_time  # 限制最后一个字幕时间不超过视频时长
               
                # 添加处理后的句子到字幕列表
                processed_subtitles.append((sentence_display, start_time, sentence_duration))
                start_time += sentence_duration  # 更新下一个句子的起始时间
               
                if start_time >= video_duration:  # 如果字幕时间已超出视频时长,停止
                    break

    return processed_subtitles

def add_subtitles_and_audio_to_video(video_path, subtitle_path, audio_path, output_path):
    print(f"正在为视频添加背景音和字幕。")
    width = 720
    height = 1280
    video = VideoFileClip(video_path, target_resolution=(height, width))
    video_duration = video.duration  # 获取视频总时长

    # 加载背景音频文件
    background_audio = AudioFileClip(audio_path).subclip(0, video_duration).volumex(1.5)

    # 混合背景音频与原视频音频
    original_audio = video.audio.volumex(1)
    mixed_audio = CompositeAudioClip([original_audio, background_audio])

    # 预处理字幕文件
    processed_subtitles = preprocess_subtitles(subtitle_path, video_duration)

    clips = []
    for text, start_time, duration in processed_subtitles:
        # 创建逐句字幕
        text_clip = TextClip(
            text, fontsize=40, color='white', bg_color='rgba(0, 0, 0, 0.5)', method='caption', font='Microsoft-YaHei-Bold-&-Microsoft-YaHei-UI-Bold', size=(450, None)
        ).set_duration(duration).set_start(start_time).set_position(('center', 800))
        clips.append(text_clip)

    # 将字幕和视频合成
    final_clip = CompositeVideoClip([video] + clips).set_audio(mixed_audio)

    # 输出最终视频
    final_clip.write_videofile(output_path, codec='libx264', audio_codec='aac')

def main():
    wenan_list = process_text_file()

    print(f"正在处理字段,总共 {len(wenan_list)} 个字段。")
    for wenan in wenan_list:
        params = {
            'text': wenan.strip(),
            'speaker': 'lulaoshi',
            'new': 1
        }

        timestamp = datetime.now().strftime('%Y%m%d%H%M%S')
        base_filename = f"{params['speaker']}{timestamp}"

        print(f"正在使用克隆声音,生成音频,文案:{wenan}")
        try:
            response = requests.get('http://localhost:9880/', params=params)
            response.raise_for_status()
        except requests.RequestException as e:
            print(f"请求失败: {e}")
            continue

        audio_filename = f'{base_filename}.wav'
        save_path = os.path.join(audio_save_folder, audio_filename)
        with open(save_path, 'wb') as f:
            f.write(response.content)

        adjusted_audio_path = os.path.join(audio_save_folder, f'{base_filename}_adjusted.wav')
        ffmpeg_volume_adjust_cmd = f'ffmpeg -i "{save_path}" -filter:a "volume=4" "{adjusted_audio_path}"'
        try:
            subprocess.check_call(ffmpeg_volume_adjust_cmd, shell=True)
            save_path = adjusted_audio_path
        except subprocess.CalledProcessError:
            print("音频音量调整失败,继续使用原始音频文件。")

        audio_duration = get_audio_duration(save_path)
        if audio_duration is None:
            continue

        selected_video_paths, total_video_duration = select_video_paths(audio_duration)
        video_path = concat_and_trim_videos(selected_video_paths, audio_duration, os.path.join(output_video_folder, base_filename))

        output_video_path = os.path.join(output_video_folder, f'{base_filename}.mp4')
        combine_audio_video(video_path, save_path, output_video_path)

        subtitle_path = r'D:\CosyVoice-win\音频输出\output.srt'
        background_audio_path = os.path.join(base_dir, 'music', 'xinghe.mp3')
        final_output_path = os.path.join(output_video_folder, f'{base_filename}_final.mp4')
        add_subtitles_and_audio_to_video(output_video_path, subtitle_path, background_audio_path, final_output_path)
        print(f"视频生成完成,请在{output_video_folder} 文件夹中查看视频名称为: {base_filename}_final.mp4")

if __name__ == "__main__":
    main()
```
  • WeChat Official Account

  • Scan to Visit Mobile Site

QQ|NIUBOYI ( 京ICP备17025393号 )|网站地图

GMT+8, 2026-10-30 19:46 , Processed in 0.383606 second(s), 37 queries .

POWERED BY NIUBOYIAI! 主理人:NIUBOYIAI  点亮哥

Copyright © 2001-2020, NIUBOYI AI.