Spaces:
Running
Running
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() |