增加了各种字体

This commit is contained in:
Larkspur-Wang 2025-04-20 21:35:15 +08:00
parent 64d0cc2966
commit b90c241a71
15 changed files with 546 additions and 64 deletions

View File

@ -99,24 +99,29 @@ class VideoParams(BaseModel):
text_background_color: Union[bool, str] = True
# 艺术字体相关参数
art_font_enabled: Optional[bool] = False
art_font_type: Optional[str] = "normal" # normal, shadow, outline, 3d, etc.
art_font_enabled: Optional[bool] = True
art_font_type: Optional[str] = "3d" # normal, shadow, outline, 3d, etc.
art_font_background: Optional[str] = "none" # none, red, blue, etc.
subtitle_position: Optional[str] = "custom" # top, bottom, center, custom
custom_position: float = 65.0
# 标题贴纸相关参数
title_sticker_enabled: Optional[bool] = False
title_sticker_enabled: Optional[bool] = True
title_sticker_text: Optional[str] = ""
title_sticker_font: Optional[str] = "STHeitiMedium.ttc"
title_sticker_font_size: Optional[int] = 80
title_sticker_style: Optional[str] = "rainbow" # rainbow, neon, gradient, etc.
title_sticker_font_size: Optional[int] = 160
title_sticker_style: Optional[str] = "metallic" # rainbow, neon, gradient, metallic, etc.
title_sticker_background_enabled: Optional[bool] = True # 是否启用背景
title_sticker_background: Optional[str] = "rounded_rect" # none, rounded_rect, rect, etc.
title_sticker_background_color: Optional[str] = "#000000"
title_sticker_border: Optional[bool] = True
title_sticker_border_color: Optional[str] = "#FFFFFF"
title_sticker_position: Optional[str] = "custom" # upper_middle, middle, lower_middle, custom
title_sticker_custom_position: Optional[float] = 15.0 # 自定义位置,表示离顶部的百分比
font_size: int = 60
stroke_color: Optional[str] = "#000000"
stroke_width: float = 1.5
stroke_color: Optional[str] = "#FFFFFF"
stroke_width: float = 4.0
n_threads: Optional[int] = 2
paragraph_number: Optional[int] = 1

View File

