Media Pipeline

Included is a built-in media pipeline:

  • uploads to storage (local or S3-compatible)

  • derivative generation (thumb/small/medium/large) via sharp

  • animation support for Lottie (JSON) and SVG

  • optional optimization (WebP for images, compressed MP4 for videos)

  • category tagging + metadata for discovery

Key files

  • Controller: app/controllers/media_controller.ts

  • Service: app/services/media_service.ts

  • Storage: app/services/storage_service.ts

  • Variant generation action: app/actions/generate_media_variants_action.ts

  • Media field type (module props/custom fields): app/fields/media.ts

  • Frontend Renderer: inertia/components/MediaRenderer.tsx

Supported Formats & Processing

Raster Images

JPG, PNG, WebP, GIF, AVIF are processed via sharp to generate size variants and optimized WebP versions.

Vector & Animations

  • SVG: Uploaded and served as-is. Supports dark-mode variant overrides.

  • Lottie: Supports .json and .lottie files. Rendered on the frontend using @lottiefiles/lottie-player.

Video

MP4 and WebM support. Playback modes (autoplay, inline, modal) are stored in the media asset metadata. Videos can be optimized to a compressed MP4 format via ffmpeg.

  • MEDIA_VIDEO_OPTIMIZATION_CRF (default ~28)

  • MEDIA_VIDEO_OPTIMIZATION_PRESET (default ~medium)

Derivatives configuration

Derivatives are configured with env:

  • MEDIA_DERIVATIVES

Default (if unset) in media_service.ts:

typescript
thumb:200x200_crop,small:400x,medium:800x,large:1600x

Notes:

  • _crop uses fit: cover (square crops, etc.)

  • non-crop uses fit: inside

Dark mode variants

Dark mode for media is handled in two ways:

  1. Manual Overrides: Editors can upload a specific dark version of an image or SVG.

  2. Automatic Generation: If no manual override is provided, the system can automatically generate a dark version of the original image by applying a tunable brightness and saturation transform.

Automatic Dark Base Generation

The CreateDarkBaseAction (app/actions/create_dark_base_action.ts) is responsible for generating these dark versions. It uses sharp to modulate the original image.

Configuration

You can tune the appearance of automatically generated dark variants via environment variables:

  • MEDIA_DARK_BRIGHTNESS (default ~0.55): Controls the brightness of the generated dark image (range: 0.1 to 2.0).

  • MEDIA_DARK_SATURATION (default ~0.75): Controls the saturation of the generated dark image (range: 0 to 2.0).

These settings allow you to ensure that automatically darkened images still look good and maintain brand consistency without manual work for every asset.

Optimized Media

media_service can generate optimized derivatives for better web performance.

Images (WebP)

  • MEDIA_WEBP_QUALITY (default ~82)

Video (MP4)

  • MEDIA_VIDEO_OPTIMIZATION_CRF (default ~28, range 0-51)

  • MEDIA_VIDEO_OPTIMIZATION_PRESET (default ~medium)

  • FFMPEG_PATH (optional, custom path to ffmpeg binary)

  • FFPROBE_PATH (optional, custom path to ffprobe binary)

How media is referenced by modules

Modules commonly use a prop schema like:

ts
image: { type: 'media', storeAs: 'id' }

This means editors store a media asset id in props; renderers resolve it through the media API/service.

Storage Configuration

The media pipeline supports local and S3-compatible (specifically Cloudflare R2) storage. This is controlled by the STORAGE_DRIVER environment variable.

For detailed configuration instructions, see the Deployment Guide: Persistent Storage.

Migrating Storage Backends

If you are switching from local storage to Cloudflare R2, you can use the built-in migration command to sync your existing assets and update database references:

bash
node ace migrate:media:r2

See the Deployment Guide for full details on the migration process.

AI-Generated Media

The media pipeline is integrated with AI agents for automated image generation:

  • Tools: Agents use the generate_image MCP tool to create new assets.

  • Providers: Supports OpenAI (DALL-E) and Google (Imagen).

  • Staging: Generated images are automatically downloaded, optimized, and saved to the CMS media library before being staged in the post's review draft.

  • Config: Providers and models are configured at the agent level (see providerMedia and modelMedia in the AI Agents documentation).

External Media Imports

The media pipeline supports importing assets directly from external URLs. This is particularly useful during content migrations:

  • Detection: The DatabaseImportService and MigrationAgent automatically detect URLs starting with http in media fields.

  • Processing: When a URL is detected, the system downloads the file, performs standard optimization (WebP/MP4), and generates all configured variants.

  • Storage: The downloaded and processed asset is then uploaded to the configured storage driver (Local or R2) and registered in the media_assets table.

Operational notes

  • If you add new derivative specs, consider a backfill job to regenerate variants for existing assets.

  • If you change storage backends, ensure existing URLs remain valid or provide redirect/migration logic.