feat(seed): add Instagram channel-promo template to FlatRender (local)
- scripts/seed_instagram_promo.py — dedicated seeder for the 5-scene FlexStory IG promo (the generic seeder only handles CharacterStory's 2-text-per-scene shape). Scenes keyed `<BlockId>__<n>` (render decodes the block from the key); each scene's content-elements are that block's real fields (Text + Media); colours as shared. render_remotion_comp = FlexStory-<asp> so GetFlexStoryProps routes it. - public/template-media/InstagramPromo* — thumbnails, per-scene stills, preview MP4. Applied to local fr2-postgres + fr2-frontend: container fr-instagram-promo (3 aspects, 15 scenes, 138 fields), served by the gateway and the /templates detail page (200). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
After Width: | Height: | Size: 983 KiB |
|
After Width: | Height: | Size: 661 KiB |
|
After Width: | Height: | Size: 813 KiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 661 KiB |
|
After Width: | Height: | Size: 983 KiB |
|
After Width: | Height: | Size: 661 KiB |
|
After Width: | Height: | Size: 813 KiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 661 KiB |
|
After Width: | Height: | Size: 983 KiB |
|
After Width: | Height: | Size: 661 KiB |
|
After Width: | Height: | Size: 813 KiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 661 KiB |
@@ -0,0 +1,96 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# Seeds the Instagram channel-promo FlexStory template into content.* so it appears
|
||||||
|
# in the local FlatRender studio/templates, editable. Each scene's key is
|
||||||
|
# "<BlockId>__<n>" (the render boundary decodes the block from the key prefix); the
|
||||||
|
# scene's content-elements are that block's real fields. render_remotion_comp =
|
||||||
|
# FlexStory-<asp> so GetFlexStoryProps routes it (internal.go:327).
|
||||||
|
#
|
||||||
|
# Usage: python scripts/seed_instagram_promo.py | docker exec -i fr2-postgres psql -U postgres -d flatrender
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
NS = uuid.UUID("11111111-2222-3333-4444-555555555555")
|
||||||
|
def uid(s): return str(uuid.uuid5(NS, s))
|
||||||
|
def q(s): return "'" + str(s).replace("'", "''") + "'"
|
||||||
|
|
||||||
|
ASPECTS = [("16x9", 1920, 1080, "16:9"), ("1x1", 1080, 1080, "1:1"), ("9x16", 1080, 1920, "9:16")]
|
||||||
|
CTITLES = {"accentColor": "رنگ اصلی", "secondaryColor": "رنگ دوم", "backgroundColor": "پسزمینه", "textColor": "رنگ متن"}
|
||||||
|
|
||||||
|
TID, SLUG = "InstagramPromo", "fr-instagram-promo"
|
||||||
|
NAME = "تبلیغ کانال اینستاگرام"
|
||||||
|
DESC = "تبلیغ حرفهای صفحهٔ اینستاگرام با نمای واقعی پروفایل (تم روشن)؛ معرفی، نمایش پستها (عکس و ویدیو)، آمار و دعوت به فالو. کاملاً قابل ویرایش و انعطافپذیر."
|
||||||
|
COLORS = [("accentColor", "#dc2743"), ("secondaryColor", "#7c5cff"), ("backgroundColor", "#f7f4fa"), ("textColor", "#15151a")]
|
||||||
|
|
||||||
|
# (blockId, persian scene label, durationSec, [(key, title, default, type)])
|
||||||
|
T = "Text"; M = "Media"
|
||||||
|
SCENES = [
|
||||||
|
("IGIntro", "شروع (لوگوی اینستاگرام)", 3, [
|
||||||
|
("badge", "نشان", "اینستاگرام", T), ("headline", "تیتر", "صفحهٔ ما را دنبال کنید", T),
|
||||||
|
("subtitle", "زیرعنوان", "هر روز یک طرح تازه", T)]),
|
||||||
|
("IGProfile", "صفحهٔ اینستاگرام (واقعی)", 5, [
|
||||||
|
("headline", "تیتر بالای صفحه", "صفحهٔ ما را دنبال کنید", T), ("handle", "آیدی", "flat.studio", T),
|
||||||
|
("name", "نام صفحه", "استودیو فلت", T), ("category", "دستهبندی", "هنر و طراحی", T),
|
||||||
|
("bio1", "بیو — خط ۱", "هر روز یک طرح تازه ✨", T), ("bio2", "بیو — خط ۲", "آموزش، قالب و الهام برای طراحان", T),
|
||||||
|
("bio3", "بیو — خط ۳", "سفارش و دانلود 👇", T), ("link", "لینک", "flat.studio/shop", T),
|
||||||
|
("posts", "تعداد پست", "۳۲۰", T), ("followers", "دنبالکننده", "۲۴٫۸ هزار", T), ("following", "دنبالشده", "۱۸۰", T),
|
||||||
|
("hi1", "هایلایت ۱", "جدید", T), ("hi2", "هایلایت ۲", "قالبها", T), ("hi3", "هایلایت ۳", "آموزش", T), ("hi4", "هایلایت ۴", "نمونهکار", T),
|
||||||
|
("followLabel", "متن دکمهٔ دنبال", "دنبال کردن", T), ("messageLabel", "متن دکمهٔ پیام", "پیام", T),
|
||||||
|
("avatar", "تصویر پروفایل", "", M),
|
||||||
|
("post1", "پست شبکه ۱ (عکس/ویدیو)", "", M), ("post2", "پست شبکه ۲ (عکس/ویدیو)", "", M), ("post3", "پست شبکه ۳ (عکس/ویدیو)", "", M),
|
||||||
|
("post4", "پست شبکه ۴ (عکس/ویدیو)", "", M), ("post5", "پست شبکه ۵ (عکس/ویدیو)", "", M), ("post6", "پست شبکه ۶ (عکس/ویدیو)", "", M)]),
|
||||||
|
("IGFeed", "نمایش محتوا (شبکهٔ پستها)", 4, [
|
||||||
|
("caption", "عنوان بخش", "محتوای ما را ببینید", T),
|
||||||
|
("post1", "پست ۱ (عکس/ویدیو)", "", M), ("post2", "پست ۲ (عکس/ویدیو)", "", M), ("post3", "پست ۳ (عکس/ویدیو)", "", M),
|
||||||
|
("post4", "پست ۴ (عکس/ویدیو)", "", M), ("post5", "پست ۵ (عکس/ویدیو)", "", M), ("post6", "پست ۶ (عکس/ویدیو)", "", M)]),
|
||||||
|
("IGStats", "اعتمادسازی (آمار صفحه)", 3, [
|
||||||
|
("bigValue", "عدد اصلی", "۲۴۸۰۰", T), ("bigLabel", "برچسب عدد اصلی", "دنبالکننده", T),
|
||||||
|
("stat2Value", "آمار ۲ — مقدار", "۱۲۰۰۰۰۰", T), ("stat2Label", "آمار ۲ — برچسب", "پسند", T),
|
||||||
|
("stat3Value", "آمار ۳ — مقدار", "۳۲۰", T), ("stat3Label", "آمار ۳ — برچسب", "پست", T),
|
||||||
|
("proofLine", "جملهٔ اعتماد", "به جمع هزاران دنبالکنندهٔ ما بپیوندید", T)]),
|
||||||
|
("IGFollowCTA", "فراخوان دنبالکردن", 3, [
|
||||||
|
("headline", "تیتر فراخوان", "همین حالا دنبال کنید", T), ("handle", "آیدی صفحه", "@flat.studio", T),
|
||||||
|
("buttonLabel", "متن دکمه", "دنبال کردن", T), ("followedLabel", "متن بعد از دنبال", "دنبال شد", T),
|
||||||
|
("footer", "زیرنویس", "لینک در بایو 👆", T)]),
|
||||||
|
]
|
||||||
|
DUR = sum(s[2] for s in SCENES)
|
||||||
|
|
||||||
|
def swatch_svg(colors):
|
||||||
|
return f'<svg xmlns="http://www.w3.org/2000/svg" width="200" height="40">' + "".join(f'<rect x="{i*50}" y="0" width="50" height="40" fill="{c}"/>' for i, c in enumerate(colors)) + '</svg>'
|
||||||
|
def icon_svg(hexv):
|
||||||
|
return f'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10" fill="{hexv}"/></svg>'
|
||||||
|
|
||||||
|
out = ["BEGIN;", f"DELETE FROM content.project_containers WHERE slug = {q(SLUG)};"]
|
||||||
|
cid = uid("c-" + TID)
|
||||||
|
thumb16 = f"/template-media/{TID}-16x9.png"
|
||||||
|
preview = f"/template-media/{TID}.mp4"
|
||||||
|
out.append(
|
||||||
|
"INSERT INTO content.project_containers (id,tenant_id,slug,name,description,image,demo,full_demo,mini_demo,"
|
||||||
|
"is_published,is_premium,is_mockup,primary_mode,sort) VALUES ("
|
||||||
|
f"{q(cid)},NULL,{q(SLUG)},{q(NAME)},{q(DESC)},{q(thumb16)},{q(preview)},{q(preview)},{q(preview)},TRUE,FALSE,FALSE,'FLEXIBLE',2);")
|
||||||
|
|
||||||
|
for (asp, w, h, aspstr) in ASPECTS:
|
||||||
|
pid = uid(f"p-{TID}-{asp}")
|
||||||
|
thumb = f"/template-media/{TID}-{asp}.png"
|
||||||
|
out.append(
|
||||||
|
"INSERT INTO content.projects (id,container_id,name,image,full_demo,original_width,original_height,aspect,"
|
||||||
|
"project_duration_sec,free_fps,choose_mode,resolution,render_engine,render_remotion_comp,is_published,sort) VALUES ("
|
||||||
|
f"{q(pid)},{q(cid)},{q(aspstr)},{q(thumb)},{q(preview)},{w},{h},{q(aspstr)},"
|
||||||
|
f"{DUR},30,'FLEXIBLE','FullHD','Remotion',{q('FlexStory-'+asp)},TRUE,0);")
|
||||||
|
for sidx, (block, label, dur, fields) in enumerate(SCENES, 1):
|
||||||
|
skid = uid(f"s-{TID}-{asp}-{sidx}")
|
||||||
|
skey = f"{block}__{sidx}"
|
||||||
|
scimg = f"/template-media/{TID}-{asp}-c{sidx}.png"
|
||||||
|
out.append(
|
||||||
|
"INSERT INTO content.scenes (id,project_id,key,title,image,snapshot_url,demo,scene_color_svg,default_duration_sec,sort) VALUES ("
|
||||||
|
f"{q(skid)},{q(pid)},{q(skey)},{q(label)},{q(scimg)},{q(scimg)},{q(preview)},{q(swatch_svg([c for _,c in COLORS]))},{dur},{sidx-1});")
|
||||||
|
for pos, (k, title, val, typ) in enumerate(fields):
|
||||||
|
out.append(
|
||||||
|
"INSERT INTO content.scene_content_elements (id,scene_id,key,title,type,default_value,position_in_container,direction_layer_value) VALUES ("
|
||||||
|
f"{q(uid(f'ce-{TID}-{asp}-{sidx}-{k}'))},{q(skid)},{q(k)},{q(title)},{q(typ)},{q(val)},{pos},{1 if typ=='Text' else 0});")
|
||||||
|
for si, (k, hexv) in enumerate(COLORS):
|
||||||
|
out.append(
|
||||||
|
"INSERT INTO content.shared_colors (id,project_id,element_key,title,icon,attr_value,default_color,sort) VALUES ("
|
||||||
|
f"{q(uid(f'sc-{TID}-{asp}-{k}'))},{q(pid)},{q(k)},{q(CTITLES[k])},{q(icon_svg(hexv))},'fill',{q(hexv)},{si});")
|
||||||
|
|
||||||
|
out.append("COMMIT;")
|
||||||
|
out.append(f"SELECT slug, name, primary_mode FROM content.project_containers WHERE slug = {q(SLUG)};")
|
||||||
|
print("\n".join(out))
|
||||||