增加字幕,增加标题贴纸

This commit is contained in:
Larkspur-Wang 2025-04-20 00:16:06 +08:00
parent 2cd0097ba1
commit 64d0cc2966
4 changed files with 677 additions and 22 deletions

View File

@ -98,6 +98,22 @@ class VideoParams(BaseModel):
text_fore_color: Optional[str] = "#FFFFFF"
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_background: Optional[str] = "none" # none, red, blue, etc.
# 标题贴纸相关参数
title_sticker_enabled: Optional[bool] = False
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_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"
font_size: int = 60
stroke_color: Optional[str] = "#000000"
stroke_width: float = 1.5
@ -124,6 +140,11 @@ class SubtitleRequest(BaseModel):
video_source: Optional[str] = "local"
subtitle_enabled: Optional[str] = "true"
# 艺术字体相关参数
art_font_enabled: Optional[bool] = False
art_font_type: Optional[str] = "normal" # normal, shadow, outline, 3d, etc.
art_font_background: Optional[str] = "none" # none, red, blue, etc.
class AudioRequest(BaseModel):
video_script: str

View File

@ -1,6 +1,7 @@
import glob
import os
import random
import uuid
from typing import List
from loguru import logger
@ -16,7 +17,7 @@ from moviepy import (
concatenate_videoclips,
)
from moviepy.video.tools.subtitles import SubtitlesClip
from PIL import ImageFont
from PIL import Image, ImageDraw, ImageFont, ImageFilter
from app.models import const
from app.models.schema import (
@ -182,6 +183,345 @@ def combine_videos(
return combined_video_path
def create_title_sticker(text, font, font_size, style, background, background_color, border, border_color, size):
"""
创建标题贴纸
:param text: 标题文本
:param font: 字体路径
:param font_size: 字体大小
:param style: 标题样式rainbow, neon, gradient等
:param background: 背景类型none, rounded_rect, rect等
:param background_color: 背景颜色
:param border: 是否有边框
:param border_color: 边框颜色
:param size: 视频尺寸
:return: ImageClip对象
"""
if not text:
return None
video_width, video_height = size
# 创建字体对象
font_obj = ImageFont.truetype(font, 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)
sticker_width = text_width + padding_x * 2
sticker_height = text_height + padding_y * 2
# 确保文本在背景中垂直居中
text_y_position = (sticker_height - text_height) // 2
# 创建透明背景图像
img = Image.new('RGBA', (sticker_width, sticker_height), (0, 0, 0, 0))
draw = ImageDraw.Draw(img)
# 绘制背景
if background != "none":
# 确保背景颜色完全不透明
if background_color.startswith('#') and len(background_color) == 7:
bg_color = background_color + 'ff' # 添加不透明度
else:
bg_color = background_color
if background == "rounded_rect":
# 绘制圆角矩形
radius = int(sticker_height * 0.3) # 圆角半径
draw.rounded_rectangle(
[(0, 0), (sticker_width, sticker_height)],
radius=radius,
fill=bg_color
)
elif background == "rect":
# 绘制矩形
draw.rectangle(
[(0, 0), (sticker_width, sticker_height)],
fill=bg_color
)
# 根据样式绘制文本
if 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 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 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)
else: # 默认样式
# 添加描边
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"title_sticker_{str(uuid.uuid4())}.png")
img.save(temp_img_path, format="PNG")
# 创建图像剪辑
clip = ImageClip(temp_img_path)
# 删除临时文件
try:
os.remove(temp_img_path)
except Exception as e:
logger.warning(f"Failed to remove temporary image file: {e}")
return clip
def create_art_text_clip(text, font, font_size, color, art_font_type, art_font_background, size, text_align='center'):
"""
创建艺术字体字幕
:param text: 文本内容
:param font: 字体路径
:param font_size: 字体大小
:param color: 字体颜色
:param art_font_type: 艺术字体类型normal, shadow, outline, 3d, neon, metallic
:param art_font_background: 背景颜色
:param size: 字幕大小
:param text_align: 文本对齐方式
:return: TextClip对象
"""
width, height = size[0], None
# 创建一个透明背景的图像
# 首先计算文本高度
font_obj = ImageFont.truetype(font, font_size)
lines = text.split('\n')
total_height = 0
for line in lines:
left, top, right, bottom = font_obj.getbbox(line)
line_height = bottom - top
total_height += line_height + 10 # 添加行间距
# 创建背景图像
if art_font_background != "none" and art_font_background != "":
# 如果是预定义颜色
if art_font_background in ["red", "blue", "green", "yellow", "purple", "orange"]:
bg_colors = {
"red": (255, 0, 0, 180),
"blue": (0, 0, 255, 180),
"green": (0, 128, 0, 180),
"yellow": (255, 255, 0, 180),
"purple": (128, 0, 128, 180),
"orange": (255, 165, 0, 180)
}
bg_color = bg_colors.get(art_font_background, (255, 0, 0, 180))
else:
# 如果是自定义颜色(如#FF0000
try:
# 将十六进制颜色代码转换为RGBA
hex_color = art_font_background.lstrip('#')
r, g, b = tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4))
bg_color = (r, g, b, 180) # 半透明
except Exception:
bg_color = (255, 0, 0, 180) # 默认红色
# 创建背景图像,比文本区域稍大一些
bg_img = Image.new('RGBA', (width, total_height + 40), (0, 0, 0, 0))
draw = ImageDraw.Draw(bg_img)
# 绘制圆角矩形背景
draw.rounded_rectangle([(10, 5), (width-10, total_height+35)], radius=20, fill=bg_color)
else:
bg_img = Image.new('RGBA', (width, total_height + 40), (0, 0, 0, 0))
# 创建文本图像
txt_img = Image.new('RGBA', (width, total_height + 40), (0, 0, 0, 0))
draw = ImageDraw.Draw(txt_img)
# 解析颜色
try:
if color.startswith('#'):
hex_color = color.lstrip('#')
r, g, b = tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4))
text_color = (r, g, b, 255)
else:
text_color = (255, 255, 255, 255) # 默认白色
except Exception:
text_color = (255, 255, 255, 255) # 默认白色
# 根据艺术字体类型应用不同的效果
y_offset = 20 # 初始垂直偏移
for line in lines:
# 计算文本宽度以实现居中对齐
left, top, right, bottom = font_obj.getbbox(line)
text_width = right - left
line_height = bottom - top
if text_align == 'center':
x_position = (width - text_width) // 2
elif text_align == 'right':
x_position = width - text_width - 20
else: # left
x_position = 20
if art_font_type == "shadow":
# 阴影效果
shadow_offset = max(2, font_size // 20) # 阴影偏移量
draw.text((x_position + shadow_offset, y_offset + shadow_offset), line, font=font_obj, fill=(0, 0, 0, 160))
draw.text((x_position, y_offset), line, font=font_obj, fill=text_color)
elif art_font_type == "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((x_position + dx, y_offset + dy), line, font=font_obj, fill=(0, 0, 0, 200))
# 绘制主文本
draw.text((x_position, y_offset), line, font=font_obj, fill=text_color)
elif art_font_type == "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((x_position - i, y_offset + i), line, font=font_obj, fill=shadow_color)
# 绘制主文本
draw.text((x_position, y_offset), line, font=font_obj, fill=text_color)
elif art_font_type == "neon":
# 霓虹灯效果
glow_iterations = 5
glow_color = (0, 255, 255, 50) # 青色荧光
for i in range(glow_iterations, 0, -1):
blur_radius = i * 2
for dx, dy in [(j, k) for j in range(-1, 2) for k in range(-1, 2)]:
draw.text((x_position + dx * blur_radius, y_offset + dy * blur_radius),
line, font=font_obj, fill=(glow_color[0], glow_color[1], glow_color[2], glow_color[3] // i))
# 绘制主文本
draw.text((x_position, y_offset), line, font=font_obj, fill=text_color)
elif art_font_type == "metallic":
# 金属效果
# 金属渐变色
metallic_base = (212, 175, 55, 255) # 金色基色
metallic_highlight = (255, 223, 0, 255) # 金色高光
# 绘制金属效果的底色
draw.text((x_position, y_offset), line, font=font_obj, fill=metallic_base)
# 添加高光效果
highlight_offset = max(1, font_size // 30)
draw.text((x_position - highlight_offset, y_offset - highlight_offset),
line, font=font_obj, fill=(255, 255, 255, 100))
# 添加阴影增强金属感
shadow_offset = max(1, font_size // 25)
draw.text((x_position + shadow_offset, y_offset + shadow_offset),
line, font=font_obj, fill=(100, 100, 100, 100))
else: # normal
# 普通文本
draw.text((x_position, y_offset), line, font=font_obj, fill=text_color)
y_offset += line_height + 10 # 移动到下一行
# 合并背景和文本图像
final_img = Image.alpha_composite(bg_img, txt_img)
# 如果是霓虹灯效果,添加模糊
if art_font_type == "neon":
final_img = final_img.filter(ImageFilter.GaussianBlur(1))
# 将PIL图像转换为TextClip
# 需要先保存为临时文件
temp_img_path = os.path.join(utils.storage_dir("temp", create=True), f"art_text_{str(uuid.uuid4())}.png")
final_img.save(temp_img_path, format="PNG")
# 创建图像剪辑
clip = ImageClip(temp_img_path)
# 删除临时文件
try:
os.remove(temp_img_path)
except Exception as e:
logger.warning(f"Failed to remove temporary image file: {e}")
return clip
def wrap_text(text, max_width, font="Arial", fontsize=60):
# Create ImageFont
font = ImageFont.truetype(font, fontsize)
@ -279,18 +619,34 @@ def generate_video(
wrapped_txt, txt_height = wrap_text(
phrase, max_width=max_width, font=font_path, fontsize=params.font_size
)
_clip = TextClip(
text=wrapped_txt,
font=font_path,
font_size=params.font_size,
color=params.text_fore_color,
bg_color=params.text_background_color,
stroke_color=params.stroke_color,
stroke_width=params.stroke_width,
size=(video_width, None),
method='caption',
text_align='center'
)
# 判断是否启用艺术字体
if hasattr(params, 'art_font_enabled') and params.art_font_enabled:
# 创建艺术字体
_clip = create_art_text_clip(
text=wrapped_txt,
font=font_path,
font_size=params.font_size,
color=params.text_fore_color,
art_font_type=params.art_font_type,
art_font_background=params.art_font_background,
size=(video_width, None),
text_align='center'
)
else:
# 使用普通字幕
_clip = TextClip(
text=wrapped_txt,
font=font_path,
font_size=params.font_size,
color=params.text_fore_color,
bg_color=params.text_background_color,
stroke_color=params.stroke_color,
stroke_width=params.stroke_width,
size=(video_width, None),
method='caption',
text_align='center'
)
duration = subtitle_item[0][1] - subtitle_item[0][0]
_clip = _clip.with_start(subtitle_item[0][0])
_clip = _clip.with_end(subtitle_item[0][1])
@ -319,15 +675,61 @@ def generate_video(
)
def make_textclip(text):
return TextClip(
text=text,
font=font_path,
font_size=params.font_size,
size=(video_width, None),
method='caption',
text_align='center'
# 判断是否启用艺术字体
if hasattr(params, 'art_font_enabled') and params.art_font_enabled:
# 创建艺术字体
return create_art_text_clip(
text=text,
font=font_path,
font_size=params.font_size,
color=params.text_fore_color,
art_font_type=params.art_font_type,
art_font_background=params.art_font_background,
size=(video_width, None),
text_align='center'
)
else:
# 使用普通字幕
return TextClip(
text=text,
font=font_path,
font_size=params.font_size,
size=(video_width, None),
method='caption',
text_align='center'
)
# 创建所有视频元素的列表
video_elements = [video_clip]
# 添加标题贴纸
if hasattr(params, 'title_sticker_enabled') and params.title_sticker_enabled and params.title_sticker_text:
# 获取标题贴纸字体路径
title_font_path = os.path.join(utils.font_dir(), params.title_sticker_font)
if os.name == "nt":
title_font_path = title_font_path.replace("\\", "/")
# 创建标题贴纸
title_sticker = create_title_sticker(
text=params.title_sticker_text,
font=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,
size=(video_width, video_height)
)
# 设置标题贴纸位置(顶部中间)
if title_sticker:
title_sticker = title_sticker.with_position(("center", video_height * 0.05))
title_sticker = title_sticker.with_duration(video_clip.duration)
video_elements.append(title_sticker)
logger.info(f"Added title sticker: {params.title_sticker_text}")
# 添加字幕
if subtitle_path and os.path.exists(subtitle_path):
sub = SubtitlesClip(
subtitles=subtitle_path, encoding="utf-8", make_textclip=make_textclip
@ -336,7 +738,10 @@ def generate_video(
for item in sub.subtitles:
clip = create_text_clip(subtitle_item=item)
text_clips.append(clip)
video_clip = CompositeVideoClip([video_clip, *text_clips])
video_elements.extend(text_clips)
# 合成所有视频元素
video_clip = CompositeVideoClip(video_elements)
bgm_file = get_bgm_file(bgm_type=params.bgm_type, bgm_file=params.bgm_file)
if bgm_file:

View File

@ -773,6 +773,199 @@ with right_panel:
with stroke_cols[1]:
params.stroke_width = st.slider(tr("Stroke Width"), 0.0, 10.0, 1.5)
# 艺术字体设置
st.write(tr("Art Font Settings"))
params.art_font_enabled = st.checkbox(tr("Enable Art Font"), value=False)
if params.art_font_enabled:
art_font_types = [
(tr("Normal"), "normal"),
(tr("Shadow"), "shadow"),
(tr("Outline"), "outline"),
(tr("3D"), "3d"),
(tr("Neon"), "neon"),
(tr("Metallic"), "metallic"),
]
selected_index = st.selectbox(
tr("Art Font Type"),
index=0,
options=range(len(art_font_types)),
format_func=lambda x: art_font_types[x][0],
)
params.art_font_type = art_font_types[selected_index][1]
art_font_backgrounds = [
(tr("None"), "none"),
(tr("Red"), "red"),
(tr("Blue"), "blue"),
(tr("Green"), "green"),
(tr("Yellow"), "yellow"),
(tr("Purple"), "purple"),
(tr("Orange"), "orange"),
(tr("Custom"), "custom"),
]
selected_index = st.selectbox(
tr("Art Font Background"),
index=0,
options=range(len(art_font_backgrounds)),
format_func=lambda x: art_font_backgrounds[x][0],
)
params.art_font_background = art_font_backgrounds[selected_index][1]
if params.art_font_background == "custom":
params.art_font_background = st.color_picker(tr("Custom Background Color"), "#FF0000")
# 预览效果
st.write(tr("Preview"))
preview_text = tr("Art Font Preview")
# 根据选择的艺术字体类型生成不同的CSS样式
font_style = ""
bg_style = "background-color: #333;"
# 设置背景颜色
if params.art_font_background != "none":
if params.art_font_background == "red":
bg_style = "background-color: rgba(255, 0, 0, 0.7);"
elif params.art_font_background == "blue":
bg_style = "background-color: rgba(0, 0, 255, 0.7);"
elif params.art_font_background == "green":
bg_style = "background-color: rgba(0, 128, 0, 0.7);"
elif params.art_font_background == "yellow":
bg_style = "background-color: rgba(255, 255, 0, 0.7);"
elif params.art_font_background == "purple":
bg_style = "background-color: rgba(128, 0, 128, 0.7);"
elif params.art_font_background == "orange":
bg_style = "background-color: rgba(255, 165, 0, 0.7);"
elif params.art_font_background.startswith("#"):
# 处理自定义颜色
bg_style = f"background-color: {params.art_font_background}aa;"
# 设置字体样式
if params.art_font_type == "normal":
font_style = f"color: {params.text_fore_color}; font-weight: bold;"
elif params.art_font_type == "shadow":
font_style = f"color: {params.text_fore_color}; font-weight: bold; text-shadow: 3px 3px 5px #000;"
elif params.art_font_type == "outline":
font_style = f"color: {params.text_fore_color}; font-weight: bold; text-shadow: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000;"
elif params.art_font_type == "3d":
font_style = f"color: {params.text_fore_color}; 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.art_font_type == "neon":
font_style = f"color: {params.text_fore_color}; font-weight: bold; text-shadow: 0 0 5px #fff, 0 0 10px #fff, 0 0 15px #0073e6, 0 0 20px #0073e6, 0 0 25px #0073e6, 0 0 30px #0073e6, 0 0 35px #0073e6;"
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)
if params.title_sticker_enabled:
params.title_sticker_text = st.text_input(tr("Title Text"), value="")
# 标题贴纸字体选择
title_font_names = get_all_fonts()
saved_title_font_name = config.ui.get("title_sticker_font", params.font_name)
saved_title_font_index = 0
if saved_title_font_name in title_font_names:
saved_title_font_index = title_font_names.index(saved_title_font_name)
params.title_sticker_font = st.selectbox(
tr("Title Font"), title_font_names, index=saved_title_font_index, key="title_font_select"
)
config.ui["title_sticker_font"] = params.title_sticker_font
# 标题贴纸字体大小
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")
config.ui["title_sticker_font_size"] = params.title_sticker_font_size
# 标题贴纸样式
title_sticker_styles = [
(tr("Rainbow"), "rainbow"),
(tr("Neon"), "neon"),
(tr("Gradient"), "gradient"),
(tr("Normal"), "normal"),
]
selected_style_index = st.selectbox(
tr("Title Style"),
index=0,
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"),
]
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"
)
params.title_sticker_background = title_sticker_backgrounds[selected_bg_index][1]
# 标题贴纸背景颜色
if params.title_sticker_background != "none":
params.title_sticker_background_color = st.color_picker(tr("Title Background Color"), "#000000", key="title_bg_color")
# 标题贴纸边框
params.title_sticker_border = st.checkbox(tr("Enable Title Border"), value=True, key="title_border")
if params.title_sticker_border:
params.title_sticker_border_color = st.color_picker(tr("Title Border Color"), "#FFFFFF", key="title_border_color")
# 预览效果
st.write(tr("Title Sticker Preview"))
title_preview_text = params.title_sticker_text or tr("Title Sticker Preview")
# 生成标题贴纸预览样式
title_font_style = ""
title_bg_style = ""
# 设置背景样式
if 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:
title_bg_style = f"background-color: {params.title_sticker_background_color};"
# 设置边框
border_style = ""
if params.title_sticker_border:
border_style = f"border: 2px solid {params.title_sticker_border_color};"
# 设置文字样式
if params.title_sticker_style == "rainbow":
title_font_style = "background: linear-gradient(to right, red, orange, yellow, green, blue, indigo, violet); -webkit-background-clip: text; -webkit-text-fill-color: transparent; font-weight: bold;"
elif params.title_sticker_style == "neon":
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;"
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)
start_button = st.button(tr("Generate Video"), use_container_width=True, type="primary")
if start_button:
config.save_config()

View File

@ -83,6 +83,42 @@
"Voice Example": "这是一段测试语音合成的示例文本",
"Synthesizing Voice": "语音合成中,请稍候...",
"TTS Provider": "语音合成提供商",
"Hide Log": "隐藏日志"
"Hide Log": "隐藏日志",
"Art Font Settings": "艺术字体设置",
"Enable Art Font": "启用艺术字体",
"Art Font Type": "艺术字体类型",
"Normal": "普通",
"Shadow": "阴影",
"Outline": "描边",
"3D": "3D立体",
"Neon": "霓虹灯",
"Metallic": "金属质感",
"Art Font Background": "艺术字体背景",
"None": "无背景",
"Red": "红色",
"Blue": "蓝色",
"Green": "绿色",
"Yellow": "黄色",
"Purple": "紫色",
"Orange": "橙色",
"Custom Background Color": "自定义背景颜色",
"Preview": "预览效果",
"Art Font Preview": "艺术字体预览",
"Title Sticker Settings": "标题贴纸设置",
"Enable Title Sticker": "启用标题贴纸",
"Title Text": "标题文字",
"Title Font": "标题字体",
"Title Font Size": "标题字体大小",
"Title Style": "标题样式",
"Rainbow": "彩虹",
"Neon": "霓虹灯",
"Gradient": "渐变",
"Title Background": "标题背景",
"Rounded Rectangle": "圆角矩形",
"Rectangle": "矩形",
"Title Background Color": "标题背景颜色",
"Enable Title Border": "启用标题边框",
"Title Border Color": "标题边框颜色",
"Title Sticker Preview": "标题贴纸预览"
}
}