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.