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()