@ -183,7 +183,7 @@ def combine_videos(
return combined_video_path
def create_title_sticker(text, font, font_size, style, background, background_color, border, border_color, size):
def create_title_sticker(text, font, font_size, style, background, background_color, border, border_color, size, background_enabled=True):
"""
创建标题贴纸
@ -196,6 +196,7 @@ def create_title_sticker(text, font, font_size, style, background, background_co
:param border: 是否有边框
:param border_color: 边框颜色
:param size: 视频尺寸
:param background_enabled: 是否启用背景
:return: ImageClip对象
"""
if not text:
@ -224,8 +225,8 @@ def create_title_sticker(text, font, font_size, style, background, background_co
img = Image.new('RGBA', (sticker_width, sticker_height), (0, 0, 0, 0))
draw = ImageDraw.Draw(img)
# 绘制背景
if background != "none":
# 绘制背景(如果启用了背景)
if background != "none" and background_enabled:
# 确保背景颜色完全不透明
if background_color.startswith('#') and len(background_color) == 7:
bg_color = background_color + 'ff' # 添加不透明度
@ -719,15 +720,33 @@ def generate_video(
background_color=params.title_sticker_background_color,
border=params.title_sticker_border,
border_color=params.title_sticker_border_color,
size=(video_width, video_height)
size=(video_width, video_height),
background_enabled=params.title_sticker_background_enabled
)
# 设置标题贴纸位置(顶部中间)
# 设置标题贴纸位置
if title_sticker:
title_sticker = title_sticker.with_position(("center", video_height * 0.05))
# 根据用户选择的位置设置标题贴纸位置
if params.title_sticker_position == "upper_middle":
# 上方中间
title_sticker = title_sticker.with_position(("center", video_height * 0.10))
elif params.title_sticker_position == "middle":
# 正中间
title_sticker = title_sticker.with_position(("center", "center"))
elif params.title_sticker_position == "lower_middle":
# 下方中间
title_sticker = title_sticker.with_position(("center", video_height * 0.80))
elif params.title_sticker_position == "custom":
# 自定义位置
custom_y = video_height * (params.title_sticker_custom_position / 100)
title_sticker = title_sticker.with_position(("center", custom_y))
else:
# 默认位置(上方中间)
title_sticker = title_sticker.with_position(("center", video_height * 0.10))
title_sticker = title_sticker.with_duration(video_clip.duration)
video_elements.append(title_sticker)
logger.info(f"Added title sticker: {params.title_sticker_text}")
logger.info(f"Added title sticker: {params.title_sticker_text} at position {params.title_sticker_position}")
# 添加字幕
if subtitle_path and os.path.exists(subtitle_path):
@ -819,6 +838,344 @@ def preprocess_video(materials: List[MaterialInfo], clip_duration=4):
return materials
def create_preview_image(text, font_path, font_size, style, background_type, background_color, border, border_color, background_enabled=True, is_title=False):
"""
创建预览图像
:param text: 文本内容
:param font_path: 字体路径
:param font_size: 字体大小
:param style: 样式normal, shadow, outline, 3d, neon, metallic, rainbow, gradient
:param background_type: 背景类型none, rounded_rect, rect等
:param background_color: 背景颜色
:param border: 是否有边框
:param border_color: 边框颜色
:param background_enabled: 是否启用背景
:param is_title: 是否是标题影响样式处理
:return: 临时图像文件路径
"""
# 设置预览图像宽度
preview_width = 400
# 创建字体对象
font_obj = ImageFont.truetype(font_path, font_size)
# 计算文本尺寸
left, top, right, bottom = font_obj.getbbox(text)
text_width = right - left
text_height = bottom - top
# 设置图像尺寸(比文本略大)
padding_x = int(text_width * 0.3)
padding_y = int(text_height * 0.5)
img_width = min(preview_width, text_width + padding_x * 2)
img_height = text_height + padding_y * 2
# 确保文本在背景中垂直居中
text_y_position = (img_height - text_height) // 2
# 创建透明背景图像
img = Image.new('RGBA', (img_width, img_height), (0, 0, 0, 0))
draw = ImageDraw.Draw(img)
# 绘制背景(如果启用了背景)
if background_type != "none" and background_enabled:
# 确保背景颜色完全不透明
if background_color.startswith('#') and len(background_color) == 7:
bg_color = background_color + 'ff' # 添加不透明度
else:
bg_color = background_color
if background_type == "rounded_rect":
# 绘制圆角矩形
radius = int(img_height * 0.3) # 圆角半径
draw.rounded_rectangle(
[(0, 0), (img_width, img_height)],
radius=radius,
fill=bg_color
)
elif background_type == "rect":
# 绘制矩形
draw.rectangle(
[(0, 0), (img_width, img_height)],
fill=bg_color
)
# 根据样式绘制文本
if is_title and style == "rainbow":
# 彩虹渐变文字
rainbow_colors = ["#FF0000", "#FF7F00", "#FFFF00", "#00FF00", "#0000FF", "#4B0082", "#9400D3"]
# 创建渐变色文本
gradient_img = Image.new('RGBA', (text_width, text_height), (0, 0, 0, 0))
gradient_draw = ImageDraw.Draw(gradient_img)
# 计算每个字符的颜色
for i, char in enumerate(text):
color_idx = i % len(rainbow_colors)
char_width = font_obj.getbbox(char)[2] - font_obj.getbbox(char)[0]
gradient_draw.text((left + i * char_width, 0), char, font=font_obj, fill=rainbow_colors[color_idx])
# 添加描边
if border:
for offset_x, offset_y in [(1, 0), (-1, 0), (0, 1), (0, -1)]:
draw.text((padding_x + offset_x, text_y_position + offset_y), text, font=font_obj, fill=border_color)
# 将渐变文本粘贴到主图像
img.paste(gradient_img, (padding_x, text_y_position), gradient_img)
elif is_title and style == "neon":
# 霓虹灯效果
glow_color = "#FF4500" # 橙红色
outer_glow_color = "#FFFF00" # 黄色外发光
# 添加外发光效果
for offset in range(3, 0, -1):
alpha = 100 - offset * 30
glow_alpha = max(0, alpha)
glow_color_with_alpha = glow_color[0:7] + format(glow_alpha, '02x')
for dx, dy in [(ox, oy) for ox in range(-offset, offset+1) for oy in range(-offset, offset+1)]:
draw.text((padding_x + dx, text_y_position + dy), text, font=font_obj, fill=glow_color_with_alpha)
# 添加内发光
draw.text((padding_x, text_y_position), text, font=font_obj, fill=outer_glow_color)
# 添加主文本
draw.text((padding_x, text_y_position), text, font=font_obj, fill=glow_color)
# 应用模糊效果增强霓虹感
img = img.filter(ImageFilter.GaussianBlur(1))
elif is_title and style == "gradient":
# 渐变效果
start_color = (255, 0, 0) # 红色
end_color = (0, 0, 255) # 蓝色
# 创建渐变色文本
gradient_img = Image.new('RGBA', (text_width, text_height), (0, 0, 0, 0))
gradient_draw = ImageDraw.Draw(gradient_img)
# 绘制渐变背景
for y in range(text_height):
r = int(start_color[0] + (end_color[0] - start_color[0]) * y / text_height)
g = int(start_color[1] + (end_color[1] - start_color[1]) * y / text_height)
b = int(start_color[2] + (end_color[2] - start_color[2]) * y / text_height)
gradient_draw.line([(0, y), (text_width, y)], fill=(r, g, b, 255))
# 创建文本蒙版
mask = Image.new('L', (text_width, text_height), 0)
mask_draw = ImageDraw.Draw(mask)
mask_draw.text((0, 0), text, font=font_obj, fill=255)
# 应用蒙版到渐变图像
gradient_text = Image.new('RGBA', (text_width, text_height), (0, 0, 0, 0))
gradient_text.paste(gradient_img, (0, 0), mask)
# 添加描边
if border:
for offset_x, offset_y in [(1, 0), (-1, 0), (0, 1), (0, -1)]:
draw.text((padding_x + offset_x, text_y_position + offset_y), text, font=font_obj, fill=border_color)
# 将渐变文本粘贴到主图像
img.paste(gradient_text, (padding_x, text_y_position), gradient_text)
elif style == "shadow":
# 阴影效果
shadow_offset = max(2, font_size // 20) # 阴影偏移量
draw.text((padding_x + shadow_offset, text_y_position + shadow_offset), text, font=font_obj, fill=(0, 0, 0, 160))
draw.text((padding_x, text_y_position), text, font=font_obj, fill="#FFFFFF")
elif style == "outline":
# 描边效果
outline_size = max(2, font_size // 25) # 描边大小
# 绘制描边(四个方向)
for dx, dy in [(-1,-1), (-1,1), (1,-1), (1,1), (-outline_size,0), (outline_size,0), (0,-outline_size), (0,outline_size)]:
draw.text((padding_x + dx, text_y_position + dy), text, font=font_obj, fill=(0, 0, 0, 200))
# 绘制主文本
draw.text((padding_x, text_y_position), text, font=font_obj, fill="#FFFFFF")
elif style == "3d":
# 3D立体效果
depth = max(3, font_size // 15) # 3D深度
for i in range(depth, 0, -1):
alpha = 100 + (155 * i // depth) # 渐变透明度
shadow_color = (0, 0, 0, alpha)
draw.text((padding_x - i, text_y_position + i), text, font=font_obj, fill=shadow_color)
# 绘制主文本
draw.text((padding_x, text_y_position), text, font=font_obj, fill="#FFFFFF")
elif style == "metallic":
# 金属效果
# 金属渐变色
metallic_base = (212, 175, 55, 255) # 金色基色
# 绘制金属效果的底色
draw.text((padding_x, text_y_position), text, font=font_obj, fill=metallic_base)
# 添加高光效果
highlight_offset = max(1, font_size // 30)
draw.text((padding_x - highlight_offset, text_y_position - highlight_offset),
text, font=font_obj, fill=(255, 255, 255, 100))
# 添加阴影增强金属感
shadow_offset = max(1, font_size // 25)
draw.text((padding_x + shadow_offset, text_y_position + shadow_offset),
text, font=font_obj, fill=(100, 100, 100, 100))
else: # normal
# 添加描边
if border:
for offset_x, offset_y in [(1, 0), (-1, 0), (0, 1), (0, -1)]:
draw.text((padding_x + offset_x, text_y_position + offset_y), text, font=font_obj, fill=border_color)
# 绘制主文本
draw.text((padding_x, text_y_position), text, font=font_obj, fill="#FFFFFF")
# 保存为临时文件
temp_img_path = os.path.join(utils.storage_dir("temp", create=True), f"preview_{str(uuid.uuid4())}.png")
img.save(temp_img_path, format="PNG")
return temp_img_path
def create_unified_preview(video_aspect, subtitle_params=None, title_params=None):
"""
创建统一预览图像
:param video_aspect: 视频宽高比
:param subtitle_params: 字幕参数字典
:param title_params: 标题参数字典
:return: 临时图像文件路径
"""
# 获取视频尺寸
aspect = VideoAspect(video_aspect)
video_width, video_height = aspect.to_resolution()
# 计算预览区域的尺寸,保持原始宽高比
preview_width = 400 # 固定宽度
preview_height = int(preview_width * video_height / video_width)
# 创建背景图像
bg_img = Image.new('RGB', (preview_width, preview_height), (51, 51, 51)) # #333333 背景色
# 如果有字幕参数,创建字幕预览
if subtitle_params and subtitle_params.get("enabled", False):
# 获取字幕参数
text = subtitle_params.get("text", "字幕预览")
font_path = subtitle_params.get("font_path")
font_size = subtitle_params.get("font_size", 60)
style = subtitle_params.get("style", "normal")
background = subtitle_params.get("background", "none")
background_color = subtitle_params.get("background_color", "#000000")
border = subtitle_params.get("border", True)
border_color = subtitle_params.get("border_color", "#FFFFFF")
position = subtitle_params.get("position", "bottom")
custom_position = subtitle_params.get("custom_position", 85.0)
background_enabled = subtitle_params.get("background_enabled", True)
# 创建字幕预览图像
subtitle_img_path = create_preview_image(
text=text,
font_path=font_path,
font_size=font_size,
style=style,
background_type=background,
background_color=background_color,
border=border,
border_color=border_color,
background_enabled=background_enabled,
is_title=False
)
# 加载字幕图像
subtitle_img = Image.open(subtitle_img_path)
# 计算字幕位置
if position == "top":
subtitle_y = int(preview_height * 0.05)
elif position == "center":
subtitle_y = int(preview_height * 0.5 - subtitle_img.height / 2)
elif position == "bottom":
subtitle_y = int(preview_height * 0.95 - subtitle_img.height)
elif position == "custom":
subtitle_y = int(preview_height * custom_position / 100 - subtitle_img.height / 2)
else:
subtitle_y = int(preview_height * 0.95 - subtitle_img.height)
# 计算水平居中位置
subtitle_x = int(preview_width / 2 - subtitle_img.width / 2)
# 将字幕图像粘贴到背景图像上
bg_img.paste(subtitle_img, (subtitle_x, subtitle_y), subtitle_img)
# 删除临时文件
try:
os.remove(subtitle_img_path)
except Exception as e:
logger.warning(f"Failed to remove temporary subtitle image file: {e}")
# 如果有标题参数,创建标题预览
if title_params and title_params.get("enabled", False):
# 获取标题参数
text = title_params.get("text", "标题预览")
font_path = title_params.get("font_path")
font_size = title_params.get("font_size", 80)
style = title_params.get("style", "rainbow")
background = title_params.get("background", "rounded_rect")
background_color = title_params.get("background_color", "#000000")
border = title_params.get("border", True)
border_color = title_params.get("border_color", "#FFFFFF")
position = title_params.get("position", "upper_middle")
custom_position = title_params.get("custom_position", 15.0)
background_enabled = title_params.get("background_enabled", True)
# 创建标题预览图像
title_img_path = create_preview_image(
text=text,
font_path=font_path,
font_size=font_size,
style=style,
background_type=background,
background_color=background_color,
border=border,
border_color=border_color,
background_enabled=background_enabled,
is_title=True
)
# 加载标题图像
title_img = Image.open(title_img_path)
# 计算标题位置
if position == "upper_middle":
title_y = int(preview_height * 0.10 - title_img.height / 2)
elif position == "middle":
title_y = int(preview_height * 0.5 - title_img.height / 2)
elif position == "lower_middle":
title_y = int(preview_height * 0.80 - title_img.height / 2)
elif position == "custom":
title_y = int(preview_height * custom_position / 100 - title_img.height / 2)
else:
title_y = int(preview_height * 0.10 - title_img.height / 2)
# 计算水平居中位置
title_x = int(preview_width / 2 - title_img.width / 2)
# 将标题图像粘贴到背景图像上
bg_img.paste(title_img, (title_x, title_y), title_img)
# 删除临时文件
try:
os.remove(title_img_path)
except Exception as e:
logger.warning(f"Failed to remove temporary title image file: {e}")
# 保存最终预览图像
preview_img_path = os.path.join(utils.storage_dir("temp", create=True), f"unified_preview_{str(uuid.uuid4())}.png")
bg_img.save(preview_img_path, format="PNG")
return preview_img_path
if __name__ == "__main__":
m = MaterialInfo()
m.url = "/Users/harry/Downloads/IMG_2915.JPG"

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -46,6 +46,7 @@ hide_streamlit_style = """
<style>#root > div:nth-child(1) > div > div > div > div > section > div {padding-top: 0rem;}</style>
"""
st.markdown(hide_streamlit_style, unsafe_allow_html=True)
st.title(f"MoneyPrinterTurbo v{config.project_version}")
support_locales = [
@ -66,15 +67,6 @@ config_file = os.path.join(root_dir, "webui", ".streamlit", "webui.toml")
system_locale = utils.get_system_locale()
# print(f"******** system locale: {system_locale} ********")
if "video_subject" not in st.session_state:
st.session_state["video_subject"] = ""
if "video_script" not in st.session_state:
st.session_state["video_script"] = ""
if "video_terms" not in st.session_state:
st.session_state["video_terms"] = ""
if "ui_language" not in st.session_state:
st.session_state["ui_language"] = config.ui.get("language", system_locale)
def get_all_fonts():
fonts = []
@ -86,6 +78,16 @@ def get_all_fonts():
return fonts
if "video_subject" not in st.session_state:
st.session_state["video_subject"] = ""
if "video_script" not in st.session_state:
st.session_state["video_script"] = ""
if "video_terms" not in st.session_state:
st.session_state["video_terms"] = ""
if "ui_language" not in st.session_state:
st.session_state["ui_language"] = config.ui.get("language", system_locale)
def get_all_songs():
songs = []
for root, dirs, files in os.walk(song_dir):
@ -126,6 +128,21 @@ def scroll_to_bottom():
st.markdown(js, unsafe_allow_html=True)
def load_font_css():
"""Load all fonts from the font directory and create CSS for them"""
css = "<style>\n"
for font_file in get_all_fonts():
font_name = font_file.split('.')[0] # Remove file extension
font_path = os.path.join(font_dir, font_file)
# Convert font path to relative path for CSS
css += f"@font-face {{\n"
css += f" font-family: '{font_name}';\n"
css += f" src: url('file://{font_path}') format('truetype');\n"
css += f"}}\n"
css += "</style>"
return css
def init_log():
logger.remove()
_lvl = "DEBUG"
@ -586,7 +603,7 @@ with middle_panel:
params.video_aspect = VideoAspect(video_aspect_ratios[selected_index][1])
params.video_clip_duration = st.selectbox(
tr("Clip Duration"), options=[2, 3, 4, 5, 6, 7, 8, 9, 10], index=1
tr("Clip Duration"), options=[2, 3, 4, 5, 6, 7, 8, 9, 10], index=3
)
params.video_count = st.selectbox(
tr("Number of Videos Generated Simultaneously"),
@ -737,7 +754,7 @@ with right_panel:
]
selected_index = st.selectbox(
tr("Position"),
index=2,
index=3, # 默认选择自定义位置
options=range(len(subtitle_positions)),
format_func=lambda x: subtitle_positions[x][0],
)
@ -745,7 +762,7 @@ with right_panel:
if params.subtitle_position == "custom":
custom_position = st.text_input(
tr("Custom Position (% from top)"), value="70.0"
tr("Custom Position (% from top)"), value="65.0"
)
try:
params.custom_position = float(custom_position)
@ -769,13 +786,13 @@ with right_panel:
stroke_cols = st.columns([0.3, 0.7])
with stroke_cols[0]:
params.stroke_color = st.color_picker(tr("Stroke Color"), "#000000")
params.stroke_color = st.color_picker(tr("Stroke Color"), "#FFFFFF")
with stroke_cols[1]:
params.stroke_width = st.slider(tr("Stroke Width"), 0.0, 10.0, 1.5)
params.stroke_width = st.slider(tr("Stroke Width"), 0.0, 10.0, 4.0)
# 艺术字体设置
st.write(tr("Art Font Settings"))
params.art_font_enabled = st.checkbox(tr("Enable Art Font"), value=False)
params.art_font_enabled = st.checkbox(tr("Enable Art Font"), value=True)
if params.art_font_enabled:
art_font_types = [
@ -788,7 +805,7 @@ with right_panel:
]
selected_index = st.selectbox(
tr("Art Font Type"),
index=0,
index=2, # 默认选择3D立体
options=range(len(art_font_types)),
format_func=lambda x: art_font_types[x][0],
)
@ -855,18 +872,11 @@ with right_panel:
elif params.art_font_type == "metallic":
font_style = f"color: {params.text_fore_color}; font-weight: bold; background: -webkit-linear-gradient(#eee, #333); -webkit-background-clip: text; -webkit-text-fill-color: transparent; text-shadow: 2px 2px 4px rgba(0,0,0,0.3);"
# 生成HTML预览
preview_html = f"""
<div style='{bg_style} padding: 15px; border-radius: 10px; text-align: center;'>
<span style='{font_style} font-size: {params.font_size//1.5}px;'>{preview_text}</span>
</div>
"""
st.markdown(preview_html, unsafe_allow_html=True)
# 不再需要单独的预览代码,因为我们使用了统一预览区域
# 标题贴纸设置
st.write(tr("Title Sticker Settings"))
params.title_sticker_enabled = st.checkbox(tr("Enable Title Sticker"), value=False)
params.title_sticker_enabled = st.checkbox(tr("Enable Title Sticker"), value=True)
if params.title_sticker_enabled:
params.title_sticker_text = st.text_input(tr("Title Text"), value="")
@ -884,7 +894,7 @@ with right_panel:
# 标题贴纸字体大小
saved_title_font_size = config.ui.get("title_sticker_font_size", 80)
params.title_sticker_font_size = st.slider(tr("Title Font Size"), 40, 200, saved_title_font_size, key="title_font_size")
params.title_sticker_font_size = st.slider(tr("Title Font Size"), 40, 200, 160, key="title_font_size")
config.ui["title_sticker_font_size"] = params.title_sticker_font_size
# 标题贴纸样式
@ -893,34 +903,67 @@ with right_panel:
(tr("Neon"), "neon"),
(tr("Gradient"), "gradient"),
(tr("Normal"), "normal"),
(tr("3D"), "3d"),
(tr("Metallic"), "metallic"),
(tr("Shadow"), "shadow"),
(tr("Outline"), "outline"),
]
selected_style_index = st.selectbox(
tr("Title Style"),
index=0,
index=5, # 默认选择金属质感
options=range(len(title_sticker_styles)),
format_func=lambda x: title_sticker_styles[x][0],
key="title_style_select"
)
params.title_sticker_style = title_sticker_styles[selected_style_index][1]
# 标题贴纸背景
title_sticker_backgrounds = [
(tr("None"), "none"),
(tr("Rounded Rectangle"), "rounded_rect"),
(tr("Rectangle"), "rect"),
# 标题贴纸位置
title_sticker_positions = [
(tr("Upper Middle"), "upper_middle"),
(tr("Middle"), "middle"),
(tr("Lower Middle"), "lower_middle"),
(tr("Custom"), "custom"),
]
selected_bg_index = st.selectbox(
tr("Title Background"),
index=1,
options=range(len(title_sticker_backgrounds)),
format_func=lambda x: title_sticker_backgrounds[x][0],
key="title_bg_select"
selected_pos_index = st.selectbox(
tr("Title Position"),
index=3, # 默认选择自定义位置
options=range(len(title_sticker_positions)),
format_func=lambda x: title_sticker_positions[x][0],
key="title_pos_select"
)
params.title_sticker_background = title_sticker_backgrounds[selected_bg_index][1]
params.title_sticker_position = title_sticker_positions[selected_pos_index][1]
# 标题贴纸背景颜色
if params.title_sticker_background != "none":
# 如果选择自定义位置,显示位置设置滑块
if params.title_sticker_position == "custom":
params.title_sticker_custom_position = st.slider(
tr("Custom Position (% from top)"),
0.0, 100.0, 15.0, 1.0,
key="title_custom_pos"
)
# 标题贴纸背景启用选项
params.title_sticker_background_enabled = st.checkbox(tr("Enable Title Background"), value=True, key="title_bg_enabled")
if params.title_sticker_background_enabled:
# 标题贴纸背景类型
title_sticker_backgrounds = [
(tr("Rounded Rectangle"), "rounded_rect"),
(tr("Rectangle"), "rect"),
]
selected_bg_index = st.selectbox(
tr("Title Background Type"),
index=0,
options=range(len(title_sticker_backgrounds)),
format_func=lambda x: title_sticker_backgrounds[x][0],
key="title_bg_select"
)
params.title_sticker_background = title_sticker_backgrounds[selected_bg_index][1]
# 标题贴纸背景颜色
params.title_sticker_background_color = st.color_picker(tr("Title Background Color"), "#000000", key="title_bg_color")
else:
# 如果不启用背景,设置为无背景
params.title_sticker_background = "none"
# 标题贴纸边框
params.title_sticker_border = st.checkbox(tr("Enable Title Border"), value=True, key="title_border")
@ -936,7 +979,7 @@ with right_panel:
title_bg_style = ""
# 设置背景样式
if params.title_sticker_background != "none":
if params.title_sticker_background_enabled and params.title_sticker_background != "none":
if params.title_sticker_background == "rounded_rect":
title_bg_style = f"background-color: {params.title_sticker_background_color}; border-radius: 15px;"
else:
@ -954,17 +997,84 @@ with right_panel:
title_font_style = "color: #FF4500; font-weight: bold; text-shadow: 0 0 5px #FFFF00, 0 0 10px #FFFF00, 0 0 15px #FF4500, 0 0 20px #FF4500;"
elif params.title_sticker_style == "gradient":
title_font_style = "background: linear-gradient(to bottom, #ff0000, #0000ff); -webkit-background-clip: text; -webkit-text-fill-color: transparent; font-weight: bold;"
elif params.title_sticker_style == "3d":
title_font_style = "color: #FFFFFF; font-weight: bold; text-shadow: 0px 1px 0px #999, 0px 2px 0px #888, 0px 3px 0px #777, 0px 4px 0px #666, 0px 5px 0px #555, 0px 6px 0px #444, 0px 7px 0px #333, 0px 8px 7px #001135;"
elif params.title_sticker_style == "metallic":
title_font_style = "color: #D4AF37; font-weight: bold; background: -webkit-linear-gradient(#eee, #D4AF37); -webkit-background-clip: text; -webkit-text-fill-color: transparent; text-shadow: 2px 2px 4px rgba(0,0,0,0.3);"
elif params.title_sticker_style == "shadow":
title_font_style = "color: #FFFFFF; font-weight: bold; text-shadow: 3px 3px 5px #000;"
elif params.title_sticker_style == "outline":
title_font_style = "color: #FFFFFF; font-weight: bold; text-shadow: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000;"
else: # normal
title_font_style = "color: #FFFFFF; font-weight: bold;"
# 生成HTML预览
title_preview_html = f"""
<div style='{title_bg_style} {border_style} padding: 15px; text-align: center; display: inline-block; margin: 0 auto;'>
<span style='{title_font_style} font-size: {params.title_sticker_font_size//2}px;'>{title_preview_text}</span>
</div>
"""
# 不再需要单独的预览代码,因为我们使用了统一预览区域
st.markdown(f"<div style='text-align: center;'>{title_preview_html}</div>", unsafe_allow_html=True)
# 添加统一预览区域
# 在生成视频按钮之前显示统一预览区域
with st.container(border=True):
st.write(tr("Video Preview"))
# 准备字幕参数
subtitle_params = None
if params.subtitle_enabled:
# 获取字幕字体路径
subtitle_font_path = os.path.join(font_dir, params.font_name)
# 准备字幕参数
subtitle_params = {
"enabled": params.subtitle_enabled,
"text": "字幕预览",
"font_path": subtitle_font_path,
"font_size": params.font_size,
"style": params.art_font_type if params.art_font_enabled else "normal",
"background": "rounded_rect" if params.art_font_background != "none" else "none",
"background_color": params.art_font_background if params.art_font_background != "none" else "#000000",
"border": True,
"border_color": params.stroke_color,
"position": params.subtitle_position,
"custom_position": params.custom_position,
"background_enabled": params.art_font_background != "none"
}
# 准备标题参数
title_params = None
if params.title_sticker_enabled:
# 获取标题字体路径
title_font_path = os.path.join(font_dir, params.title_sticker_font)
# 准备标题参数
title_params = {
"enabled": params.title_sticker_enabled,
"text": params.title_sticker_text or "标题预览",
"font_path": title_font_path,
"font_size": params.title_sticker_font_size,
"style": params.title_sticker_style,
"background": params.title_sticker_background,
"background_color": params.title_sticker_background_color,
"border": params.title_sticker_border,
"border_color": params.title_sticker_border_color,
"position": params.title_sticker_position,
"custom_position": params.title_sticker_custom_position,
"background_enabled": params.title_sticker_background_enabled
}
# 生成预览图像
from app.services.video import create_unified_preview
preview_img_path = create_unified_preview(
video_aspect=params.video_aspect,
subtitle_params=subtitle_params,
title_params=title_params
)
# 显示预览图像
st.image(preview_img_path, use_column_width=True)
# 删除临时文件
try:
os.remove(preview_img_path)
except Exception as e:
logger.warning(f"Failed to remove temporary preview image file: {e}")
start_button = st.button(tr("Generate Video"), use_container_width=True, type="primary")
if start_button:

View File

@ -113,12 +113,22 @@
"Rainbow": "彩虹",
"Neon": "霓虹灯",
"Gradient": "渐变",
"Title Position": "标题位置",
"Upper Middle": "上方中间",
"Middle": "正中间",
"Lower Middle": "下方中间",
"Custom": "自定义",
"Custom Position (% from top)": "自定义位置(距离顶部百分比)",
"Enable Title Background": "启用标题背景",
"Title Background Type": "标题背景类型",
"Title Background": "标题背景",
"Rounded Rectangle": "圆角矩形",
"Rectangle": "矩形",
"Title Background Color": "标题背景颜色",
"Enable Title Border": "启用标题边框",
"Title Border Color": "标题边框颜色",
"Title Sticker Preview": "标题贴纸预览"
"Title Sticker Preview": "标题贴纸预览",
"Font Preview": "字体预览",
"Video Preview": "视频预览"
}
}