Media Hooks

Accent CMS provides two hooks for plugins to interact with the media pipeline. These hooks allow plugins to augment media metadata during content scanning and transform images after processing.

on_media_discover

The on_media_discover hook fires once for each non-markdown file found during content scanning. Plugins can return metadata key-value pairs that are attached to the media asset and made available in templates.

Use cases:

  • EXIF extraction – Read camera metadata from JPEG/TIFF files
  • Blur hash generation – Compute compact placeholders for lazy-loading
  • Color palette extraction – Identify dominant colors for design integration
  • Alt text generation – Use AI services to generate image descriptions
  • File validation – Log warnings for oversized or incorrectly formatted files

The hook receives the file path and MIME type as a typed record. The returned metadata map is merged into the MediaAsset and becomes available in templates:

{% for img in page.media | filter_images %}
  <img src="{{ img.url }}"
       {% if img.metadata.blurhash %}data-blurhash="{{ img.metadata.blurhash }}"{% endif %}
       {% if img.metadata.exif %}title="{{ img.metadata.exif.camera }}"{% endif %}>
{% endfor %}

Multiple plugins can contribute metadata. If two plugins return the same key, the later plugin’s value wins.

A media plugin implements the media-hooks interface (the media-plugin world). on_media_discover receives the file path and MIME type and returns a metadata map; build it from whatever the plugin can determine about the file and return it. Set requires_fs = true in plugin.toml (and import wasi:filesystem in the world) if the plugin needs to read file headers directly. See Writing a Plugin for the toolchain and The Plugin Contract for the interface signatures.

on_media_process

The on_media_process hook fires after core image processing (resizing, format conversion) and before the result is cached. Plugins receive the processed image as raw bytes and can modify them, change the content type, or attach metadata.

Use cases:

  • Watermarking – Overlay a logo or copyright text on processed images
  • Format conversion – Convert to advanced formats (AVIF, JXL) not built into the core
  • Quality optimization – Apply perceptual quality tuning beyond what the core processor offers
  • Metadata injection – Add processing metadata (dimensions, file size) for analytics

Image bytes cross the boundary as a raw list<u8> – no base64 encoding. A plugin that only wants to attach metadata returns the bytes unchanged (and may leave the content type unchanged), so the original processed image is preserved while the metadata is merged.

Multiple plugins are chained sequentially. Each plugin receives the output of the previous one.

Media Hook Lifecycle

The two media hooks fire at different points in the content and media pipelines:

Content Scanning                    Media Request
================                    =============

  Scan directory                      Receive request
       |                                    |
  Find media file                     Resolve file path
       |                                    |
  Create MediaAsset                   Check cache (hit? -> serve)
       |                                    |
  [on_media_discover]                 Core processing (resize, convert)
       |                                    |
  Merge metadata into asset           [on_media_process]
       |                                    |
  Build content index                 Cache result
       |                                    |
  Available in templates              Serve response

Media hooks are independent: a plugin can register for one or both. The discover hook enriches template data while the process hook transforms the served bytes.

The typed input and output records for both hooks are part of the plugin contract.