feat(render+templates): Remotion engine, 16 branded templates (incl. 3D), seconds pricing, coming-soon
CI/CD / CI · Web (tsc) (push) Successful in 1m21s
CI/CD / Deploy · full stack (push) Failing after 20s

Render engine
- Add Remotion (code-based) as a 2nd render engine alongside After Effects.
  node-agent dispatches on Job.Engine; RunRemotion maps bindings -> --props,
  renders native then ffmpeg-scales to the quality tier (aspect-preserving).
- content.projects.render_engine + render_remotion_comp (migration 32);
  render-svc claim resolves engine and routes (skips .aep for Remotion).
- Admin TemplatesAdmin gains an engine picker + Remotion composition id field.

Template pack (services/remotion)
- 16 branded, Persian (Vazirmatn), color- and text-editable templates, each in
  3 aspects (16:9 / 1:1 / 9:16): LogoMotion, Opener, InstaPromo, YouTubeIntro,
  Slideshow, HappyBirthday, SalePromo, QuoteCard, EventInvite, Countdown,
  GlitterReveal (editable logo image), NowruzGreeting (animated characters),
  and 4 cinematic 3D templates via @remotion/three (Hero3D, Nowruz3D,
  Birthday3D, Promo3D) with reflections + bloom/DOF/vignette.
- scripts/seed_remotion_templates.py seeds containers/projects/scenes/colors.

Pricing
- Rewrite /pricing to the seconds-based model (charge = length x resolution),
  data-driven from /v1/plans, Toman, broker checkout.

Coming-soon
- Persian experimental-build overlay on all pages (launch date + countdown).

Fixes
- middleware matcher bypasses all static asset paths; catalog mapping passes
  cover image + preview video so real thumbnails render.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
