MediaProcessor 

The media data processor resolves a file resource and hands it to whichever registered media processor's supports() method matches it first. Implement the interface yourself to support whatever resource kinds you need (images, documents, videos, download links, ...).

The interface 

interface MediaProcessor
Fully qualified name
\CPSIT\Typo3Handlebars\DataProcessing\Media\MediaProcessor
process ( contentObjectRenderer, resource, configuration = [])

Process the given resource and return the resulting array, which is stored under media 's as key.

param ContentObjectRenderer contentObjectRenderer

The current content object renderer.

param resource

The resolved resource — a core ResourceInterface or an Extbase File / FileReference .

param array configuration

This processor's slice of config.<name> .

returntype

array

supports ( resource)

Return true if this processor can handle the given resource. Called for every registered media processor, in priority order, until one returns true .

param mixed resource

The resolved resource, of unknown type.

returntype

bool

Implement a media processor 

Implementations are auto-registered because the interface itself carries #[AutoconfigureTag('handlebars.media_processor')] . The #[AsTaggedItem('<name>')] attribute on the implementing class both determines matching order among several processors (higher priority is tried first, default 0, same as PathProvider and VariableProvider) and gives the processor its <name> , i.e. the key under which media looks up its config.<name> block.

EXT:my_extension/Classes/DataProcessing/Media/DownloadProcessor.php
namespace Vendor\Extension\DataProcessing\Media;

use CPSIT\Typo3Handlebars\DataProcessing\Media\MediaProcessor;
use Symfony\Component\DependencyInjection\Attribute\AsTaggedItem;
use TYPO3\CMS\Core\Resource\AbstractFile;
use TYPO3\CMS\Core\Resource\ResourceInterface;
use TYPO3\CMS\Extbase\Domain\Model\File;
use TYPO3\CMS\Extbase\Domain\Model\FileReference;
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;

#[AsTaggedItem('download')]
final readonly class DownloadProcessor implements MediaProcessor
{
    public function process(
        ContentObjectRenderer $contentObjectRenderer,
        ResourceInterface|File|FileReference $resource,
        array $configuration = [],
    ): array {
        return [
            'url' => $resource->getPublicUrl(),
            'label' => $configuration['label'] ?? $resource->getName(),
        ];
    }

    public function supports(mixed $resource): bool
    {
        return $resource instanceof AbstractFile && !$resource->isImage();
    }
}
Copied!

With this registered, config.download.label becomes available wherever media is used.

Typed configuration with ConfigurableProcessor 

Mapping configuration by hand, as above, is fine for a couple of options. For more involved configuration, extend the abstract \CPSIT\Typo3Handlebars\DataProcessing\Media\ConfigurableProcessor instead, which uses cuyz/valinor to map the raw configuration array onto a typed, immutable configuration object before processFile() is called:

/**
 * @extends ConfigurableProcessor<MyConfiguration>
 */
final class MyProcessor extends ConfigurableProcessor
{
    public function processFile(
        ContentObjectRenderer $contentObjectRenderer,
        ResourceInterface|File|FileReference $resource,
        Configuration $configuration,
    ): array {
        // $configuration is an instance of MyConfiguration
    }

    public function supports(mixed $resource): bool
    {
        // ...
    }

    protected function getConfigurationClass(): string
    {
        return MyConfiguration::class;
    }
}
Copied!

MyConfiguration only needs to implement the empty marker interface \CPSIT\Typo3Handlebars\DataProcessing\Media\Configuration\Configuration and declare its accepted options as constructor-promoted properties.