Skip to content

Commit

Permalink
添加批量混剪功能
Browse files Browse the repository at this point in the history
  • Loading branch information
wayne committed Jun 26, 2024
1 parent 26cb9d2 commit 74cdd69
Show file tree
Hide file tree
Showing 16 changed files with 370 additions and 59 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ hunjian_main.py
.DS_Store
*/.DS_Store
work/*
final/*
resource/*
1 change: 1 addition & 0 deletions final/README
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
最后合成的视频文件会放在这里
18 changes: 17 additions & 1 deletion locales/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": "视频资源不足,请添加资源后重试"






}
107 changes: 105 additions & 2 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand All @@ -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)
Expand Down Expand Up @@ -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')

Expand All @@ -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)
Expand All @@ -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,
Expand All @@ -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":
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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")
Expand All @@ -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')
Expand Down
18 changes: 1 addition & 17 deletions pages/01_auto_video.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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("<h1 style='text-align: center; font-weight:bold; font-family:comic sans ms; padding-top: 0rem;'> \
AI搞钱工具</h1>", unsafe_allow_html=True)
st.markdown("<h2 style='text-align: center;padding-top: 0rem;'>自动短视频生成器</h2>", unsafe_allow_html=True)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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:
Expand Down
1 change: 1 addition & 0 deletions resource/README
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
资源文件放在这里
2 changes: 1 addition & 1 deletion services/audio/tencent_recognition_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -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对应一个声道的识别结果
Expand Down
7 changes: 4 additions & 3 deletions services/captioning/captioning_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()

Expand Down
Empty file added services/hunjian/__init__.py
Empty file.
89 changes: 89 additions & 0 deletions services/hunjian/hunjian_service.py
Original file line number Diff line number Diff line change
@@ -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
Loading

0 comments on commit 74cdd69

Please sign in to comment.