优化模板选择的交互逻辑, 支持直接预览选择
@@ -22,7 +22,7 @@ Suitable for TikTok, Kuaishou, Xiaohongshu, and other short video platforms.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Blurred background card style, suitable for graphic content display
|
Blurred background card style, suitable for graphic content display
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ Suitable for TikTok, Kuaishou, Xiaohongshu, and other short video platforms.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Cartoon style, suitable for light and lively content
|
Cartoon style, suitable for light and lively content
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@ Suitable for TikTok, Kuaishou, Xiaohongshu, and other short video platforms.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Default template, simple and versatile
|
Default template, simple and versatile
|
||||||
|
|
||||||
@@ -46,7 +46,7 @@ Suitable for TikTok, Kuaishou, Xiaohongshu, and other short video platforms.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Elegant style, suitable for artistic and intellectual content
|
Elegant style, suitable for artistic and intellectual content
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ Suitable for TikTok, Kuaishou, Xiaohongshu, and other short video platforms.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Retro fashion style, suitable for nostalgic themes
|
Retro fashion style, suitable for nostalgic themes
|
||||||
|
|
||||||
@@ -62,7 +62,7 @@ Suitable for TikTok, Kuaishou, Xiaohongshu, and other short video platforms.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Life insight style, suitable for inspirational content
|
Life insight style, suitable for inspirational content
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@ Suitable for TikTok, Kuaishou, Xiaohongshu, and other short video platforms.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Modern minimalist style, suitable for business and tech content
|
Modern minimalist style, suitable for business and tech content
|
||||||
|
|
||||||
@@ -78,7 +78,7 @@ Suitable for TikTok, Kuaishou, Xiaohongshu, and other short video platforms.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Neon style, suitable for fashion and trendy content
|
Neon style, suitable for fashion and trendy content
|
||||||
|
|
||||||
@@ -86,7 +86,7 @@ Suitable for TikTok, Kuaishou, Xiaohongshu, and other short video platforms.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Psychology card style, suitable for knowledge popularization
|
Psychology card style, suitable for knowledge popularization
|
||||||
|
|
||||||
@@ -94,7 +94,7 @@ Suitable for TikTok, Kuaishou, Xiaohongshu, and other short video platforms.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Purple theme, suitable for dreamy and mysterious styles
|
Purple theme, suitable for dreamy and mysterious styles
|
||||||
|
|
||||||
@@ -102,7 +102,7 @@ Suitable for TikTok, Kuaishou, Xiaohongshu, and other short video platforms.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Minimalist style, highlighting the content itself
|
Minimalist style, highlighting the content itself
|
||||||
|
|
||||||
@@ -110,7 +110,7 @@ Suitable for TikTok, Kuaishou, Xiaohongshu, and other short video platforms.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
80s satirical cartoon style for spiritual tales
|
80s satirical cartoon style for spiritual tales
|
||||||
|
|
||||||
@@ -118,7 +118,7 @@ Suitable for TikTok, Kuaishou, Xiaohongshu, and other short video platforms.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Simple black background, suitable for inspirational content
|
Simple black background, suitable for inspirational content
|
||||||
|
|
||||||
@@ -126,7 +126,7 @@ Suitable for TikTok, Kuaishou, Xiaohongshu, and other short video platforms.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Simple line drawing style for cognitive growth content
|
Simple line drawing style for cognitive growth content
|
||||||
|
|
||||||
@@ -134,7 +134,7 @@ Suitable for TikTok, Kuaishou, Xiaohongshu, and other short video platforms.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Book style, suitable for book lists
|
Book style, suitable for book lists
|
||||||
|
|
||||||
@@ -142,7 +142,7 @@ Suitable for TikTok, Kuaishou, Xiaohongshu, and other short video platforms.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Long text style, suitable for inspirational content
|
Long text style, suitable for inspirational content
|
||||||
|
|
||||||
@@ -150,7 +150,7 @@ Suitable for TikTok, Kuaishou, Xiaohongshu, and other short video platforms.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Excerpt style, suitable for quotes and book excerpts
|
Excerpt style, suitable for quotes and book excerpts
|
||||||
|
|
||||||
@@ -158,7 +158,7 @@ Suitable for TikTok, Kuaishou, Xiaohongshu, and other short video platforms.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Health preserving tips, suitable for wellness explainers.
|
Health preserving tips, suitable for wellness explainers.
|
||||||
|
|
||||||
@@ -166,7 +166,7 @@ Suitable for TikTok, Kuaishou, Xiaohongshu, and other short video platforms.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Life insights, conveying warmth and strength
|
Life insights, conveying warmth and strength
|
||||||
|
|
||||||
@@ -174,7 +174,7 @@ Suitable for TikTok, Kuaishou, Xiaohongshu, and other short video platforms.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Full screen display, suitable for book lists
|
Full screen display, suitable for book lists
|
||||||
|
|
||||||
@@ -182,7 +182,7 @@ Suitable for TikTok, Kuaishou, Xiaohongshu, and other short video platforms.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Healing style, suitable for therapeutic content
|
Healing style, suitable for therapeutic content
|
||||||
</div>
|
</div>
|
||||||
@@ -199,7 +199,7 @@ Suitable for YouTube, Bilibili, and other video platforms.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Ultrawide minimalist style, suitable for desktop viewing
|
Ultrawide minimalist style, suitable for desktop viewing
|
||||||
|
|
||||||
@@ -207,7 +207,7 @@ Suitable for YouTube, Bilibili, and other video platforms.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Dark tech style, suitable for technology and gaming content
|
Dark tech style, suitable for technology and gaming content
|
||||||
|
|
||||||
@@ -215,7 +215,7 @@ Suitable for YouTube, Bilibili, and other video platforms.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Film style, immersive experience
|
Film style, immersive experience
|
||||||
|
|
||||||
@@ -223,7 +223,7 @@ Suitable for YouTube, Bilibili, and other video platforms.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Full screen display, suitable for book lists
|
Full screen display, suitable for book lists
|
||||||
|
|
||||||
@@ -231,7 +231,7 @@ Suitable for YouTube, Bilibili, and other video platforms.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Book style, suitable for book lists
|
Book style, suitable for book lists
|
||||||
|
|
||||||
@@ -249,7 +249,7 @@ Suitable for Instagram, WeChat Moments, and other platforms.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Minimalist framed style, suitable for social media sharing
|
Minimalist framed style, suitable for social media sharing
|
||||||
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 201 KiB After Width: | Height: | Size: 201 KiB |
|
Before Width: | Height: | Size: 94 KiB After Width: | Height: | Size: 94 KiB |
|
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 83 KiB |
|
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 103 KiB |
|
Before Width: | Height: | Size: 390 KiB After Width: | Height: | Size: 390 KiB |
|
Before Width: | Height: | Size: 144 KiB After Width: | Height: | Size: 144 KiB |
|
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 70 KiB |
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 82 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 91 KiB After Width: | Height: | Size: 91 KiB |
|
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 65 KiB |
|
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 79 KiB |
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 77 KiB |
|
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 102 KiB After Width: | Height: | Size: 102 KiB |
|
Before Width: | Height: | Size: 107 KiB After Width: | Height: | Size: 107 KiB |
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 92 KiB |
|
Before Width: | Height: | Size: 91 KiB After Width: | Height: | Size: 91 KiB |
|
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 105 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 86 KiB |
|
Before Width: | Height: | Size: 125 KiB After Width: | Height: | Size: 125 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 202 KiB After Width: | Height: | Size: 202 KiB |
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 201 KiB After Width: | Height: | Size: 201 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 84 KiB |
|
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 104 KiB |
|
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 69 KiB |
|
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 83 KiB |
|
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 83 KiB |
|
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 88 KiB After Width: | Height: | Size: 88 KiB |
@@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
模糊背景卡片风格,适合图文内容展示
|
模糊背景卡片风格,适合图文内容展示
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
卡通风格,适合轻松活泼的内容
|
卡通风格,适合轻松活泼的内容
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
默认模板,简洁通用
|
默认模板,简洁通用
|
||||||
|
|
||||||
@@ -46,7 +46,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
优雅风格,适合文艺、知性内容
|
优雅风格,适合文艺、知性内容
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
复古时尚风格,适合怀旧主题
|
复古时尚风格,适合怀旧主题
|
||||||
|
|
||||||
@@ -62,7 +62,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
生活感悟风格,适合心灵鸡汤类内容
|
生活感悟风格,适合心灵鸡汤类内容
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
现代简约风格,适合商务、科技内容
|
现代简约风格,适合商务、科技内容
|
||||||
|
|
||||||
@@ -78,7 +78,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
霓虹灯风格,适合时尚、潮流内容
|
霓虹灯风格,适合时尚、潮流内容
|
||||||
|
|
||||||
@@ -86,7 +86,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
心理学卡片风格,适合知识科普
|
心理学卡片风格,适合知识科普
|
||||||
|
|
||||||
@@ -94,7 +94,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
紫色主题,适合梦幻、神秘风格
|
紫色主题,适合梦幻、神秘风格
|
||||||
|
|
||||||
@@ -102,7 +102,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
极简风格,突出内容本身
|
极简风格,突出内容本身
|
||||||
|
|
||||||
@@ -110,7 +110,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
80年代讽刺漫画风格,适合精神类小故事
|
80年代讽刺漫画风格,适合精神类小故事
|
||||||
|
|
||||||
@@ -118,7 +118,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
极简黑色背景,适合心灵鸡汤类内容
|
极简黑色背景,适合心灵鸡汤类内容
|
||||||
|
|
||||||
@@ -126,7 +126,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
简笔画,适合认知成长类内容
|
简笔画,适合认知成长类内容
|
||||||
|
|
||||||
@@ -134,7 +134,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
图书解读,适合科普类内容
|
图书解读,适合科普类内容
|
||||||
|
|
||||||
@@ -142,7 +142,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
长文本,适合励志鸡汤类内容
|
长文本,适合励志鸡汤类内容
|
||||||
|
|
||||||
@@ -150,7 +150,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
图文摘抄,适合图文摘抄,名人名言
|
图文摘抄,适合图文摘抄,名人名言
|
||||||
|
|
||||||
@@ -158,7 +158,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
养生窍门,适合养生科普内容
|
养生窍门,适合养生科普内容
|
||||||
|
|
||||||
@@ -166,7 +166,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
人生感悟,传递温暖与力量
|
人生感悟,传递温暖与力量
|
||||||
|
|
||||||
@@ -174,7 +174,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
全屏模版,适合书单号
|
全屏模版,适合书单号
|
||||||
|
|
||||||
@@ -182,7 +182,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
治愈模版,适合疗愈类内容
|
治愈模版,适合疗愈类内容
|
||||||
</div>
|
</div>
|
||||||
@@ -199,7 +199,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
超宽屏极简风格,适合桌面端观看
|
超宽屏极简风格,适合桌面端观看
|
||||||
|
|
||||||
@@ -207,7 +207,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
暗黑科技风格,适合技术、游戏内容
|
暗黑科技风格,适合技术、游戏内容
|
||||||
|
|
||||||
@@ -215,7 +215,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
电影风格,沉浸式体验
|
电影风格,沉浸式体验
|
||||||
|
|
||||||
@@ -223,7 +223,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
全屏显示,适合书单号
|
全屏显示,适合书单号
|
||||||
|
|
||||||
@@ -231,7 +231,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
图书解读,适合科普类内容
|
图书解读,适合科普类内容
|
||||||
</div>
|
</div>
|
||||||
@@ -248,7 +248,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
极简边框风格,适合社交媒体分享
|
极简边框风格,适合社交媒体分享
|
||||||
|
|
||||||
|
|||||||
@@ -228,6 +228,46 @@ def render_style_config(pixelle_video):
|
|||||||
# ====================================================================
|
# ====================================================================
|
||||||
# Storyboard Template Section
|
# Storyboard Template Section
|
||||||
# ====================================================================
|
# ====================================================================
|
||||||
|
|
||||||
|
def get_template_preview_path(template_path: str, language: str = "zh_CN") -> str:
|
||||||
|
"""
|
||||||
|
Get the preview image path for a template based on language.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
template_path: Template path like "1080x1920/image_default.html"
|
||||||
|
language: Language code, either "zh_CN" or "en"
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Path to preview image in docs/images/
|
||||||
|
"""
|
||||||
|
# Extract size and template name from path
|
||||||
|
# e.g., "1080x1920/image_default.html" -> size="1080x1920", name="image_default"
|
||||||
|
path_parts = template_path.split('/')
|
||||||
|
if len(path_parts) >= 2:
|
||||||
|
size = path_parts[0] # e.g., "1080x1920"
|
||||||
|
template_file = path_parts[1] # e.g., "image_default.html"
|
||||||
|
template_name = template_file.replace('.html', '') # e.g., "image_default"
|
||||||
|
|
||||||
|
# Build preview image path
|
||||||
|
# Format: docs/images/{size}/{template_name}.jpg or {template_name}_en.jpg
|
||||||
|
# Chinese uses Chinese preview, all other languages use English preview for better i18n
|
||||||
|
suffix = "" if language == "zh_CN" else "_en"
|
||||||
|
|
||||||
|
# Try different image extensions
|
||||||
|
for ext in ['.jpg', '.png']:
|
||||||
|
preview_path = f"docs/images/{size}/{template_name}{suffix}{ext}"
|
||||||
|
if os.path.exists(preview_path):
|
||||||
|
return preview_path
|
||||||
|
|
||||||
|
# Fallback: try without language suffix (for templates with only one version)
|
||||||
|
for ext in ['.jpg', '.png']:
|
||||||
|
preview_path = f"docs/images/{size}/{template_name}{ext}"
|
||||||
|
if os.path.exists(preview_path):
|
||||||
|
return preview_path
|
||||||
|
|
||||||
|
# If no preview found, return empty string
|
||||||
|
return ""
|
||||||
|
|
||||||
with st.container(border=True):
|
with st.container(border=True):
|
||||||
st.markdown(f"**{tr('section.template')}**")
|
st.markdown(f"**{tr('section.template')}**")
|
||||||
|
|
||||||
@@ -284,18 +324,13 @@ def render_style_config(pixelle_video):
|
|||||||
st.warning(f"No {template_type_options[selected_template_type]} templates found. Please select a different type or add templates.")
|
st.warning(f"No {template_type_options[selected_template_type]} templates found. Please select a different type or add templates.")
|
||||||
st.stop()
|
st.stop()
|
||||||
|
|
||||||
# Build display options with group separators
|
# Build orientation i18n mapping
|
||||||
ORIENTATION_I18N = {
|
ORIENTATION_I18N = {
|
||||||
'portrait': tr('orientation.portrait'),
|
'portrait': tr('orientation.portrait'),
|
||||||
'landscape': tr('orientation.landscape'),
|
'landscape': tr('orientation.landscape'),
|
||||||
'square': tr('orientation.square')
|
'square': tr('orientation.square')
|
||||||
}
|
}
|
||||||
|
|
||||||
display_options = []
|
|
||||||
template_paths_ordered = [] # Use ordered list instead of dict to avoid key conflicts
|
|
||||||
default_index = 0
|
|
||||||
current_index = 0
|
|
||||||
|
|
||||||
# Get default template from config
|
# Get default template from config
|
||||||
template_config = pixelle_video.config.get("template", {})
|
template_config = pixelle_video.config.get("template", {})
|
||||||
config_default_template = template_config.get("default_template", "1080x1920/image_default.html")
|
config_default_template = template_config.get("default_template", "1080x1920/image_default.html")
|
||||||
@@ -312,68 +347,145 @@ def render_style_config(pixelle_video):
|
|||||||
}
|
}
|
||||||
type_specific_default = type_default_templates.get(selected_template_type, config_default_template)
|
type_specific_default = type_default_templates.get(selected_template_type, config_default_template)
|
||||||
|
|
||||||
|
# Initialize selected template in session state if not exists
|
||||||
|
if 'selected_template' not in st.session_state:
|
||||||
|
st.session_state['selected_template'] = type_specific_default
|
||||||
|
|
||||||
|
# Collect size groups and prepare tabs
|
||||||
|
size_groups = []
|
||||||
|
size_labels = []
|
||||||
|
|
||||||
for size, templates in grouped_templates.items():
|
for size, templates in grouped_templates.items():
|
||||||
if not templates:
|
if not templates:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# Filter templates to only include those with proper naming convention
|
||||||
|
# Only show templates starting with static_, image_, or video_
|
||||||
|
valid_templates = []
|
||||||
|
for template in templates:
|
||||||
|
template_name = template.display_info.name
|
||||||
|
if template_name.startswith(('static_', 'image_', 'video_')):
|
||||||
|
valid_templates.append(template)
|
||||||
|
|
||||||
|
# Skip if no valid templates after filtering
|
||||||
|
if not valid_templates:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Separate templates into two groups: with preview and without preview
|
||||||
|
templates_with_preview = []
|
||||||
|
templates_without_preview = []
|
||||||
|
|
||||||
|
for template in valid_templates:
|
||||||
|
preview_path = get_template_preview_path(template.template_path, current_lang)
|
||||||
|
if preview_path and os.path.exists(preview_path):
|
||||||
|
templates_with_preview.append(template)
|
||||||
|
else:
|
||||||
|
templates_without_preview.append(template)
|
||||||
|
|
||||||
|
# Skip this group if no templates at all
|
||||||
|
if not templates_with_preview and not templates_without_preview:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Combine: templates with preview first, then without preview
|
||||||
|
all_templates = templates_with_preview + templates_without_preview
|
||||||
|
|
||||||
# Get orientation from first template in group
|
# Get orientation from first template in group
|
||||||
orientation = ORIENTATION_I18N.get(
|
orientation = ORIENTATION_I18N.get(
|
||||||
templates[0].display_info.orientation,
|
all_templates[0].display_info.orientation,
|
||||||
templates[0].display_info.orientation
|
all_templates[0].display_info.orientation
|
||||||
)
|
)
|
||||||
width = templates[0].display_info.width
|
width = all_templates[0].display_info.width
|
||||||
height = templates[0].display_info.height
|
height = all_templates[0].display_info.height
|
||||||
|
|
||||||
# Add group separator
|
# Create tab label
|
||||||
separator = f"─── {orientation} {width}×{height} ───"
|
tab_label = f"{orientation} {width}×{height}"
|
||||||
display_options.append(separator)
|
size_labels.append(tab_label)
|
||||||
template_paths_ordered.append(None) # Separator has no template path
|
size_groups.append(all_templates)
|
||||||
current_index += 1
|
|
||||||
|
# Create tabs for each size group (wrapped in expander)
|
||||||
|
with st.expander(tr("template.gallery_view"), expanded=True):
|
||||||
|
if size_groups:
|
||||||
|
tabs = st.tabs(size_labels)
|
||||||
|
|
||||||
|
for tab, all_templates in zip(tabs, size_groups):
|
||||||
|
with tab:
|
||||||
|
# Create grid layout (5 columns)
|
||||||
|
num_cols = 5
|
||||||
|
cols = st.columns(num_cols)
|
||||||
|
|
||||||
|
for idx, template in enumerate(all_templates):
|
||||||
|
col_idx = idx % num_cols
|
||||||
|
with cols[col_idx]:
|
||||||
|
# Get preview image path
|
||||||
|
preview_path = get_template_preview_path(template.template_path, current_lang)
|
||||||
|
|
||||||
|
# Display preview image or placeholder
|
||||||
|
if preview_path and os.path.exists(preview_path):
|
||||||
|
st.image(preview_path, use_container_width=True)
|
||||||
|
else:
|
||||||
|
# Placeholder for templates without preview (fixed height, compact layout)
|
||||||
|
st.markdown(
|
||||||
|
f"""
|
||||||
|
<div style="
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
height: 150px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 8px;
|
||||||
|
color: white;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
padding: 10px;
|
||||||
|
">
|
||||||
|
<div style="
|
||||||
|
font-size: 14px;
|
||||||
|
opacity: 0.95;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 5;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
word-break: break-all;
|
||||||
|
">{template.display_info.name}</div>
|
||||||
|
</div>
|
||||||
|
""",
|
||||||
|
unsafe_allow_html=True
|
||||||
|
)
|
||||||
|
|
||||||
|
# Select button (unified label)
|
||||||
|
is_selected = (st.session_state['selected_template'] == template.template_path)
|
||||||
|
button_label = f"{tr('template.selected')}" if is_selected else tr('template.select_button')
|
||||||
|
button_type = "primary" if is_selected else "secondary"
|
||||||
|
|
||||||
|
if st.button(
|
||||||
|
button_label,
|
||||||
|
key=f"template_{template.template_path}",
|
||||||
|
use_container_width=True,
|
||||||
|
type=button_type,
|
||||||
|
):
|
||||||
|
st.session_state['selected_template'] = template.template_path
|
||||||
|
st.rerun()
|
||||||
|
else:
|
||||||
|
st.warning(tr("template.no_templates_with_preview"))
|
||||||
|
|
||||||
# Add templates in this group
|
# Display selected template name (inside expander, below tabs)
|
||||||
for t in templates:
|
frame_template = st.session_state['selected_template']
|
||||||
display_name = f" {t.display_info.name}"
|
|
||||||
display_options.append(display_name)
|
# Find the selected template's display name
|
||||||
template_paths_ordered.append(t.template_path) # Add to ordered list
|
selected_template_name = None
|
||||||
|
for size, templates in grouped_templates.items():
|
||||||
# Set default: priority is config > type-specific default > first in portrait
|
for template in templates:
|
||||||
if t.template_path == config_default_template:
|
if template.template_path == frame_template:
|
||||||
default_index = current_index
|
selected_template_name = template.display_info.name
|
||||||
elif default_index == 0 and t.template_path == type_specific_default:
|
break
|
||||||
default_index = current_index
|
if selected_template_name:
|
||||||
elif default_index == 0 and t.display_info.orientation == 'portrait':
|
break
|
||||||
default_index = current_index
|
|
||||||
|
if selected_template_name:
|
||||||
current_index += 1
|
st.info(f"📋 {tr('template.selected_template')}: **{selected_template_name}**")
|
||||||
|
|
||||||
# Dropdown with grouped display
|
|
||||||
# Create unique display strings by appending hidden unique identifier
|
|
||||||
# This ensures Streamlit doesn't confuse templates with same name in different groups
|
|
||||||
unique_display_options = []
|
|
||||||
for i, option in enumerate(display_options):
|
|
||||||
# Add zero-width space characters as unique identifier (invisible to users)
|
|
||||||
unique_option = option + ("\u200B" * i) # \u200B is zero-width space
|
|
||||||
unique_display_options.append(unique_option)
|
|
||||||
|
|
||||||
selected_unique_option = st.selectbox(
|
|
||||||
tr("template.select"),
|
|
||||||
unique_display_options,
|
|
||||||
index=default_index,
|
|
||||||
label_visibility="collapsed",
|
|
||||||
help=tr("template.select_help")
|
|
||||||
)
|
|
||||||
|
|
||||||
# Get index from selected unique option
|
|
||||||
selected_index = unique_display_options.index(selected_unique_option)
|
|
||||||
|
|
||||||
# Check if separator is selected (shouldn't happen, but handle it)
|
|
||||||
if display_options[selected_index].startswith("───"):
|
|
||||||
st.warning(tr("template.separator_selected"))
|
|
||||||
st.stop()
|
|
||||||
|
|
||||||
# Get full template path directly by index
|
|
||||||
frame_template = template_paths_ordered[selected_index]
|
|
||||||
|
|
||||||
|
|
||||||
# Display video size from template
|
# Display video size from template
|
||||||
from pixelle_video.utils.template_util import parse_template_size
|
from pixelle_video.utils.template_util import parse_template_size
|
||||||
video_width, video_height = parse_template_size(frame_template)
|
video_width, video_height = parse_template_size(frame_template)
|
||||||
|
|||||||
@@ -103,6 +103,11 @@
|
|||||||
"template.preview_image_help": "Supports local path or URL",
|
"template.preview_image_help": "Supports local path or URL",
|
||||||
"template.preview_caption": "Template Preview: {template}",
|
"template.preview_caption": "Template Preview: {template}",
|
||||||
"template.custom_parameters": "Custom Parameters",
|
"template.custom_parameters": "Custom Parameters",
|
||||||
|
"template.gallery_view": "Template Gallery",
|
||||||
|
"template.select_button": "Check",
|
||||||
|
"template.selected": "Checked",
|
||||||
|
"template.selected_template": "Current Template",
|
||||||
|
"template.no_templates_with_preview": "⚠️ No templates available for this type",
|
||||||
"image.not_required": "Current template does not require image generation",
|
"image.not_required": "Current template does not require image generation",
|
||||||
"image.not_required_hint": "The selected template is text-only and does not need images. Benefits: ⚡ Faster generation 💰 Lower cost",
|
"image.not_required_hint": "The selected template is text-only and does not need images. Benefits: ⚡ Faster generation 💰 Lower cost",
|
||||||
"video.title": "🎬 Video Settings",
|
"video.title": "🎬 Video Settings",
|
||||||
|
|||||||
@@ -103,6 +103,11 @@
|
|||||||
"template.preview_image_help": "支持本地路径或 URL",
|
"template.preview_image_help": "支持本地路径或 URL",
|
||||||
"template.preview_caption": "模板预览:{template}",
|
"template.preview_caption": "模板预览:{template}",
|
||||||
"template.custom_parameters": "自定义参数",
|
"template.custom_parameters": "自定义参数",
|
||||||
|
"template.gallery_view": "模板库",
|
||||||
|
"template.select_button": "选择",
|
||||||
|
"template.selected": "已选",
|
||||||
|
"template.selected_template": "当前模板",
|
||||||
|
"template.no_templates_with_preview": "⚠️ 该类型暂无可用模板",
|
||||||
"image.not_required": "当前模板不需要插图生成",
|
"image.not_required": "当前模板不需要插图生成",
|
||||||
"image.not_required_hint": "您选择的模板是纯文本模板,无需生成图片。这将:⚡ 加快生成速度 💰 降低生成成本",
|
"image.not_required_hint": "您选择的模板是纯文本模板,无需生成图片。这将:⚡ 加快生成速度 💰 降低生成成本",
|
||||||
"video.title": "🎬 视频设置",
|
"video.title": "🎬 视频设置",
|
||||||
|
|||||||