🎨 style: Format Code
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
import json
|
||||
import logging
|
||||
import re
|
||||
import json
|
||||
from typing import List
|
||||
|
||||
from loguru import logger
|
||||
from openai import OpenAI
|
||||
from openai import AzureOpenAI
|
||||
from openai import AzureOpenAI, OpenAI
|
||||
from openai.types.chat import ChatCompletion
|
||||
|
||||
from app.config import config
|
||||
@@ -295,7 +295,7 @@ Generate a script for a video, depending on the subject of the video.
|
||||
paragraphs = response.split("\n\n")
|
||||
|
||||
# Select the specified number of paragraphs
|
||||
selected_paragraphs = paragraphs[:paragraph_number]
|
||||
# selected_paragraphs = paragraphs[:paragraph_number]
|
||||
|
||||
# Join the selected paragraphs into a single string
|
||||
return "\n\n".join(paragraphs)
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import os
|
||||
import random
|
||||
from typing import List
|
||||
from urllib.parse import urlencode
|
||||
|
||||
import requests
|
||||
from typing import List
|
||||
from loguru import logger
|
||||
from moviepy.video.io.VideoFileClip import VideoFileClip
|
||||
|
||||
from app.config import config
|
||||
from app.models.schema import VideoAspect, VideoConcatMode, MaterialInfo
|
||||
from app.models.schema import MaterialInfo, VideoAspect, VideoConcatMode
|
||||
from app.utils import utils
|
||||
|
||||
requested_count = 0
|
||||
@@ -42,7 +42,7 @@ def search_videos_pexels(
|
||||
api_key = get_api_key("pexels_api_keys")
|
||||
headers = {
|
||||
"Authorization": api_key,
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36"
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36",
|
||||
}
|
||||
# Build URL
|
||||
params = {"query": search_term, "per_page": 20, "orientation": video_orientation}
|
||||
@@ -129,7 +129,7 @@ def search_videos_pixabay(
|
||||
for video_type in video_files:
|
||||
video = video_files[video_type]
|
||||
w = int(video["width"])
|
||||
h = int(video["height"])
|
||||
# h = int(video["height"])
|
||||
if w >= video_width:
|
||||
item = MaterialInfo()
|
||||
item.provider = "pixabay"
|
||||
@@ -169,7 +169,11 @@ def save_video(video_url: str, save_dir: str = "") -> str:
|
||||
with open(video_path, "wb") as f:
|
||||
f.write(
|
||||
requests.get(
|
||||
video_url, headers=headers, proxies=config.proxy, verify=False, timeout=(60, 240)
|
||||
video_url,
|
||||
headers=headers,
|
||||
proxies=config.proxy,
|
||||
verify=False,
|
||||
timeout=(60, 240),
|
||||
).content
|
||||
)
|
||||
|
||||
@@ -184,7 +188,7 @@ def save_video(video_url: str, save_dir: str = "") -> str:
|
||||
except Exception as e:
|
||||
try:
|
||||
os.remove(video_path)
|
||||
except Exception as e:
|
||||
except Exception:
|
||||
pass
|
||||
logger.warning(f"invalid video file: {video_path} => {str(e)}")
|
||||
return ""
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import ast
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
from app.config import config
|
||||
from app.models import const
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import json
|
||||
import os.path
|
||||
import re
|
||||
from timeit import default_timer as timer
|
||||
|
||||
from faster_whisper import WhisperModel
|
||||
from timeit import default_timer as timer
|
||||
from loguru import logger
|
||||
|
||||
from app.config import config
|
||||
@@ -88,7 +88,7 @@ def create(audio_file, subtitle_file: str = ""):
|
||||
is_segmented = True
|
||||
|
||||
seg_end = word.end
|
||||
# 如果包含标点,则断句
|
||||
# If it contains punctuation, then break the sentence.
|
||||
seg_text += word.word
|
||||
|
||||
if utils.str_contains_punctuation(word.word):
|
||||
@@ -246,7 +246,7 @@ def correct(subtitle_file, video_script):
|
||||
script_index += 1
|
||||
subtitle_index = next_subtitle_index
|
||||
|
||||
# 处理剩余的脚本行
|
||||
# Process the remaining lines of the script.
|
||||
while script_index < len(script_lines):
|
||||
logger.warning(f"Extra script line: {script_lines[script_index]}")
|
||||
if subtitle_index < len(subtitle_items):
|
||||
|
||||
@@ -3,7 +3,6 @@ import os.path
|
||||
import re
|
||||
from os import path
|
||||
|
||||
from edge_tts import SubMaker
|
||||
from loguru import logger
|
||||
|
||||
from app.config import config
|
||||
@@ -158,7 +157,7 @@ def get_video_materials(task_id, params, video_terms, audio_duration):
|
||||
|
||||
|
||||
def generate_final_videos(
|
||||
task_id, params, downloaded_videos, audio_file, subtitle_path
|
||||
task_id, params, downloaded_videos, audio_file, subtitle_path
|
||||
):
|
||||
final_video_paths = []
|
||||
combined_video_paths = []
|
||||
@@ -212,7 +211,7 @@ def start(task_id, params: VideoParams, stop_at: str = "video"):
|
||||
|
||||
if type(params.video_concat_mode) is str:
|
||||
params.video_concat_mode = VideoConcatMode(params.video_concat_mode)
|
||||
|
||||
|
||||
# 1. Generate script
|
||||
video_script = generate_script(task_id, params)
|
||||
if not video_script:
|
||||
@@ -246,7 +245,9 @@ def start(task_id, params: VideoParams, stop_at: str = "video"):
|
||||
sm.state.update_task(task_id, state=const.TASK_STATE_PROCESSING, progress=20)
|
||||
|
||||
# 3. Generate audio
|
||||
audio_file, audio_duration, sub_maker = generate_audio(task_id, params, video_script)
|
||||
audio_file, audio_duration, sub_maker = generate_audio(
|
||||
task_id, params, video_script
|
||||
)
|
||||
if not audio_file:
|
||||
sm.state.update_task(task_id, state=const.TASK_STATE_FAILED)
|
||||
return
|
||||
@@ -263,7 +264,9 @@ def start(task_id, params: VideoParams, stop_at: str = "video"):
|
||||
return {"audio_file": audio_file, "audio_duration": audio_duration}
|
||||
|
||||
# 4. Generate subtitle
|
||||
subtitle_path = generate_subtitle(task_id, params, video_script, sub_maker, audio_file)
|
||||
subtitle_path = generate_subtitle(
|
||||
task_id, params, video_script, sub_maker, audio_file
|
||||
)
|
||||
|
||||
if stop_at == "subtitle":
|
||||
sm.state.update_task(
|
||||
@@ -330,6 +333,5 @@ if __name__ == "__main__":
|
||||
video_subject="金钱的作用",
|
||||
voice_name="zh-CN-XiaoyiNeural-Female",
|
||||
voice_rate=1.0,
|
||||
|
||||
)
|
||||
start(task_id, params, stop_at="video")
|
||||
|
||||
@@ -4,7 +4,17 @@ import random
|
||||
from typing import List
|
||||
|
||||
from loguru import logger
|
||||
from moviepy import *
|
||||
from moviepy import (
|
||||
AudioFileClip,
|
||||
ColorClip,
|
||||
CompositeAudioClip,
|
||||
CompositeVideoClip,
|
||||
ImageClip,
|
||||
TextClip,
|
||||
VideoFileClip,
|
||||
afx,
|
||||
concatenate_videoclips,
|
||||
)
|
||||
from moviepy.video.tools.subtitles import SubtitlesClip
|
||||
from PIL import ImageFont
|
||||
|
||||
@@ -90,15 +100,15 @@ def combine_videos(
|
||||
video_ratio = video_width / video_height
|
||||
|
||||
if clip_ratio == video_ratio:
|
||||
# 等比例缩放
|
||||
# Resize proportionally
|
||||
clip = clip.resized((video_width, video_height))
|
||||
else:
|
||||
# 等比缩放视频
|
||||
# Resize proportionally
|
||||
if clip_ratio > video_ratio:
|
||||
# 按照目标宽度等比缩放
|
||||
# Resize proportionally based on the target width
|
||||
scale_factor = video_width / clip_w
|
||||
else:
|
||||
# 按照目标高度等比缩放
|
||||
# Resize proportionally based on the target height
|
||||
scale_factor = video_height / clip_h
|
||||
|
||||
new_width = int(clip_w * scale_factor)
|
||||
@@ -143,7 +153,7 @@ def combine_videos(
|
||||
|
||||
|
||||
def wrap_text(text, max_width, font="Arial", fontsize=60):
|
||||
# 创建字体对象
|
||||
# Create ImageFont
|
||||
font = ImageFont.truetype(font, fontsize)
|
||||
|
||||
def get_text_size(inner_text):
|
||||
@@ -257,12 +267,14 @@ def generate_video(
|
||||
elif params.subtitle_position == "top":
|
||||
_clip = _clip.with_position(("center", video_height * 0.05))
|
||||
elif params.subtitle_position == "custom":
|
||||
# 确保字幕完全在屏幕内
|
||||
margin = 10 # 额外的边距,单位为像素
|
||||
# Ensure the subtitle is fully within the screen bounds
|
||||
margin = 10 # Additional margin, in pixels
|
||||
max_y = video_height - _clip.h - margin
|
||||
min_y = margin
|
||||
custom_y = (video_height - _clip.h) * (params.custom_position / 100)
|
||||
custom_y = max(min_y, min(custom_y, max_y)) # 限制 y 值在有效范围内
|
||||
custom_y = max(
|
||||
min_y, min(custom_y, max_y)
|
||||
) # Constrain the y value within the valid range
|
||||
_clip = _clip.with_position(("center", custom_y))
|
||||
else: # center
|
||||
_clip = _clip.with_position(("center", "center"))
|
||||
@@ -273,14 +285,16 @@ def generate_video(
|
||||
[afx.MultiplyVolume(params.voice_volume)]
|
||||
)
|
||||
|
||||
if subtitle_path and os.path.exists(subtitle_path):
|
||||
generator = lambda text: TextClip(
|
||||
def make_textclip(text):
|
||||
return TextClip(
|
||||
text=text,
|
||||
font=font_path,
|
||||
font_size=params.font_size,
|
||||
)
|
||||
|
||||
if subtitle_path and os.path.exists(subtitle_path):
|
||||
sub = SubtitlesClip(
|
||||
subtitles=subtitle_path, encoding="utf-8", make_textclip=generator
|
||||
subtitles=subtitle_path, encoding="utf-8", make_textclip=make_textclip
|
||||
)
|
||||
text_clips = []
|
||||
for item in sub.subtitles:
|
||||
@@ -335,25 +349,26 @@ def preprocess_video(materials: List[MaterialInfo], clip_duration=4):
|
||||
|
||||
if ext in const.FILE_TYPE_IMAGES:
|
||||
logger.info(f"processing image: {material.url}")
|
||||
# 创建一个图片剪辑,并设置持续时间为3秒钟
|
||||
# Create an image clip and set its duration to 3 seconds
|
||||
clip = (
|
||||
ImageClip(material.url)
|
||||
.with_duration(clip_duration)
|
||||
.with_position("center")
|
||||
)
|
||||
# 使用resize方法来添加缩放效果。这里使用了lambda函数来使得缩放效果随时间变化。
|
||||
# 假设我们想要从原始大小逐渐放大到120%的大小。
|
||||
# t代表当前时间,clip.duration为视频总时长,这里是3秒。
|
||||
# 注意:1 表示100%的大小,所以1.2表示120%的大小
|
||||
# Apply a zoom effect using the resize method.
|
||||
# A lambda function is used to make the zoom effect dynamic over time.
|
||||
# The zoom effect starts from the original size and gradually scales up to 120%.
|
||||
# t represents the current time, and clip.duration is the total duration of the clip (3 seconds).
|
||||
# Note: 1 represents 100% size, so 1.2 represents 120% size.
|
||||
zoom_clip = clip.resized(
|
||||
lambda t: 1 + (clip_duration * 0.03) * (t / clip.duration)
|
||||
)
|
||||
|
||||
# 如果需要,可以创建一个包含缩放剪辑的复合视频剪辑
|
||||
# (这在您想要在视频中添加其他元素时非常有用)
|
||||
# Optionally, create a composite video clip containing the zoomed clip.
|
||||
# This is useful when you want to add other elements to the video.
|
||||
final_clip = CompositeVideoClip([zoom_clip])
|
||||
|
||||
# 输出视频
|
||||
# Output the video to a file.
|
||||
video_file = f"{material.url}.mp4"
|
||||
final_clip.write_videofile(video_file, fps=30, logger=None)
|
||||
final_clip.close()
|
||||
|
||||
@@ -2,11 +2,13 @@ import asyncio
|
||||
import os
|
||||
import re
|
||||
from datetime import datetime
|
||||
from typing import Union
|
||||
from xml.sax.saxutils import unescape
|
||||
|
||||
import edge_tts
|
||||
from edge_tts import SubMaker, submaker
|
||||
from edge_tts.submaker import mktimestamp
|
||||
from loguru import logger
|
||||
from edge_tts import submaker, SubMaker
|
||||
import edge_tts
|
||||
from moviepy.video.tools import subtitles
|
||||
|
||||
from app.config import config
|
||||
@@ -1054,7 +1056,7 @@ def is_azure_v2_voice(voice_name: str):
|
||||
|
||||
def tts(
|
||||
text: str, voice_name: str, voice_rate: float, voice_file: str
|
||||
) -> [SubMaker, None]:
|
||||
) -> Union[SubMaker, None]:
|
||||
if is_azure_v2_voice(voice_name):
|
||||
return azure_tts_v2(text, voice_name, voice_file)
|
||||
return azure_tts_v1(text, voice_name, voice_rate, voice_file)
|
||||
@@ -1072,7 +1074,7 @@ def convert_rate_to_percent(rate: float) -> str:
|
||||
|
||||
def azure_tts_v1(
|
||||
text: str, voice_name: str, voice_rate: float, voice_file: str
|
||||
) -> [SubMaker, None]:
|
||||
) -> Union[SubMaker, None]:
|
||||
voice_name = parse_voice_name(voice_name)
|
||||
text = text.strip()
|
||||
rate_str = convert_rate_to_percent(voice_rate)
|
||||
@@ -1095,7 +1097,7 @@ def azure_tts_v1(
|
||||
|
||||
sub_maker = asyncio.run(_do())
|
||||
if not sub_maker or not sub_maker.subs:
|
||||
logger.warning(f"failed, sub_maker is None or sub_maker.subs is None")
|
||||
logger.warning("failed, sub_maker is None or sub_maker.subs is None")
|
||||
continue
|
||||
|
||||
logger.info(f"completed, output file: {voice_file}")
|
||||
@@ -1105,7 +1107,7 @@ def azure_tts_v1(
|
||||
return None
|
||||
|
||||
|
||||
def azure_tts_v2(text: str, voice_name: str, voice_file: str) -> [SubMaker, None]:
|
||||
def azure_tts_v2(text: str, voice_name: str, voice_file: str) -> Union[SubMaker, None]:
|
||||
voice_name = is_azure_v2_voice(voice_name)
|
||||
if not voice_name:
|
||||
logger.error(f"invalid voice name: {voice_name}")
|
||||
|
||||
Reference in New Issue
Block a user