diff --git a/app/models/schema.py b/app/models/schema.py index 6849735..730006d 100644 --- a/app/models/schema.py +++ b/app/models/schema.py @@ -94,6 +94,9 @@ class VideoParams: video_concat_mode: Optional[VideoConcatMode] = VideoConcatMode.random.value video_clip_duration: Optional[int] = 5 video_count: Optional[int] = 1 + + video_language: Optional[str] = "" # auto detect + voice_name: Optional[str] = VoiceNames[0] bgm_type: Optional[str] = "random" bgm_file: Optional[str] = "" diff --git a/app/services/llm.py b/app/services/llm.py index 764d2f3..dc70913 100644 --- a/app/services/llm.py +++ b/app/services/llm.py @@ -72,10 +72,10 @@ def _generate_response(prompt: str) -> str: if response: content = response.choices[0].message.content - return content + return content.replace("\n", "") -def generate_script(video_subject: str, language: str = "zh-CN", paragraph_number: int = 1) -> str: +def generate_script(video_subject: str, language: str = "", paragraph_number: int = 1) -> str: prompt = f""" # Role: Video Script Generator @@ -92,13 +92,12 @@ Generate a script for a video, depending on the subject of the video. 7. you must not mention the prompt, or anything about the script itself. also, never talk about the amount of paragraphs or lines. just write the script. 8. respond in the same language as the video subject. -## Output Example: -What is the meaning of life. This question has puzzled philosophers. - # Initialization: - video subject: {video_subject} - number of paragraphs: {paragraph_number} """.strip() + if language: + prompt += f"\n- language: {language}" final_script = "" logger.info(f"subject: {video_subject}") diff --git a/app/services/material.py b/app/services/material.py index ca7f92b..f9c108e 100644 --- a/app/services/material.py +++ b/app/services/material.py @@ -71,7 +71,7 @@ def search_videos(search_term: str, break return video_items except Exception as e: - logger.error(f"search videos failed: {e}") + logger.error(f"search videos failed: {str(e)}") return [] @@ -81,7 +81,7 @@ def save_video(video_url: str, save_dir: str) -> str: video_path = f"{save_dir}/{video_id}.mp4" proxies = config.pexels.get("proxies", None) with open(video_path, "wb") as f: - f.write(requests.get(video_url, proxies=proxies, verify=False).content) + f.write(requests.get(video_url, proxies=proxies, verify=False, timeout=(10, 180)).content) return video_path @@ -129,6 +129,6 @@ def download_videos(task_id: str, logger.info(f"total duration of downloaded videos: {total_duration} seconds, skip downloading more") break except Exception as e: - logger.error(f"failed to download video: {item}, {e}") + logger.error(f"failed to download video: {utils.to_json(item)} => {str(e)}") logger.success(f"downloaded {len(video_paths)} videos") return video_paths diff --git a/app/services/task.py b/app/services/task.py index af1881b..e454a26 100644 --- a/app/services/task.py +++ b/app/services/task.py @@ -48,7 +48,7 @@ def start(task_id, params: VideoParams): logger.info("\n\n## generating video script") video_script = params.video_script.strip() if not video_script: - video_script = llm.generate_script(video_subject=video_subject, language=language, + video_script = llm.generate_script(video_subject=video_subject, language=params.video_language, paragraph_number=paragraph_number) else: logger.debug(f"video script: \n{video_script}") diff --git a/app/services/voice.py b/app/services/voice.py index b8a5d5c..cbaca4d 100644 --- a/app/services/voice.py +++ b/app/services/voice.py @@ -26,7 +26,7 @@ def tts(text: str, voice_name: str, voice_file: str) -> [SubMaker, None]: logger.info(f"completed, output file: {voice_file}") return sub_maker except Exception as e: - logger.error(f"failed, error: {e}") + logger.error(f"failed, error: {str(e)}") return None @@ -61,29 +61,34 @@ def create_subtitle(sub_maker: submaker.SubMaker, text: str, subtitle_file: str) script_lines_without_space = [line.replace(" ", "") for line in script_lines] sub_line = "" - for _, (offset, sub) in enumerate(zip(sub_maker.offset, sub_maker.subs)): - _start_time, end_time = offset - if start_time < 0: - start_time = _start_time - sub = unescape(sub) - sub_line += sub - if sub_line == script_lines[sub_index] or sub_line == script_lines_without_space[sub_index]: - sub_text = script_lines[sub_index] - sub_index += 1 - line = formatter( - idx=sub_index, - start_time=start_time, - end_time=end_time, - sub_text=sub_text, - ) - # logger.debug(line.strip()) - sub_items.append(line) - start_time = -1.0 - sub_line = "" + try: + for _, (offset, sub) in enumerate(zip(sub_maker.offset, sub_maker.subs)): + _start_time, end_time = offset + if start_time < 0: + start_time = _start_time - with open(subtitle_file, "w", encoding="utf-8") as file: - file.write("\n".join(sub_items) + "\n") + sub = unescape(sub) + sub_line += sub + if sub_line == script_lines[sub_index] or sub_line == script_lines_without_space[sub_index]: + sub_text = script_lines[sub_index] + sub_index += 1 + line = formatter( + idx=sub_index, + start_time=start_time, + end_time=end_time, + sub_text=sub_text, + ) + # logger.debug(line.strip()) + sub_items.append(line) + start_time = -1.0 + sub_line = "" + + with open(subtitle_file, "w", encoding="utf-8") as file: + file.write("\n".join(sub_items) + "\n") + + except Exception as e: + logger.error(f"failed, error: {str(e)}") def get_audio_duration(sub_maker: submaker.SubMaker): diff --git a/webui/Main.py b/webui/Main.py index db1ba98..7d23394 100644 --- a/webui/Main.py +++ b/webui/Main.py @@ -95,9 +95,26 @@ with left_panel: st.write("**文案设置**") cfg.video_subject = st.text_input("视频主题(给定一个关键词,:red[AI自动生成]视频文案)", value=st.session_state['video_subject']).strip() + + video_languages = [ + ("自动判断(Auto detect)", ""), + ] + for lang in ["zh-CN", "zh-TW", "en-US"]: + video_languages.append((lang, lang)) + + selected_index = st.selectbox("生成视频脚本的语言(:blue[一般情况AI会自动根据你输入的主题语言输出])", + index=1, + options=range(len(video_languages)), # 使用索引作为内部选项值 + format_func=lambda x: video_languages[x][0] # 显示给用户的是标签 + ) + cfg.video_language = video_languages[selected_index][1] + + if cfg.video_language: + st.write(f"设置AI输出文案语言为: **:red[{cfg.video_language}]**") + if st.button("点击使用AI根据**主题**生成 【视频文案】 和 【视频关键词】", key="auto_generate_script"): with st.spinner("AI正在生成视频文案和关键词..."): - script = llm.generate_script(cfg.video_subject) + script = llm.generate_script(video_subject=cfg.video_subject, language=cfg.video_language) terms = llm.generate_terms(cfg.video_subject, script) st.toast('AI生成成功') st.session_state['video_script'] = script @@ -106,7 +123,7 @@ with left_panel: cfg.video_script = st.text_area( "视频文案(:blue[①可不填,使用AI生成 ②合理使用标点断句,有助于生成字幕])", value=st.session_state['video_script'], - height=280 + height=230 ) if st.button("点击使用AI根据**文案**生成【视频关键词】", key="auto_generate_terms"): if not cfg.video_script: