feat(render+templates): Remotion engine, 16 branded templates (incl. 3D), seconds pricing, coming-soon
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:
@@ -176,6 +176,8 @@ public class TemplateService(ContentDbContext db)
|
||||
ProjectDurationSec = req.ProjectDurationSec, MinDurationSec = req.MinDurationSec,
|
||||
MaxDurationSec = req.MaxDurationSec, FreeFps = req.FreeFps, ChooseMode = chooseMode,
|
||||
Resolution = resolution, VipFactor = req.VipFactor, RenderAepComp = req.RenderAepComp,
|
||||
RenderEngine = string.IsNullOrWhiteSpace(req.RenderEngine) ? "AfterEffects" : req.RenderEngine,
|
||||
RenderRemotionComp = req.RenderRemotionComp,
|
||||
IsPublished = req.IsPublished, Sort = req.Sort
|
||||
};
|
||||
|
||||
@@ -225,6 +227,7 @@ public class TemplateService(ContentDbContext db)
|
||||
ProjectDurationSec = src.ProjectDurationSec, MinDurationSec = src.MinDurationSec,
|
||||
MaxDurationSec = src.MaxDurationSec, FreeFps = src.FreeFps, ChooseMode = src.ChooseMode,
|
||||
Resolution = resolution, VipFactor = src.VipFactor, RenderAepComp = src.RenderAepComp,
|
||||
RenderEngine = src.RenderEngine, RenderRemotionComp = src.RenderRemotionComp,
|
||||
SharedLayerImage = src.SharedLayerImage, SharedColorsSvg = src.SharedColorsSvg,
|
||||
SharedColorPresetsSvg = src.SharedColorPresetsSvg,
|
||||
IsPublished = false, Sort = src.Sort,
|
||||
@@ -359,6 +362,8 @@ public class TemplateService(ContentDbContext db)
|
||||
project.MinDurationSec = req.MinDurationSec; project.MaxDurationSec = req.MaxDurationSec;
|
||||
project.FreeFps = req.FreeFps; project.ChooseMode = chooseMode; project.Resolution = resolution;
|
||||
project.VipFactor = req.VipFactor; project.RenderAepComp = req.RenderAepComp;
|
||||
if (!string.IsNullOrWhiteSpace(req.RenderEngine)) project.RenderEngine = req.RenderEngine;
|
||||
if (req.RenderRemotionComp != null) project.RenderRemotionComp = req.RenderRemotionComp;
|
||||
project.SharedLayerImage = req.SharedLayerImage; project.SharedColorsSvg = req.SharedColorsSvg;
|
||||
project.SharedColorPresetsSvg = req.SharedColorPresetsSvg;
|
||||
project.IsPublished = req.IsPublished; project.Sort = req.Sort;
|
||||
@@ -402,6 +407,8 @@ public class TemplateService(ContentDbContext db)
|
||||
if (req.Image != null) project.Image = req.Image;
|
||||
if (req.FullDemo != null) project.FullDemo = req.FullDemo;
|
||||
if (req.SharedColorsSvg != null) project.SharedColorsSvg = req.SharedColorsSvg;
|
||||
if (!string.IsNullOrWhiteSpace(req.RenderEngine)) project.RenderEngine = req.RenderEngine;
|
||||
if (req.RenderRemotionComp != null) project.RenderRemotionComp = req.RenderRemotionComp;
|
||||
project.UpdatedAt = DateTime.UtcNow;
|
||||
|
||||
await db.SaveChangesAsync();
|
||||
@@ -472,7 +479,8 @@ public class TemplateService(ContentDbContext db)
|
||||
p.ProjectDurationSec, p.MinDurationSec, p.MaxDurationSec,
|
||||
p.FreeFps, p.ChooseMode.ToString(), p.Resolution.ToString(),
|
||||
p.IsPublished, p.Sort,
|
||||
p.AepFileUrl, p.AepFileSizeBytes, p.RenderAepComp
|
||||
p.AepFileUrl, p.AepFileSizeBytes, p.RenderAepComp,
|
||||
p.RenderEngine, p.RenderRemotionComp
|
||||
);
|
||||
|
||||
/// <summary>Browse/search all projects (template items) across containers.</summary>
|
||||
@@ -489,7 +497,8 @@ public class TemplateService(ContentDbContext db)
|
||||
.Skip((page - 1) * pageSize).Take(pageSize)
|
||||
.Select(p => new ProjectListItemResponse(
|
||||
p.Id, p.ContainerId, p.Container.Name, p.Container.Slug, p.Name, p.Image,
|
||||
p.Aspect, p.Resolution.ToString(), p.AepFileUrl, p.RenderAepComp, p.IsPublished, p.Sort))
|
||||
p.Aspect, p.Resolution.ToString(), p.AepFileUrl, p.RenderAepComp, p.IsPublished, p.Sort,
|
||||
p.RenderEngine, p.RenderRemotionComp))
|
||||
.ToListAsync();
|
||||
return new PagedResponse<ProjectListItemResponse>(items,
|
||||
new PaginationMeta(page, pageSize, total, (int)Math.Ceiling((double)total / pageSize)));
|
||||
@@ -529,6 +538,8 @@ public class TemplateService(ContentDbContext db)
|
||||
if (req.AepFileMd5 != null) project.AepFileMd5 = req.AepFileMd5;
|
||||
if (req.AepFileSizeBytes.HasValue) project.AepFileSizeBytes = req.AepFileSizeBytes;
|
||||
if (!string.IsNullOrWhiteSpace(req.RenderAepComp)) project.RenderAepComp = req.RenderAepComp;
|
||||
if (!string.IsNullOrWhiteSpace(req.RenderEngine)) project.RenderEngine = req.RenderEngine;
|
||||
if (req.RenderRemotionComp != null) project.RenderRemotionComp = req.RenderRemotionComp;
|
||||
if (req.Folder != null) project.Folder = req.Folder;
|
||||
project.AepUploadedAt = DateTime.UtcNow;
|
||||
project.UpdatedAt = DateTime.UtcNow;
|
||||
@@ -541,6 +552,7 @@ public class TemplateService(ContentDbContext db)
|
||||
p.OriginalWidth, p.OriginalHeight, p.Aspect,
|
||||
p.ProjectDurationSec, p.MinDurationSec, p.MaxDurationSec,
|
||||
p.FreeFps, p.ChooseMode.ToString(), p.Resolution.ToString(), p.VipFactor, p.RenderAepComp,
|
||||
p.RenderEngine, p.RenderRemotionComp,
|
||||
p.SharedLayerImage, p.IsPublished, p.Sort,
|
||||
p.Scenes.Select(MapScene).ToList(),
|
||||
p.SharedColors.Select(sc => new SharedColorResponse(sc.Id, sc.ElementKey, sc.Title, sc.Icon, sc.AttrValue.ToString(), sc.DefaultColor, sc.Sort)).ToList(),
|
||||
|
||||
@@ -95,6 +95,11 @@ public class Project
|
||||
public decimal VipFactor { get; set; } = 1.0m;
|
||||
public string RenderAepComp { get; set; } = "flatrender";
|
||||
|
||||
/// <summary>Render engine for this template: "AfterEffects" (default) or "Remotion".</summary>
|
||||
public string RenderEngine { get; set; } = "AfterEffects";
|
||||
/// <summary>For Remotion templates, the composition id to render (e.g. "KineticQuote").</summary>
|
||||
public string? RenderRemotionComp { get; set; }
|
||||
|
||||
public string? SharedLayerImage { get; set; }
|
||||
public string? SharedColorsSvg { get; set; }
|
||||
public string? SharedColorPresetsSvg { get; set; }
|
||||
|
||||
@@ -307,6 +307,8 @@ public class ContentDbContext(DbContextOptions<ContentDbContext> options) : DbCo
|
||||
e.Property(x => x.Resolution).HasColumnName("resolution");
|
||||
e.Property(x => x.VipFactor).HasColumnName("vip_factor");
|
||||
e.Property(x => x.RenderAepComp).HasColumnName("render_aep_comp");
|
||||
e.Property(x => x.RenderEngine).HasColumnName("render_engine");
|
||||
e.Property(x => x.RenderRemotionComp).HasColumnName("render_remotion_comp");
|
||||
e.Property(x => x.SharedLayerImage).HasColumnName("shared_layer_image");
|
||||
e.Property(x => x.SharedColorsSvg).HasColumnName("shared_colors_svg");
|
||||
e.Property(x => x.SharedColorPresetsSvg).HasColumnName("shared_color_presets_svg");
|
||||
|
||||
@@ -213,7 +213,9 @@ public record CreateProjectRequest(
|
||||
decimal VipFactor,
|
||||
string RenderAepComp,
|
||||
bool IsPublished,
|
||||
int Sort
|
||||
int Sort,
|
||||
string? RenderEngine = null,
|
||||
string? RenderRemotionComp = null
|
||||
);
|
||||
|
||||
public record UpdateProjectRequest(
|
||||
@@ -239,7 +241,9 @@ public record UpdateProjectRequest(
|
||||
string? SharedColorsSvg,
|
||||
string? SharedColorPresetsSvg,
|
||||
bool IsPublished,
|
||||
int Sort
|
||||
int Sort,
|
||||
string? RenderEngine = null,
|
||||
string? RenderRemotionComp = null
|
||||
);
|
||||
|
||||
// Partial update — only non-null fields are applied, so editing an aspect/resolution
|
||||
@@ -260,7 +264,9 @@ public record SetAepRequest(
|
||||
string? AepFileMd5,
|
||||
long? AepFileSizeBytes,
|
||||
string? RenderAepComp,
|
||||
string? Folder
|
||||
string? Folder,
|
||||
string? RenderEngine = null,
|
||||
string? RenderRemotionComp = null
|
||||
);
|
||||
|
||||
public record PatchProjectRequest(
|
||||
@@ -279,7 +285,9 @@ public record PatchProjectRequest(
|
||||
int? Sort,
|
||||
string? Image,
|
||||
string? FullDemo,
|
||||
string? SharedColorsSvg
|
||||
string? SharedColorsSvg,
|
||||
string? RenderEngine = null,
|
||||
string? RenderRemotionComp = null
|
||||
);
|
||||
|
||||
// ── CMS ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -131,7 +131,9 @@ public record ProjectResponse(
|
||||
int Sort,
|
||||
string? AepFileUrl,
|
||||
long? AepFileSizeBytes,
|
||||
string RenderAepComp
|
||||
string RenderAepComp,
|
||||
string RenderEngine,
|
||||
string? RenderRemotionComp
|
||||
);
|
||||
|
||||
public record ProjectListItemResponse(
|
||||
@@ -146,7 +148,9 @@ public record ProjectListItemResponse(
|
||||
string? AepFileUrl,
|
||||
string RenderAepComp,
|
||||
bool IsPublished,
|
||||
int Sort
|
||||
int Sort,
|
||||
string RenderEngine,
|
||||
string? RenderRemotionComp
|
||||
);
|
||||
|
||||
public record ProjectAssetResponse(Guid Id, Guid ProjectId, string Name, string Kind, string Url, long? SizeBytes, int Sort);
|
||||
@@ -171,6 +175,8 @@ public record ProjectDetailResponse(
|
||||
string Resolution,
|
||||
decimal VipFactor,
|
||||
string RenderAepComp,
|
||||
string RenderEngine,
|
||||
string? RenderRemotionComp,
|
||||
string? SharedLayerImage,
|
||||
bool IsPublished,
|
||||
int Sort,
|
||||
|
||||
Reference in New Issue
Block a user