Files

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

  1. VideoRenderService.render(videoId) fetches all data
  2. Builds ordered list of blocks from VideoConfig.blockOrder
  3. Appends active CAMPAIGN blocks at the end (regardless of order)
  4. For each block: applies blockOverrides, resolves tokens
  5. Joins blocks with blank lines (unless compact: true)
  6. Returns { rendered: string, hash: string }

Key Files

  • backend/src/shared/render-engine/video-render.service.ts — data fetching + orchestration
  • backend/src/shared/render-engine/render-engine.service.ts — pure rendering logic
  • backend/src/shared/system-variables/system-variables.registry.ts — system token registry

Adding a New System Token

  1. Add the token to SYSTEM_VARIABLES[] in system-variables.registry.ts
  2. Add it to SYSTEM_VARIABLE_TOKENS Set
  3. Implement resolution in the appropriate resolver in RenderEngineService
  4. 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.

  • 07 - Render Engine (architecture)
  • 03 - Blocks API
  • 04 - Templates API
  • 06 - Collaborators