Media Ingesters and Renderers
Media ingesters and renderers are classes that expand the kinds of media users can add, update, and display. Together, they define the entire round trip of a media: how it's added to an item, updated, and rendered to the screen. It's important to know that every media has a ingester and renderer and that they are able to work both with uploaded files and with remote content.
Built-in Ingesters and Renderers
Omeka S comes with several commonly-used media ingesters and renderers:
Ingesters
- html: Add and edit HTML media
- iiif: Add a media by IIIF image URL
- oembed: Add a media by oEmbed URL
- upload: Add a media by file upload
- url: Add a media by URL
- youtube: Add a media by YouTube URL
Renderers
- file: Display a file
- html: Display HTML
- iiif: Display an IIIF image
- oembed: Display an oEmbed image or HTML
- youtube: Display a YouTube iframe
Note that the "upload" and "url" ingesters don't have a similarly-named renderer.
This is because they both use the generic "file" renderer, which renders files according
to their media type or extension (configurable in the file_renderers
config).
So, when writing a custom ingester, you may find that using an already-registered
renderer will suffice.
Interfaces
Ingester
Every ingester class must implement IngesterInterface
. The required methods are:
Method | Purpose | Comment |
---|---|---|
getLabel() |
Returns a human-readable label for the ingester. | It should be unique across all media ingesters. |
getRenderer() |
Returns the name of the renderer for media ingested by the ingester. | It should be the name of a registered media renderer. |
form() |
Returns the markup used to render the controls needed to add the media. | |
ingest() |
Processes an ingest (create) request. | Typically used to validate form data and fetch files or thumbnails |
If the media can be updated then it must implement MutableIngesterInterface
. The
required methods are:
Method | Purpose | Comment |
---|---|---|
updateForm() |
Returns the markup used to render the controls needed to edit the media. | |
update() |
Processes an update request. |
You should look at the built-in ingesters for a better idea of how to use the passed arguments.
Renderer
Every renderer class must implement RendererInterface
. The required method is:
Method | Purpose | Comment |
---|---|---|
render() |
Returns the markup used to render the media. |
You should look at the built-in renderers for a better idea of how to use the passed arguments.
File Downloaders and Uploaders
You'll find that, for some media, you'll need to upload or download files during ingest, typically to fetch files and do something with them, like store them or generate thumbnail images. For these purposes, Omeka S provides two services:
Omeka\File\Downloader
: Download a file from a remote URIOmeka\File\Uploader
: Upload a file from a local client
To inject these services into your ingester you'd follow Zend's service factory pattern.
For example, after injecting the Omeka\File\Downloader
service into your ingester,
you could fetch a thumbnail image for your media in IngesterInterface::ingest()
by passing it a URL:
$url = sprintf('http://example.com/%s.jpg', $imagePath);
$tempFile = $this->downloader->download($url);
if ($tempFile) {
$tempFile->mediaIngestFile($media, $request, $errorStore, false);
}
Both the downloader and uploader will return a temporary file object that has a
handy mediaIngestFile()
method designed specifically for ingesting a media file.
It will automatically validate the file and set the storage ID to the media, among
other things. The method signature, in order:
Argument | Type | Default | Definition |
---|---|---|---|
$media |
Media |
n/a | The media object |
$request |
Request |
n/a | The request object |
$errorStore |
ErrorStore |
n/a | The error store object |
$storeOriginal |
bool |
true | Do you want to store the original file? |
$storeThumbnails |
bool |
true | Do you want to store thumbnail images? |
$deleteTempFile |
bool |
true | Do you want to delete the temporary file after ingest? |
$hydrateFileMetadataOnStoreOriginalFalse |
bool |
false | Do you want to hydrate the file metadata when $storeOriginal = false ? |
Note that in the above example we're passing false
to $storeOriginal
and relying
on the default $storeThumbnails = true
to only create thumbnail images, discarding
the downloaded temporary file.
Adding a Custom Ingester and Renderer
If the built-in ingesters and renderers aren't adequate, Omeka S makes it relatively easy to add custom ones. To demonstrate, we're going to build a module that adds a simple "Tweet" media.
First let's create the module's INI file at /modules/MyModule/config/module.ini
:
[info]
version = "1.0.0-alpha"
omeka_version_constraint = "^1.0.0 || ^2.0.0"
name = "My Module"
description = "Adds a simple Tweet media"
Then let's create the module file at /moudules/MyModule/Module.php
and register
the media in the config:
<?php
namespace MyModule;
use Omeka\Module\AbstractModule;
class Module extends AbstractModule
{
public function getConfig()
{
return [
'media_ingesters' => [
'factories' => [
'mymodule_tweet' => Service\Media\Ingester\TweetFactory::class,
],
],
'media_renderers' => [
'invokables' => [
'mymodule_tweet' => Media\Renderer\Tweet::class,
],
],
];
}
}
Note that you must register the ingester and renderer in the module config for Omeka S to detect them.
Next, since our ingester needs the Omeka\HttpClient
service, let's create the
"Tweet" ingester factory at /modules/MyModule/src/Service/Ingester/TweetFactory.php
:
<?php
namespace MyModule\Service\Media\Ingester;
use MyModule\Media\Ingester\Tweet;
use Zend\ServiceManager\Factory\FactoryInterface;
use Interop\Container\ContainerInterface;
class TweetFactory implements FactoryInterface
{
public function __invoke(ContainerInterface $services, $requestedName, array $options = null)
{
return new Tweet($services->get('Omeka\HttpClient'));
}
}
Then, let's create the "Tweet" ingester class at /modules/MyModule/src/Media/Ingester/Tweet.php
:
<?php
namespace MyModule\Media\Ingester;
use Omeka\Api\Request;
use Omeka\Entity\Media;
use Omeka\Media\Ingester\IngesterInterface;
use Omeka\Stdlib\ErrorStore;
use Zend\Form\Element\Text;
use Zend\Http\Client;
use Zend\View\Renderer\PhpRenderer;
class Tweet implements IngesterInterface
{
protected $client;
public function __construct(Client $client)
{
$this->client = $client;
}
public function getLabel()
{
return 'Tweet'; // @translate
}
public function getRenderer()
{
return 'mymodule_tweet';
}
public function form(PhpRenderer $view, array $options = [])
{
$input = new Text('o:media[__index__][o:source]');
$input->setOptions([
'label' => 'Tweet URL', // @translate
'info' => 'URL for the tweet to embed.', // @translate
]);
$input->setAttributes([
'required' => true,
]);
return $view->formRow($input);
}
public function ingest(Media $media, Request $request, ErrorStore $errorStore)
{
// Validate the request data.
$data = $request->getContent();
if (!isset($data['o:source'])) {
$errorStore->addError('o:source', 'No tweet URL specified');
return;
}
// Validate the URL.
$isMatch = preg_match('/^https:\/\/twitter\.com\/[\w]+\/status\/[\d]+$/', $data['o:source']);
if (!$isMatch) {
$errorStore->addError('o:source', sprintf(
'Invalid tweet URL: %s',
$data['o:source']
));
return;
}
// Get the oEmbed JSON.
$url = sprintf('https://publish.twitter.com/oembed?url=%s', urlencode($data['o:source']));
$response = $this->client->setUri($url)->send();
if (!$response->isOk()) {
$errorStore->addError('o:source', sprintf(
'Error reading tweet: %s (%s)',
$response->getReasonPhrase(),
$response->getStatusCode()
));
return false;
}
// Set the Media source and data.
$media->setSource($data['o:source']);
$media->setData(json_decode($response->getBody(), true));
}
}
Finally, let's create the "Tweet" renderer class at /modules/MyModule/src/Media/Renderer/Tweet.php
:
<?php
namespace MyModule\Media\Renderer;
use Omeka\Api\Representation\MediaRepresentation;
use Omeka\Media\Renderer\RendererInterface;
use Zend\View\Renderer\PhpRenderer;
class Tweet implements RendererInterface
{
public function render(PhpRenderer $view, MediaRepresentation $media, array $options = [])
{
// Return the oEmbed JSON's "html" value.
return $media->mediaData()['html'];
}
}
After installing the module, you'll be able to add an embedded tweet to an item by adding a "Tweet" media to an item and entering a URL to a single tweet. (Note that this fuctionality is generally covered by the built-in "oembed" ingester, so you don't actually need a new ingester for this. It's a helpful example nonetheless.)