soroush.asadi
2026-06-21 15:52:52 +03:30
parent b9b91397b0
commit 4f04f6bf75
137 changed files with 8942 additions and 135 deletions
+226
View File
@@ -0,0 +1,226 @@
/**
* Registry of FlatRender branded templates. Each entry is rendered into the
* three supported aspects (16:9 / 1:1 / 9:16) by Root.tsx, producing composition
* ids like "LogoMotion-16x9". Every template uses Persian text presets + the
* shared colour props so the studio can offer one consistent edit experience.
*/
import React from "react";
import type { AnyZodObject } from "zod";
import { BRAND } from "./lib/branding";
import { LogoMotion, logoMotionSchema } from "./compositions/LogoMotion";
import { Opener, openerSchema } from "./compositions/Opener";
import { InstaPromo, instaPromoSchema } from "./compositions/InstaPromo";
import { YouTubeIntro, youTubeIntroSchema } from "./compositions/YouTubeIntro";
import { Slideshow, slideshowSchema } from "./compositions/Slideshow";
import { HappyBirthday, happyBirthdaySchema } from "./compositions/HappyBirthday";
import { SalePromo, salePromoSchema } from "./compositions/SalePromo";
import { QuoteCard, quoteCardSchema } from "./compositions/QuoteCard";
import { EventInvite, eventInviteSchema } from "./compositions/EventInvite";
import { Countdown, countdownSchema } from "./compositions/Countdown";
import { GlitterReveal, glitterRevealSchema } from "./compositions/GlitterReveal";
import { NowruzGreeting, nowruzGreetingSchema } from "./compositions/NowruzGreeting";
import { Hero3D, hero3DSchema } from "./compositions/Hero3D";
import { Nowruz3D, nowruz3DSchema } from "./compositions/Nowruz3D";
import { Birthday3D, birthday3DSchema } from "./compositions/Birthday3D";
import { Promo3D, promo3DSchema } from "./compositions/Promo3D";
export interface TemplateDef {
/** Base id; the registered composition ids are `${id}-${aspect}`. */
id: string;
/** Persian display name (used when seeding the site catalog). */
name: string;
/** Short Persian description for the catalog. */
description: string;
component: React.FC<any>; // eslint-disable-line @typescript-eslint/no-explicit-any
schema: AnyZodObject;
durationSec: number;
defaultProps: Record<string, unknown>;
}
const c = (accent: string, secondary: string, bg: string) => ({
accentColor: accent,
secondaryColor: secondary,
backgroundColor: bg,
textColor: BRAND.white,
});
export const TEMPLATES: TemplateDef[] = [
{
id: "LogoMotion",
name: "موشن لوگو",
description: "نمایش حرفه‌ای لوگو و نام برند با درخشش و حرکت",
component: LogoMotion,
schema: logoMotionSchema,
durationSec: 5,
defaultProps: { brandText: "فلت‌رندر", tagline: "موشن، ساده و حرفه‌ای", ...c(BRAND.blue, BRAND.purple, "#04060f") },
},
{
id: "Opener",
name: "تیتراژ آغازین",
description: "شروع سینمایی برای ویدیو با عنوان و زیرعنوان",
component: Opener,
schema: openerSchema,
durationSec: 5,
defaultProps: { kicker: "تقدیم می‌کند", title: "یک شروع تازه", subtitle: "داستان شما از همین‌جا آغاز می‌شود", ...c(BRAND.cyan, "#6366f1", "#0a0a12") },
},
{
id: "InstaPromo",
name: "تبلیغ پیج اینستاگرام",
description: "معرفی و تبلیغ صفحهٔ اینستاگرام با دعوت به فالو",
component: InstaPromo,
schema: instaPromoSchema,
durationSec: 5,
defaultProps: { handle: "@flatrender", headline: "پیج ما را دنبال کنید", subtext: "هر روز محتوای تازه و الهام‌بخش", cta: "فالو کنید", ...c(BRAND.pink, BRAND.amber, "#140a12") },
},
{
id: "YouTubeIntro",
name: "اینترو کانال یوتیوب",
description: "اینترو حرفه‌ای کانال یوتیوب با دکمهٔ سابسکرایب",
component: YouTubeIntro,
schema: youTubeIntroSchema,
durationSec: 5,
defaultProps: { channelName: "کانال فلت‌رندر", subtitle: "آموزش، ترفند و انگیزه", cta: "سابسکرایب کنید", ...c("#ff4d4d", BRAND.purple, "#0c0810") },
},
{
id: "Slideshow",
name: "اسلایدشو",
description: "نمایش پشت‌سرهم چند پیام یا ویژگی به‌صورت اسلاید",
component: Slideshow,
schema: slideshowSchema,
durationSec: 9,
defaultProps: { title: "چرا فلت‌رندر؟", slide1: "ساخت ویدیو در چند دقیقه", slide2: "بدون نیاز به دانش فنی", slide3: "خروجی با کیفیت حرفه‌ای", ...c(BRAND.green, "#3b82f6", "#060b0a") },
},
{
id: "HappyBirthday",
name: "تولدت مبارک",
description: "کارت تبریک تولد با کاغذرنگی و نام شخص",
component: HappyBirthday,
schema: happyBirthdaySchema,
durationSec: 6,
defaultProps: { greeting: "تولدت مبارک", name: "سارا", message: "بهترین‌ها را برایت آرزومندیم 🎉", ...c(BRAND.pink, "#fde047", "#140a18") },
},
{
id: "SalePromo",
name: "فروش ویژه",
description: "بنر تبلیغاتی فروش و تخفیف با دعوت به خرید",
component: SalePromo,
schema: salePromoSchema,
durationSec: 5,
defaultProps: { badge: "۵۰٪ تخفیف", headline: "فروش ویژهٔ پایان فصل", subtext: "فقط تا پایان همین هفته", cta: "همین حالا خرید کنید", ...c(BRAND.amber, BRAND.pink, "#120a08") },
},
{
id: "QuoteCard",
name: "کارت نقل‌قول",
description: "نمایش جملهٔ انگیزشی یا نقل‌قول با نام گوینده",
component: QuoteCard,
schema: quoteCardSchema,
durationSec: 6,
defaultProps: { quote: "موفقیت، مجموع تلاش‌های کوچکِ هر روز است.", author: "فلت‌رندر", ...c(BRAND.cyan, "#6366f1", "#0a0a12") },
},
{
id: "EventInvite",
name: "دعوت‌نامهٔ رویداد",
description: "دعوت‌نامهٔ شیک برای رویداد با تاریخ و مکان",
component: EventInvite,
schema: eventInviteSchema,
durationSec: 6,
defaultProps: { kicker: "دعوت‌نامه", eventTitle: "همایش سالانهٔ نوآوری", date: "۱۵ مهر ۱۴۰۳", location: "تهران، سالن همایش‌ها", cta: "ثبت‌نام کنید", ...c(BRAND.purple, BRAND.blue, "#0a0814") },
},
{
id: "Countdown",
name: "شمارش معکوس",
description: "شمارش معکوس هیجان‌انگیز برای شروع یک رویداد",
component: Countdown,
schema: countdownSchema,
durationSec: 8,
defaultProps: { title: "شروع رویداد تا", startNumber: 5, goText: "شروع!", subtitle: "آماده‌اید؟", ...c(BRAND.blue, BRAND.cyan, "#04060f") },
},
{
id: "GlitterReveal",
name: "نمایش لوگو با غبار درخشان",
description: "نمایش جادویی لوگو با ذرات درخشان؛ لوگو و متن قابل ویرایش",
component: GlitterReveal,
schema: glitterRevealSchema,
durationSec: 6,
defaultProps: { brandText: "فلت‌رندر", tagline: "موشن، ساده و حرفه‌ای", logoUrl: "", ...c(BRAND.blue, BRAND.purple, "#05040e") },
},
{
id: "NowruzGreeting",
name: "تبریک نوروز",
description: "صحنهٔ بهاری نوروز با شخصیت‌های متحرک؛ حاجی‌فیروز، ماهی قرمز و سبزه",
component: NowruzGreeting,
schema: nowruzGreetingSchema,
durationSec: 7.5,
defaultProps: {
greeting: "نوروز مبارک",
subtitle: "سال نو پیروز و شادمان",
message: "۱۴۰۶",
accentColor: "#f5b942",
secondaryColor: "#e23b3b",
backgroundColor: "#1fb6b0",
textColor: "#fdf6e3",
},
},
{
id: "Hero3D",
name: "نمایش سه‌بعدی برند",
description: "نمایش حرفه‌ای و سه‌بعدی لوگو و برند با نورپردازی و جلوه‌های واقعی",
component: Hero3D,
schema: hero3DSchema,
durationSec: 6,
defaultProps: { brandText: "فلت‌رندر", tagline: "موشن، ساده و حرفه‌ای", ...c(BRAND.blue, BRAND.purple, "#04060f") },
},
{
id: "Nowruz3D",
name: "تبریک نوروز سه‌بعدی",
description: "صحنهٔ سه‌بعدی نوروز با حاجی‌فیروز، سفرهٔ هفت‌سین و نورپردازی سینمایی",
component: Nowruz3D,
schema: nowruz3DSchema,
durationSec: 7,
defaultProps: {
greeting: "نوروز مبارک",
subtitle: "سال نو پیروز و شادمان",
message: "۱۴۰۶",
accentColor: "#f5c542",
secondaryColor: "#e23b3b",
backgroundColor: "#1a1228",
textColor: "#fdf6e3",
},
},
{
id: "Birthday3D",
name: "تولد سه‌بعدی",
description: "صحنهٔ سه‌بعدی تولد با کیک و شمع‌های روشن، بادکنک و کاغذرنگی",
component: Birthday3D,
schema: birthday3DSchema,
durationSec: 6,
defaultProps: {
greeting: "تولدت مبارک",
name: "سارا",
message: "بهترین‌ها را برایت آرزومندیم 🎉",
accentColor: "#fb7185",
secondaryColor: "#a855f7",
backgroundColor: "#1a1226",
textColor: "#fdf6e3",
},
},
{
id: "Promo3D",
name: "فروش ویژه سه‌بعدی",
description: "تبلیغ سه‌بعدی فروش و تخفیف با جعبه‌های هدیه و نورپردازی سینمایی",
component: Promo3D,
schema: promo3DSchema,
durationSec: 6,
defaultProps: {
badge: "۵۰٪ تخفیف",
headline: "فروش ویژهٔ پایان فصل",
subtext: "فقط تا پایان همین هفته",
cta: "همین حالا خرید کنید",
accentColor: "#f59e0b",
secondaryColor: "#fb7185",
backgroundColor: "#140e1f",
textColor: "#ffffff",
},
},
];