File size: 10,291 Bytes
dd74184
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
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()