5.8 KiB
Description Engine
User Perspective
The description engine generates video descriptions from reusable building blocks. Instead of writing descriptions manually for each video, users define blocks (intro, CTA, social links, sponsor copy) and assemble them into templates. Each video can then use a template as its starting point, overriding individual blocks or variables as needed.
Blocks (/blocks)
A block is a named, reusable piece of description content. Types:
- Static: Plain text, rendered as-is. No token substitution. Suitable for boilerplate that never changes.
- Variable: Text with
{token}placeholders. Tokens are resolved from team variables, video fields, or collaborator data. - Campaign: Like Variable but linked to a Campaign. Auto-included in descriptions during the campaign's date window — no manual placement needed.
- Collaborator: Expanded once per assigned collaborator. The block's content is used as a template, cloned for each collaborator in
VideoConfig.collaboratorIds, with{collab.*}tokens resolved per-collaborator. Clones are joined with a blank line (or single newline if compact). - Conditional: Shown only when its condition evaluates to true.
Blocks have a compact toggle: when compact is off, a blank line is inserted before the block's content during rendering.
Templates (/templates)
A template defines the default block assembly for a type of video. It contains:
- Default block order: which blocks appear and in what sequence
- Default variable values: pre-filled values for variable tokens
- Video fields: default metadata values (privacy, category, tags, etc.) applied when assigned
- Rules: required links for lint checking
Applying a template to a video copies the block order, variable values, and optionally the video field defaults.
Variable Tokens
In Variable-type blocks, {token} placeholders are replaced at render time:
{video.title},{video.tags},{video.category},{video.gameTitle},{video.language}— from the video record{video.scheduledAt},{video.recordingDate}— dates; support inline format override (see below){video.playlists}— comma-separated list of playlist titles the video belongs to{video.playlistLinks}— comma-separated list of full YouTube playlist URLs the video belongs to{collab.name},{collab.youtube},{collab.twitch},{collab.instagram},{collab.tiktok},{collab.twitter},{collab.bluesky},{collab.discord},{collab.aliases},{collab.notes}— from the first assigned collaborator only (see below). Note:{collab.youtube}resolves to the full URL (https://www.youtube.com/@handle), not just the handle.{my_variable}— from team-level variables or video-level variable value overrides
Collaborator Token Behavior with Multiple Collaborators
In VARIABLE, CONDITIONAL, CAMPAIGN, and freetext blocks: {collab.*} tokens always resolve to the first collaborator in the video's collaboratorIds list. If a video has multiple collaborators, tokens in non-COLLABORATOR blocks only reflect collaborator #1.
In COLLABORATOR blocks: the block is cloned once per collaborator, each clone resolved against that collaborator's data, then joined with a blank line. This is the correct way to list multiple collaborators — one COLLABORATOR block produces one entry per person.
Practical rule: use a COLLABORATOR block whenever the content should repeat for each collaborator. Use {collab.*} tokens in other block types only when you have exactly one collaborator, or you intentionally want only the first.
Date Token Format Overrides
Date tokens ({video.scheduledAt} and {video.recordingDate}) support an inline format string using | as a separator:
{video.scheduledAt|DD.MM.YYYY} → e.g. 05.03.2024
{video.recordingDate|MMMM D, YYYY} → e.g. March 5, 2024
Available format tokens: YYYY (4-digit year), YY (2-digit), MMMM (full month), MMM (short month), MM (zero-padded month), M (month), DD (zero-padded day), D (day).
Without an inline override, the team's date format setting is used. If no team format is set, the default is YYYY-MM-DD. If the date field is not set on the video, the token resolves to an empty string.
No other tokens support the |format syntax — it is silently ignored on non-date tokens.
Freetext Entries
In the video editor, users can add freetext sections directly in the block order without creating a named block. These appear as free-form text areas in the editor and are stored with IDs prefixed freetext:.
Developer Perspective
Render Path
VideoRenderService.render(videoId)fetches all data- Builds ordered list of blocks from
VideoConfig.blockOrder - Appends active CAMPAIGN blocks at the end (regardless of order)
- For each block: applies
blockOverrides, resolves tokens - Joins blocks with blank lines (unless
compact: true) - Returns
{ rendered: string, hash: string }
Key Files
backend/src/shared/render-engine/video-render.service.ts— data fetching + orchestrationbackend/src/shared/render-engine/render-engine.service.ts— pure rendering logicbackend/src/shared/system-variables/system-variables.registry.ts— system token registry
Adding a New System Token
- Add the token to
SYSTEM_VARIABLES[]insystem-variables.registry.ts - Add it to
SYSTEM_VARIABLE_TOKENSSet - Implement resolution in the appropriate resolver in
RenderEngineService - Without step 2,
resolveVariables()will attempt to resolve it as a team/video variable and fail silently
Block Versioning
DescriptionBlock has a BlockVersion history table. Every content change creates a new version snapshot. Version number increments on each save.
Related
- 07 - Render Engine (architecture)
- 03 - Blocks API
- 04 - Templates API
- 06 - Collaborators