mirror of
https://github.com/harry0703/MoneyPrinterTurbo.git
synced 2026-02-21 16:37:21 +08:00
增加字幕,增加标题贴纸
This commit is contained in:
parent
2cd0097ba1
commit
64d0cc2966
@ -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
|
||||
|
||||
@ -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:
|
||||
|
||||
193
webui/Main.py
193
webui/Main.py
@ -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()
|
||||
|
||||
@ -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": "标题贴纸预览"
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user