From 74cdd693ec32514675f6a40dead893c301112eb0 Mon Sep 17 00:00:00 2001 From: wayne Date: Wed, 26 Jun 2024 11:13:21 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=89=B9=E9=87=8F=E6=B7=B7?= =?UTF-8?q?=E5=89=AA=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 + final/README | 1 + locales/zh-CN.json | 18 ++- main.py | 107 ++++++++++++- pages/01_auto_video.py | 18 +-- resource/README | 1 + services/audio/tencent_recognition_service.py | 2 +- services/captioning/captioning_service.py | 7 +- services/hunjian/__init__.py | 0 services/hunjian/hunjian_service.py | 89 +++++++++++ services/resource/pexels_service.py | 7 +- services/resource/pixabay_service.py | 2 +- services/video/video_service.py | 144 +++++++++++++++--- tools/file_utils.py | 20 ++- tools/utils.py | 9 +- work/README | 2 +- 16 files changed, 370 insertions(+), 59 deletions(-) create mode 100644 final/README create mode 100644 resource/README create mode 100644 services/hunjian/__init__.py create mode 100644 services/hunjian/hunjian_service.py diff --git a/.gitignore b/.gitignore index 1ee4324..c4683ea 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,5 @@ hunjian_main.py .DS_Store */.DS_Store work/* +final/* +resource/* \ No newline at end of file diff --git a/final/README b/final/README new file mode 100644 index 0000000..38a01de --- /dev/null +++ b/final/README @@ -0,0 +1 @@ +最后合成的视频文件会放在这里 \ No newline at end of file diff --git a/locales/zh-CN.json b/locales/zh-CN.json index e9afe53..4fe8348 100644 --- a/locales/zh-CN.json +++ b/locales/zh-CN.json @@ -72,7 +72,23 @@ "Video normalize...": "视频初始化...", "Generate Video...": "生成视频...", "Add Subtitles...": "添加字幕...", - "Generate Video completed!": "生成视频完成!" + "Generate Video completed!": "生成视频完成!", + + + "Mix Video": "视频混剪区", + "Mix Video Scene": "视频片段", + "Video Scene Resource": "视频片段资源目录", + "Please input video scene resource folder path": "请输入视频片段资源目录路径", + "Video Scene Text": "视频片段文案路径", + "Please input video scene text path": "请输入视频片段文案路径", + "Add More Scene": "添加更多片段", + "Delete Extra Scene": "删除多余片段", + "One Line Text For One Scene,UTF-8 encoding": "一个场景所需的文字放在TXT文件中的一行,程序会随机从TXT文件中挑选一行文本作为该场景的文字内容,TXT文件必须UTF-8编码", + "You Need More Resource": "视频资源不足,请添加资源后重试" + + + + } \ No newline at end of file diff --git a/main.py b/main.py index 12eea02..e26dea8 100644 --- a/main.py +++ b/main.py @@ -2,11 +2,12 @@ import streamlit as st -from config.config import my_config +from config.config import my_config, audio_voices_azure, audio_voices_ali, audio_voices_tencent from services.audio.alitts_service import AliAudioService from services.audio.azure_service import AzureAudioService from services.audio.tencent_tts_service import TencentAudioService from services.captioning.captioning_service import generate_caption, add_subtitles +from services.hunjian.hunjian_service import concat_audio_list, get_audio_and_video_list from services.llm.azure_service import MyAzureService from services.llm.baichuan_service import MyBaichuanService from services.llm.baidu_qianfan_service import BaiduQianfanService @@ -16,7 +17,7 @@ from services.llm.tongyi_service import MyTongyiService from services.resource.pexels_service import PexelsService from services.resource.pixabay_service import PixabayService -from services.video.video_service import get_audio_duration, VideoService +from services.video.video_service import get_audio_duration, VideoService, VideoMixService from tools.tr_utils import tr from tools.utils import random_with_system_time, get_must_session_option, extent_audio @@ -32,6 +33,16 @@ audio_output_dir = os.path.abspath(audio_output_dir) +def get_audio_voices(): + selected_audio_provider = my_config['audio']['provider'] + if selected_audio_provider == 'Azure': + return audio_voices_azure + if selected_audio_provider == 'Ali': + return audio_voices_ali + if selected_audio_provider == 'Tencent': + return audio_voices_tencent + + def get_resource_provider(): resource_provider = my_config['resource']['provider'] print("resource_provider:", resource_provider) @@ -71,6 +82,8 @@ def get_audio_service(): def main_generate_video_content(): print("main_generate_video_content begin") topic = get_must_session_option('video_subject', "请输入要生成的主题") + if topic is None: + return video_language = st.session_state.get('video_language') video_length = st.session_state.get('video_length') @@ -97,6 +110,8 @@ def main_try_test_audio(): else: video_content = "你好,我是程序那些事" audio_voice = get_must_session_option("audio_voice", "请先设置配音语音") + if audio_voice is None: + return audio_service.read_with_ssml(video_content, audio_voice, audio_rate) @@ -111,7 +126,11 @@ def main_generate_video_dubbing(): audio_rate = get_audio_rate() video_content = get_must_session_option("video_content", "请先设置视频主题") + if video_content is None: + return audio_voice = get_must_session_option("audio_voice", "请先设置配音语音") + if audio_voice is None: + return audio_service.save_with_ssml(video_content, audio_output_file, audio_voice, @@ -121,6 +140,16 @@ def main_generate_video_dubbing(): print("main_generate_video_dubbing end") +def main_generate_video_dubbing_for_mix(): + print("main_generate_video_dubbing_for_mix begin") + audio_service = get_audio_service() + audio_rate = get_audio_rate() + audio_output_file_list, video_dir_list = get_audio_and_video_list(audio_service, audio_rate) + st.session_state["audio_output_file_list"] = audio_output_file_list + st.session_state["video_dir_list"] = video_dir_list + print("main_generate_video_dubbing_for_mix end") + + def get_audio_rate(): audio_provider = my_config['audio']['provider'] if audio_provider == "Azure": @@ -180,7 +209,11 @@ def main_get_video_resource(): print("main_get_video_resource begin") resource_service = get_resource_provider() query = get_must_session_option("video_keyword", "请先设置视频关键字") + if query is None: + return audio_file = get_must_session_option("audio_output_file", "请先生成配音文件") + if audio_file is None: + return audio_length = get_audio_duration(audio_file) print("audio_length:", audio_length) return_videos, total_length = resource_service.handle_video_resource(query, audio_length, 50, False) @@ -213,7 +246,11 @@ def main_generate_ai_video(video_generator): main_get_video_resource() st.write(tr("Video normalize...")) audio_file = get_must_session_option("audio_output_file", "请先生成配音文件") + if audio_file is None: + return video_list = get_must_session_option("return_videos", "请先生成视频资源文件") + if video_list is None: + return video_service = VideoService(video_list, audio_file) print("normalize video") @@ -226,6 +263,72 @@ def main_generate_ai_video(video_generator): if enable_subtitles: st.write(tr("Add Subtitles...")) subtitle_file = get_must_session_option('captioning_output', "请先生成字幕文件") + if subtitle_file is None: + return + + font_name = st.session_state.get('subtitle_font') + font_size = st.session_state.get('subtitle_font_size') + primary_colour = st.session_state.get('subtitle_color') + outline_colour = st.session_state.get('subtitle_border_color') + outline = st.session_state.get('subtitle_border_width') + alignment = st.session_state.get('subtitle_position') + add_subtitles(video_file, subtitle_file, + font_name=font_name, + font_size=font_size, + primary_colour=primary_colour, + outline_colour=outline_colour, + outline=outline, + alignment=alignment) + print("final file with subtitle:", video_file) + st.session_state["result_video_file"] = video_file + status.update(label=tr("Generate Video completed!"), state="complete", expanded=False) + + +def main_generate_ai_video_for_mix(video_generator): + print("main_generate_ai_video_for_fix begin:") + with video_generator: + st_area = st.status(tr("Generate Video in process..."), expanded=True) + with st_area as status: + st.write(tr("Generate Video Dubbing...")) + main_generate_video_dubbing_for_mix() + st.write(tr("Video normalize...")) + video_dir_list = get_must_session_option("video_dir_list", "请选择视频目录路径") + audio_file_list = get_must_session_option("audio_output_file_list", "请先生成配音文件列表") + + video_mix_servie = VideoMixService() + # 使用 zip() 函数遍历两个列表并获得配对 + i = 0 + audio_output_file_list = [] + final_video_file_list = [] + for video_dir, audio_file in zip(video_dir_list, audio_file_list): + print(f"Video Directory: {video_dir}, Audio File: {audio_file}") + if i == 0: + matching_videos, total_length = video_mix_servie.match_videos_from_dir(video_dir, + audio_file, True) + else: + matching_videos, total_length = video_mix_servie.match_videos_from_dir(video_dir, + audio_file, False) + i = i + 1 + audio_output_file_list.append(audio_file) + final_video_file_list.extend(matching_videos) + + final_audio_output_file = concat_audio_list(audio_output_file_list) + st.session_state['audio_output_file'] = final_audio_output_file + st.write(tr("Generate Video subtitles...")) + main_generate_subtitle() + video_service = VideoService(final_video_file_list, final_audio_output_file) + print("normalize video") + video_service.normalize_video() + st.write(tr("Generate Video...")) + video_file = video_service.generate_video_with_audio() + print("final file without subtitle:", video_file) + + enable_subtitles = st.session_state.get("enable_subtitles") + if enable_subtitles: + st.write(tr("Add Subtitles...")) + subtitle_file = get_must_session_option('captioning_output', "请先生成字幕文件") + if subtitle_file is None: + return font_name = st.session_state.get('subtitle_font') font_size = st.session_state.get('subtitle_font_size') diff --git a/pages/01_auto_video.py b/pages/01_auto_video.py index 0059260..edd3883 100644 --- a/pages/01_auto_video.py +++ b/pages/01_auto_video.py @@ -3,7 +3,7 @@ from config.config import my_config, save_config, languages, audio_languages, audio_voices_azure, transition_types, \ fade_list, audio_voices_ali, audio_voices_tencent from main import main_generate_video_content, main_generate_ai_video, main_generate_video_dubbing, \ - main_get_video_resource, main_generate_subtitle, main_try_test_audio + main_get_video_resource, main_generate_subtitle, main_try_test_audio, get_audio_voices from pages.common import common_ui from tools.tr_utils import tr @@ -70,17 +70,6 @@ def generate_video(video_generator): main_generate_ai_video(video_generator) -def get_audio_voices(): - selected_audio_provider = my_config['audio']['provider'] - if selected_audio_provider == 'Azure': - return audio_voices_azure - if selected_audio_provider == 'Ali': - return audio_voices_ali - if selected_audio_provider == 'Tencent': - return audio_voices_tencent - -test_mode = my_config["test_mode"] - st.markdown("

\ AI搞钱工具

", unsafe_allow_html=True) st.markdown("

自动短视频生成器

", unsafe_allow_html=True) @@ -215,8 +204,6 @@ def get_audio_voices(): with llm_columns[3]: st.selectbox(label=tr("video Transition effect duration"), key="video_transition_effect_duration", options=["1", "2"]) - # if test_mode: - # st.button(label=tr("Get Video Resource"), on_click=get_video_resource) # 字幕 subtitle_container = st.container(border=True) @@ -265,13 +252,10 @@ def get_audio_voices(): with llm_columns[3]: st.slider(label=tr("subtitle border width"), min_value=0.0, value=0.0, max_value=4.0, step=1.0, key="subtitle_border_width") - # if test_mode: - # st.button(label=tr("Generate subtitle"), on_click=generate_subtitle) # 生成视频 video_generator = st.container(border=True) with video_generator: - # st_status = st.status(tr("Generate Video in process..."), expanded=True) st.button(label=tr("Generate Video Button"), type="primary", on_click=generate_video, args=(video_generator,)) result_video_file = st.session_state.get("result_video_file") if result_video_file: diff --git a/resource/README b/resource/README new file mode 100644 index 0000000..89768c1 --- /dev/null +++ b/resource/README @@ -0,0 +1 @@ +资源文件放在这里 \ No newline at end of file diff --git a/services/audio/tencent_recognition_service.py b/services/audio/tencent_recognition_service.py index a84d64c..effd17f 100644 --- a/services/audio/tencent_recognition_service.py +++ b/services/audio/tencent_recognition_service.py @@ -61,7 +61,7 @@ def process(self, audioFile, language) -> List[TencentRecognitionResult]: if code != 0: print("recognize failed! request_id: ", request_id, " code: ", code, ", message: ", resp["message"]) st.toast("腾讯云语音识别失败", icon="⚠️") - st.stop() + return None print("request_id: ", request_id) # 一个channl_result对应一个声道的识别结果 diff --git a/services/captioning/captioning_service.py b/services/captioning/captioning_service.py index 283e8ca..5348ac3 100644 --- a/services/captioning/captioning_service.py +++ b/services/captioning/captioning_service.py @@ -6,7 +6,7 @@ from config.config import my_config from services.alinls.speech_process import AliRecognitionService from services.audio.tencent_recognition_service import TencentRecognitionService -from services.captioning.azure_captioning_service import Captioning +from services.captioning.common_captioning_service import Captioning import subprocess from tools.file_utils import generate_temp_filename @@ -53,8 +53,9 @@ def generate_caption(): print("selected_audio_provider: Tencent") tencent_service = TencentRecognitionService() result_list = tencent_service.process(get_session_option("audio_output_file"), - get_session_option("video_language")) - # print("result list:", result_list) + get_session_option("audio_language")) + if result_list is None: + return captioning._offline_results = result_list captioning.finish() diff --git a/services/hunjian/__init__.py b/services/hunjian/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/services/hunjian/hunjian_service.py b/services/hunjian/hunjian_service.py new file mode 100644 index 0000000..89fc751 --- /dev/null +++ b/services/hunjian/hunjian_service.py @@ -0,0 +1,89 @@ +import os +import subprocess + +import streamlit as st + +from tools.file_utils import random_line_from_text_file +from tools.utils import get_must_session_option, random_with_system_time, extent_audio, run_ffmpeg_command + +# 获取当前脚本的绝对路径 +script_path = os.path.abspath(__file__) + +# print("当前脚本的绝对路径是:", script_path) + +# 脚本所在的目录 +script_dir = os.path.dirname(script_path) +# 音频输出目录 +audio_output_dir = os.path.join(script_dir, "../../work") +audio_output_dir = os.path.abspath(audio_output_dir) + + +def get_session_video_scene_text(): + video_dir_list = [] + video_text_list = [] + for i in range(5): + if "video_scene_folder_" + str(i + 1) in st.session_state and st.session_state["video_scene_folder_" + str(i + 1)] is not None: + video_dir_list.append(st.session_state["video_scene_folder_" + str(i + 1)]) + video_text_list.append(st.session_state["video_scene_text_" + str(i + 1)]) + return video_dir_list, video_text_list + + +def get_video_scene_text_list(video_text_list): + video_scene_text_list = [] + for video_text in video_text_list: + if video_text is not None: + video_line = random_line_from_text_file(video_text) + video_scene_text_list.append(video_line) + return video_scene_text_list + + +def get_video_text_from_list(video_scene_text_list): + return " ".join(video_scene_text_list) + + +def get_audio_and_video_list(audio_service, audio_rate): + audio_output_file_list = [] + video_dir_list, video_text_list = get_session_video_scene_text() + video_scene_text_list = get_video_scene_text_list(video_text_list) + audio_voice = get_must_session_option("audio_voice", "请先设置配音语音") + i = 0 + for video_scene_text in video_scene_text_list: + temp_file_name = str(random_with_system_time()) + str(i) + i = i + 1 + audio_output_file = os.path.join(audio_output_dir, str(temp_file_name) + ".wav") + audio_service.save_with_ssml(video_scene_text, + audio_output_file, + audio_voice, + audio_rate) + extent_audio(audio_output_file, 1) + audio_output_file_list.append(audio_output_file) + return audio_output_file_list, video_dir_list + + +def get_video_text(): + video_dir_list, video_text_list = get_session_video_scene_text() + video_scene_text_list = get_video_scene_text_list(video_text_list) + return get_video_text_from_list(video_scene_text_list) + + +def concat_audio_list(audio_output_file_list): + temp_output_file_name = os.path.join(audio_output_dir, str(random_with_system_time()) + ".wav") + concat_audio_file = os.path.join(audio_output_dir, "concat_audio_file.txt") + with open(concat_audio_file, 'w', encoding='utf-8') as f: + for audio_file in audio_output_file_list: + f.write("file '{}'\n".format(os.path.abspath(audio_file))) + # 调用ffmpeg来合并音频 + # 注意:这里假设ffmpeg在你的PATH中,否则你需要提供ffmpeg的完整路径 + command = [ + 'ffmpeg', + '-f', 'concat', + '-safe', '0', + '-i', concat_audio_file, + '-c', 'copy', # 如果可能,直接复制流而不是重新编码 + temp_output_file_name + ] + run_ffmpeg_command(command) + # 完成后,删除临时文件(如果你不再需要它) + os.remove(concat_audio_file) + print(f"Audio files have been merged into {temp_output_file_name}") + return temp_output_file_name diff --git a/services/resource/pexels_service.py b/services/resource/pexels_service.py index 8e86489..da2039d 100644 --- a/services/resource/pexels_service.py +++ b/services/resource/pexels_service.py @@ -18,7 +18,7 @@ script_dir = os.path.dirname(script_path) # workdir -workdir = os.path.join(script_dir, "../../work") +workdir = os.path.join(script_dir, "../../resource") workdir = os.path.abspath(workdir) @@ -61,11 +61,6 @@ def match_videos(self, video_data, audio_length, if total_length < audio_length: video_files = video["video_files"] for video_file in video_files: - # fps转换 - # video_fps = video_file["fps"] - # video_duration = video_duration * video_fps / self.fps - # if video_duration > self.video_segment_max_length: - # video_duration = self.video_segment_max_length if exact_match: if video_file["width"] == self.width and video_file["height"] == self.height: video_url = video_file['link'] diff --git a/services/resource/pixabay_service.py b/services/resource/pixabay_service.py index 993eefc..e6dc99b 100644 --- a/services/resource/pixabay_service.py +++ b/services/resource/pixabay_service.py @@ -17,7 +17,7 @@ script_dir = os.path.dirname(script_path) # workdir -workdir = os.path.join(script_dir, "../../work") +workdir = os.path.join(script_dir, "../../resource") workdir = os.path.abspath(workdir) diff --git a/services/video/video_service.py b/services/video/video_service.py index 8789d40..035a74b 100644 --- a/services/video/video_service.py +++ b/services/video/video_service.py @@ -1,5 +1,7 @@ import itertools +import math import os +import random import re import subprocess from typing import List @@ -9,7 +11,8 @@ from services.video.texiao_service import gen_filter from tools.file_utils import generate_temp_filename -from tools.utils import random_with_system_time, run_ffmpeg_command +from tools.tr_utils import tr +from tools.utils import random_with_system_time, run_ffmpeg_command, extent_audio # 获取当前脚本的绝对路径 script_path = os.path.abspath(__file__) @@ -19,9 +22,15 @@ # 脚本所在的目录 script_dir = os.path.dirname(script_path) # 视频出目录 -video_output_dir = os.path.join(script_dir, "../../work") +video_output_dir = os.path.join(script_dir, "../../final") video_output_dir = os.path.abspath(video_output_dir) +# work目录 +work_output_dir = os.path.join(script_dir, "../../work") +work_output_dir = os.path.abspath(work_output_dir) + +DEFAULT_DURATION = 5 + def get_audio_duration(audio_file): """ @@ -31,6 +40,7 @@ def get_audio_duration(audio_file): """ # 使用ffmpeg命令获取音频信息 cmd = ['ffmpeg', '-i', audio_file] + print(" ".join(cmd)) result = subprocess.run(cmd, capture_output=True) # 解析输出,找到时长信息 @@ -186,6 +196,88 @@ def add_background_music(video_file, audio_file, bgm_volume=0.5): os.renames(output_file, video_file) +class VideoMixService: + def __init__(self): + self.fps = st.session_state["video_fps"] + self.segment_min_length = st.session_state["video_segment_min_length"] + self.segment_max_length = st.session_state["video_segment_max_length"] + self.target_width, self.target_height = st.session_state["video_size"].split('x') + self.target_width = int(self.target_width) + self.target_height = int(self.target_height) + + self.enable_background_music = st.session_state["enable_background_music"] + self.background_music = st.session_state["background_music"] + self.background_music_volume = st.session_state["background_music_volume"] + + self.enable_video_transition_effect = st.session_state["enable_video_transition_effect"] + self.video_transition_effect_duration = st.session_state["video_transition_effect_duration"] + self.video_transition_effect_type = st.session_state["video_transition_effect_type"] + self.video_transition_effect_value = st.session_state["video_transition_effect_value"] + self.default_duration = DEFAULT_DURATION + if DEFAULT_DURATION < self.segment_min_length: + self.default_duration = self.segment_min_length + + def match_videos_from_dir(self, video_dir, audio_file, is_head=False): + matching_videos = [] + # 获取音频时长 + audio_duration = get_audio_duration(audio_file) + print("音频时长:" + str(audio_duration)) + + # 获取媒体文件夹中的所有图片和视频文件 + media_files = [os.path.join(video_dir, f) for f in os.listdir(video_dir) if + f.lower().endswith(('.jpg', '.jpeg', '.png', '.mp4', '.mov'))] + + # 随机排序媒体文件 + random.shuffle(media_files) + + # 确保有视频文件在列表中 + video_files = [os.path.join(video_dir, f) for f in media_files if f.lower().endswith(('.mp4', '.mov'))] + if video_files: + # 从视频文件中随机选择一个 + random_video = random.choice(video_files) + # 将随机选择的视频文件从列表中移除 + media_files.remove(random_video) + # 将随机选择的视频文件添加到列表的开头 + media_files.insert(0, random_video) + + total_length = 0 + i = 0 + for video_file in media_files: + if video_file.lower().endswith(('.jpg', '.jpeg', '.png')): + video_duration = self.default_duration + else: + video_duration = get_video_duration(video_file) + # 短的视频拉长到最小值 + if video_duration < self.segment_min_length: + video_duration = self.segment_min_length + if video_duration > self.segment_max_length: + video_duration = self.segment_max_length + + print("total length:", total_length, "audio length:", audio_duration) + if total_length < audio_duration: + if self.enable_video_transition_effect: + if i == 0 and is_head: + total_length = total_length + video_duration + else: + total_length = total_length + video_duration - float( + self.video_transition_effect_duration) + else: + total_length = total_length + video_duration + matching_videos.append(video_file) + i = i + 1 + else: + extend_length = audio_duration - total_length + extend_length = int(math.ceil(extend_length)) + if extend_length > 0: + extent_audio(audio_file, extend_length) + break + print("total length:", total_length, "audio length:", audio_duration) + if total_length < audio_duration: + st.toast(tr("You Need More Resource"), icon="⚠️") + st.stop() + return matching_videos, total_length + + class VideoService: def __init__(self, video_list, audio_file): self.video_list = video_list @@ -205,33 +297,51 @@ def __init__(self, video_list, audio_file): self.video_transition_effect_duration = st.session_state["video_transition_effect_duration"] self.video_transition_effect_type = st.session_state["video_transition_effect_type"] self.video_transition_effect_value = st.session_state["video_transition_effect_value"] + self.default_duration = DEFAULT_DURATION + if DEFAULT_DURATION < self.seg_min_duration: + self.default_duration = self.seg_min_duration - def normalize_video(self, default_duration=5): + def normalize_video(self): return_video_list = [] for media_file in self.video_list: # 如果当前文件是图片,添加转换为视频的命令 if media_file.lower().endswith(('.jpg', '.jpeg', '.png')): - output_name = generate_temp_filename(media_file, ".mp4") + output_name = generate_temp_filename(media_file, ".mp4", work_output_dir) # 判断图片的纵横比和 img_width, img_height = get_image_info(media_file) if img_width / img_height > self.target_width / self.target_height: # 转换图片为视频片段 图片的视频帧率必须要跟视频的帧率一样,否则可能在最后的合并过程中导致 合并过后的视频过长 - ffmpeg_cmd = f"ffmpeg -loop 1 -i {media_file} -c:v h264 -t {default_duration} -r {self.fps} -vf 'scale=-1:{self.target_height}:force_original_aspect_ratio=1,crop={self.target_width}:{self.target_height}:(ow-iw)/2:(oh-ih)/2' -y {output_name}" + # ffmpeg_cmd = f"ffmpeg -loop 1 -i '{media_file}' -c:v h264 -t {self.default_duration} -r {self.fps} -vf 'scale=-1:{self.target_height}:force_original_aspect_ratio=1,crop={self.target_width}:{self.target_height}:(ow-iw)/2:(oh-ih)/2' -y {output_name}" + ffmpeg_cmd = [ + 'ffmpeg', + '-loop', '1', + '-i', media_file, + '-c:v', 'h264', + '-t', str(self.default_duration), + '-r', str(self.fps), + '-vf', f'scale=-1:{self.target_height}:force_original_aspect_ratio=1,crop={self.target_width}:{self.target_height}:(ow-iw)/2:(oh-ih)/2' + '-y', output_name] else: - ffmpeg_cmd = f"ffmpeg -loop 1 -i {media_file} -c:v h264 -t {default_duration} -r {self.fps} -vf 'scale={self.target_width}:-1:force_original_aspect_ratio=1,crop={self.target_width}:{self.target_height}:(ow-iw)/2:(oh-ih)/2' -y {output_name}" - print(ffmpeg_cmd) - run_ffmpeg_command(ffmpeg_cmd) - # os.remove(media_file) + # ffmpeg_cmd = f"ffmpeg -loop 1 -i '{media_file}' -c:v h264 -t {self.default_duration} -r {self.fps} -vf 'scale={self.target_width}:-1:force_original_aspect_ratio=1,crop={self.target_width}:{self.target_height}:(ow-iw)/2:(oh-ih)/2' -y {output_name}" + ffmpeg_cmd = [ + 'ffmpeg', + '-loop', '1', + '-i', media_file, + '-c:v', 'h264', + '-t', str(self.default_duration), + '-r', str(self.fps), + '-vf', + f'scale={self.target_width}:-1:force_original_aspect_ratio=1,crop={self.target_width}:{self.target_height}:(ow-iw)/2:(oh-ih)/2' + '-y', output_name] + print(" ".join(ffmpeg_cmd)) + subprocess.run(ffmpeg_cmd, check=True, capture_output=True) return_video_list.append(output_name) else: # 当前文件是视频文件 video_duration = get_video_duration(media_file) video_width, video_height = get_video_info(media_file) - # video_fps = get_video_fps(media_file) - # 转换之后的duration - # video_duration = video_duration * float(self.fps) / float(video_fps) - output_name = generate_temp_filename(media_file) + output_name = generate_temp_filename(media_file, new_directory=work_output_dir) if self.seg_min_duration > video_duration: # 需要扩展视频 stretch_factor = float(self.seg_min_duration) / float(video_duration) # 拉长比例 @@ -341,10 +451,10 @@ def normalize_video(self, default_duration=5): print(" ".join(command)) run_ffmpeg_command(command) # 重命名最终的文件 - if os.path.exists(output_name): - os.remove(media_file) - os.renames(output_name, media_file) - return_video_list.append(media_file) + # if os.path.exists(output_name): + # os.remove(media_file) + # os.renames(output_name, media_file) + return_video_list.append(output_name) self.video_list = return_video_list return return_video_list diff --git a/tools/file_utils.py b/tools/file_utils.py index 6ea2e44..adb960c 100644 --- a/tools/file_utils.py +++ b/tools/file_utils.py @@ -1,4 +1,5 @@ import os +import random import re import string @@ -6,6 +7,11 @@ from PIL.Image import Image +def random_line(afile): + lines = afile.readlines() + return random.choice(lines) + + def read_yaml(file_name): with open(file_name, 'r', encoding='utf-8') as f: data = yaml.safe_load(f) @@ -49,7 +55,7 @@ def insert_newline(text): return re.sub(pattern, r'\1\n', text) -def generate_temp_filename(original_filepath, new_ext=""): +def generate_temp_filename(original_filepath, new_ext="", new_directory=None): # 获取文件的目录、文件名和扩展名 directory, filename_with_ext = os.path.split(original_filepath) filename, ext = os.path.splitext(filename_with_ext) @@ -61,7 +67,10 @@ def generate_temp_filename(original_filepath, new_ext=""): new_filename = filename + '.temp' + ext # 如果你需要完整的路径,可以使用os.path.join - new_filepath = os.path.join(directory, new_filename) + if new_directory: + new_filepath = os.path.join(new_directory, new_filename) + else: + new_filepath = os.path.join(directory, new_filename) return new_filepath @@ -104,3 +113,10 @@ def download_file_from_url(url, output_path): except requests.exceptions.RequestException as e: print(f"发生了一个错误: {e}") + + +def random_line_from_text_file(text_file): + # 从文本文件中随机读取文本 + with open(text_file, 'r', encoding='utf-8') as file: + line = random_line(file) + return line.strip() diff --git a/tools/utils.py b/tools/utils.py index 5dce02b..88766bc 100644 --- a/tools/utils.py +++ b/tools/utils.py @@ -6,19 +6,12 @@ from typing import Optional from tools.file_utils import generate_temp_filename -from tools.tr_utils import tr def generate_operator(): operators = ['+', '-'] return random.choice(operators) - -def random_line(afile): - lines = afile.readlines() - return random.choice(lines) - - def random_with_system_time(): system_time = int(time.time() * 1000) random_seed = (system_time + random.randint(0, 10000)) @@ -122,7 +115,7 @@ def must_have_value(option: str, msg: str) -> Optional[str]: def run_ffmpeg_command(command): try: - result = subprocess.run(command, capture_output=True, text=True) + result = subprocess.run(command, capture_output=True, check=True, text=True) if result.returncode != 0: print(f"FFmpeg returned an error: {result.stderr}") else: diff --git a/work/README b/work/README index bfa12b7..38bdd17 100644 --- a/work/README +++ b/work/README @@ -1 +1 @@ -work content put here \ No newline at end of file +这里存放的是一些中间视频或者音频文件 \ No newline at end of file