doc2video / main.py
zhao1977's picture
Upload 15 files
dd74184 verified
import argparse
import datetime
import os
import shutil
import gradio as gr
from threading import Thread
# 导入你的模块
from doc_split import doc_split_with_qwen_plus
from json2md import convert_json_file_to_md, process_markdown
from markdown_gather import merge_style_with_md_files, remove_trailing_dashes, insert_logo, remove_empty_lines, title_to_md
from marp2image import convert_md_files_to_png
from audio_generate_each_sentence import process_json_file, synthesize_md_to_speech
from srt_generate_for_each_sentence import generate_srt_from_audio
from calculate_durations_for_each_image import calculate_audio_durations
from movie_editor import images_to_video_with_durations
from audio2video import merge_audio_and_add_to_video
from srt2video import merge_video_and_subtitle
from theme_generate import theme_generate_with_qwen_plus
def main(args, progress_callback, log_callback):
try:
# 记录开始时间
start_time = datetime.datetime.now()
progress_callback(f"开始时间: {start_time.strftime('%Y年%m月%d日 %H时%M分%S秒')}")
log_callback(f"开始时间: {start_time.strftime('%Y年%m月%d日 %H时%M分%S秒')}")
# 生成带有时间戳的文件夹名
timestamp = start_time.strftime("%Y%m%d_%H%M%S")
material_folder = f"material_{timestamp}"
# 创建输出保存路径,将渲染素材复制到指定路径下
input_base_name = os.path.splitext(os.path.basename(args.input_txt_path))[0]
# 更新各个路径,使用新的文件夹名
args.json_path = os.path.join(material_folder, "json")
args.image_path = os.path.join(material_folder, "image")
args.audio_path = os.path.join(material_folder, "audio")
args.markdown_path = os.path.join(material_folder, "markdown")
args.srt_and_video_path = os.path.join(material_folder, "video")
# 创建必要的文件夹
folders_to_create = [
material_folder, args.markdown_path, args.json_path,
args.image_path, args.audio_path, args.srt_and_video_path
]
for folder in folders_to_create:
if not os.path.exists(folder):
os.makedirs(folder)
log_callback(f"创建文件夹: {folder}")
# 复制样式文件
if os.path.exists(args.input_style_path):
for filename in os.listdir(args.input_style_path):
full_path = os.path.join(args.input_style_path, filename)
if os.path.isfile(full_path):
shutil.copy2(full_path, args.markdown_path)
log_callback(f"样式文件已复制到: {args.markdown_path}")
else:
log_callback(f"警告: 输入样式文件夹路径 {args.input_style_path} 不存在,跳过复制操作。", is_warning=True)
# 通过API调用通义千问-Plus为输入文档生成文档标题
theme = theme_generate_with_qwen_plus(args.input_txt_path, args.title)
progress_callback(f"生成的文档标题: {theme}")
log_callback(f"生成的文档标题: {theme}")
# 通过API调用通义千问-Plus为输入文档划分段落,并为每一个段落生成一个段落标题
doc_split_with_qwen_plus(args.input_txt_path, args.json_path)
progress_callback(f"文档已分割并保存到: {args.json_path}")
log_callback(f"文档已分割并保存到: {args.json_path}")
# 总结各段落内容,保存为Markdown格式,并设置背景图片,可自行将style文件夹下的theme.png替换为自定义背景
for filename in os.listdir(args.json_path):
if filename.endswith('.json'):
json_file_path = os.path.join(args.json_path, filename)
convert_json_file_to_md(json_file_path, args.markdown_path, args.theme_path)
log_callback(f"转换 {json_file_path} 到 Markdown 格式并保存到: {args.markdown_path}")
# 将文档标题添加到Markdown文件开头作为标题页,并设置标题页背景,可自行将style文件夹下的title.png替换为自定义标题页背景
title_to_md(os.path.join(args.markdown_path, f'{input_base_name}.md'), theme, args.title_path)
log_callback(f"文档标题已添加到 Markdown 文件: {os.path.join(args.markdown_path, f'{input_base_name}.md')}")
# 删除空行,符合Marp格式
remove_empty_lines(os.path.join(args.markdown_path, f'{input_base_name}.md'))
log_callback(f"删除空行: {os.path.join(args.markdown_path, f'{input_base_name}.md')}")
# 添加阿里云logo。可自行替换为其他logo:将logo图片命名为logo.png,放到style文件夹下
insert_logo(os.path.join(args.markdown_path, f'{input_base_name}.md'), os.path.join(args.logo_path))
log_callback(f"插入logo到 Markdown 文件: {os.path.join(args.markdown_path, f'{input_base_name}.md')}")
process_markdown(os.path.join(args.markdown_path, f'{input_base_name}.md'))
log_callback(f"处理 Markdown 文件: {os.path.join(args.markdown_path, f'{input_base_name}.md')}")
# 定义并添加Marp样式文件。可查阅Marp官方文档自定义样式:将样式文件命名为style.md,放到style文件夹下
merge_style_with_md_files(args.markdown_path, args.markdown_style_path)
log_callback(f"合并样式文件到 Markdown 文件: {args.markdown_path}")
# 删除Markdown文件末尾的“---”,避免生成空白图片
remove_trailing_dashes(args.markdown_path)
log_callback(f"删除 Markdown 文件末尾的 '---': {args.markdown_path}")
# 使用Marp生成演示文稿图片
convert_md_files_to_png(os.path.join(args.markdown_path, f'{input_base_name}.md'), args.image_path)
log_callback(f"生成图片: {args.image_path}")
# 将各段落文档划分为若干句子,并通过API调用CosyVoice合成语音
process_json_file(os.path.join(args.json_path, f'{input_base_name}.json'), args.audio_path)
synthesize_md_to_speech(os.path.join(args.audio_path, input_base_name))
log_callback(f"合成语音: {args.audio_path}")
# 生成srt字幕文件
generate_srt_from_audio(os.path.join(args.audio_path, input_base_name), args.srt_and_video_path,
os.path.join(args.srt_and_video_path, input_base_name))
log_callback(f"生成SRT字幕文件: {args.srt_and_video_path}")
# 计算各段落的所有音频时长
durations = calculate_audio_durations(os.path.join(args.audio_path, input_base_name))
log_callback(f"计算音频时长: {durations}")
# 将所有图片剪辑为视频
images_to_video_with_durations(os.path.join(args.image_path, f'{input_base_name}'), args.srt_and_video_path,
durations, args.fps, input_base_name)
log_callback(f"生成视频: {args.srt_and_video_path}")
# 将音频文件嵌入视频
merge_audio_and_add_to_video(os.path.join(args.srt_and_video_path, f'{input_base_name}.mp4'),
os.path.join(args.audio_path, f'{input_base_name}'),
os.path.join(args.srt_and_video_path, f'{input_base_name}_with_audio.mp4'))
log_callback(f"合并音频到视频: {args.srt_and_video_path}")
# 将字幕文件嵌入视频
merge_video_and_subtitle(args.srt_and_video_path, input_base_name)
log_callback(f"合并字幕到视频: {args.srt_and_video_path}")
# 记录结束时间
end_time = datetime.datetime.now()
progress_callback(f"结束时间: {end_time.strftime('%Y年%m月%d日 %H时%M分%S秒')}")
log_callback(f"结束时间: {end_time.strftime('%Y年%m月%d日 %H时%M分%S秒')}")
# 计算总时间
elapsed_time = end_time - start_time
elapsed_hours, remainder = divmod(elapsed_time.total_seconds(), 3600)
elapsed_minutes, elapsed_seconds = divmod(remainder, 60)
elapsed_time_str = f"{int(elapsed_hours)}{int(elapsed_minutes)}{int(elapsed_seconds)}秒"
progress_callback(f"总时间: {elapsed_time_str}")
log_callback(f"总时间: {elapsed_time_str}")
except Exception as e:
log_callback(f"发生错误: {str(e)}", is_error=True)
progress_callback(f"发生错误: {str(e)}", is_error=True)
raise e
def run_conversion(input_txt_path, fps, title):
args = argparse.Namespace(
input_txt_path=input_txt_path,
input_style_path='./style',
markdown_style_path='./style/style.md',
logo_path='./style/logo.png',
theme_path='./style/theme.png',
title_path='./style/title.png',
json_path='./material/json',
image_path='./material/image',
audio_path='./material/audio',
markdown_path='./material/markdown',
srt_and_video_path='./material/video',
fps=int(fps),
title=title
)
log_text = []
def progress_callback(message, is_error=False):
log_text.append(message)
if is_error:
log_text.append(f"错误: {message}")
def log_callback(message, is_warning=False, is_error=False):
log_text.append(message)
if is_warning:
log_text.append(f"警告: {message}")
elif is_error:
log_text.append(f"错误: {message}")
def video_generation_done():
log_text.append("视频生成成功!")
main(args, progress_callback, log_callback)
video_generation_done()
return "\n".join(log_text)
def gradio_interface():
iface = gr.Interface(
fn=run_conversion,
inputs=[
gr.Textbox(lines=1, placeholder="输入文本路径", label="输入文本路径"),
gr.Number(value=30, label="帧率"),
gr.Textbox(lines=1, placeholder="视频标题", label="视频标题")
],
outputs=gr.Textbox(label="日志输出"),
title="文档生成视频",
description="将文档转换为带有音频和字幕的视频。",
live=False
)
iface.launch(share=True) # 添加 share=True
if __name__ == "__main__":
gradio_interface()