mirror of
https://github.com/harry0703/MoneyPrinterTurbo.git
synced 2026-03-11 01:07:21 +08:00
增加了各种字体
This commit is contained in:
parent
64d0cc2966
commit
b90c241a71
@ -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
|
||||
|
||||
|
||||
@ -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"
|
||||
|
||||
BIN
resource/fonts/AlimamaFangYuanTiVF-Thin-2.ttf
Normal file
BIN
resource/fonts/AlimamaFangYuanTiVF-Thin-2.ttf
Normal file
Binary file not shown.
Binary file not shown.
BIN
resource/fonts/JingNanBoBoHei-Bold-2.ttf
Normal file
BIN
resource/fonts/JingNanBoBoHei-Bold-2.ttf
Normal file
Binary file not shown.
BIN
resource/fonts/LogoSCUnboundedSans-Regular-2.ttf
Normal file
BIN
resource/fonts/LogoSCUnboundedSans-Regular-2.ttf
Normal file
Binary file not shown.
BIN
resource/fonts/PingFangLaiJiangHuHuaiGuTi-2.ttf
Normal file
BIN
resource/fonts/PingFangLaiJiangHuHuaiGuTi-2.ttf
Normal file
Binary file not shown.
BIN
resource/fonts/ZiKuJiangHuGuFengTi-2.ttf
Normal file
BIN
resource/fonts/ZiKuJiangHuGuFengTi-2.ttf
Normal file
Binary file not shown.
BIN
resource/fonts/ZiKuXingQiuFeiYangTi-2.ttf
Normal file
BIN
resource/fonts/ZiKuXingQiuFeiYangTi-2.ttf
Normal file
Binary file not shown.
BIN
resource/fonts/dingliehakkafont-2.ttf
Normal file
BIN
resource/fonts/dingliehakkafont-2.ttf
Normal file
Binary file not shown.
BIN
resource/fonts/dingliehuobanfont20241217-2.ttf
Normal file
BIN
resource/fonts/dingliehuobanfont20241217-2.ttf
Normal file
Binary file not shown.
BIN
resource/fonts/dingliexidafont-20250329V2)-2.ttf
Normal file
BIN
resource/fonts/dingliexidafont-20250329V2)-2.ttf
Normal file
Binary file not shown.
BIN
resource/fonts/huangkaihuaLawyerfont-2.ttf
Normal file
BIN
resource/fonts/huangkaihuaLawyerfont-2.ttf
Normal file
Binary file not shown.
208
webui/Main.py
208
webui/Main.py
@ -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:
|
||||
|
||||
@ -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": "视频预览"
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user