Embedding Media Masher in Your Site

The Media Masher editor consists of dozens of web components that can be embedded directly in HTML without requiring any intermediate build step. You can simply use modern import syntax to pull in the main component code, and then include the main MEDIA-MASHER element in your page.

The browser will automatically bind the code to the element, which will load sub components that contain the UI. Several attributes control the loading process, but their default values will display the editor with most of its features fully enabled.

Nonetheless, the JavaScript files are organized into two NPM style packages, if some sort of build step is required. The @mashermedia/client package contains client specific code and @mashermedia/lib has code shared with the server. These packages are NOT available in NPM’s public registry though, so you must point to the packed TGZ files for each library:

  • npm i https://js.masher.media/0.0.1-beta.36/client/package.tgz
  • npm i https://js.masher.media/0.0.1-beta.36/lib/package.tgz

You are also welcome to download the packages directly, and incorporate them into your project any way you see fit.

Component

The HTML document below can be hosted on any web host to display the Media Masher application in any modern web browser:

Example: Functional HTML
<html>
  <head>
    <title>Media Masher Functional Example</title>
    <script type='importmap'>{ "imports": {
      "lit": "https://cdn.jsdelivr.net/gh/lit/dist@3.2.0/all/lit-all.min.js",
      "@mashermedia/": "https://js.masher.media/0.0.1-beta.36/"
    } }</script>
    <script type='module'>
      import '@mashermedia/client/media-masher.js'
    </script>
    <style>
      body {
        height: 100vh; display: flex;
        margin: 0; padding: 0;
      }
    </style>
  </head>
  <body>
    <media-masher></media-masher>
  </body>
</html>

This example provides minimal styling to just fill the viewport with the editor - see the Styling Guide for all the CSS options.

The first SCRIPT element is an import map which tells the browser where to find the Media Masher and Lit modules. The second SCRIPT element imports the MEDIA-MASHER custom element, which is then used in the BODY.

Without further configuration, the component will populate itself with many subcomponents and load their corresponding JavaScript files. Some components will not load though. In particular the buttons that would trigger API calls will not display and no items will appear in the media browser.

Other approaches to displaying these elements are detailed below, but one simple way is to set the apis attribute on the MEDIA-MASHER element:

Example: MEDIA-MASHER with apis
<media-masher apis='./client/json/apis.json'></media-masher>

When a URL is provided for the apis attribute, the JSON formatted file it points to is loaded. ApiOptions objects defined in it are used to configure particular API calls, and thus display the components that might trigger them. The URL can be relative, in which case it is resolved against @mashermedia. The files in the json directory of the @mashermedia/client library generally offer reasonable default data.

The @mashermedia/client/json/apis.json file describes an API that provides some shared shapes and fonts. These will appear in the media browser. It does not provide other functionality though, so buttons like save and encode will still not appear. See the Deploying Guide for more information on configuring more advanced APIs.

Attributes

In a similar way, the icons and phrases displayed in buttons and links can be loaded from JSON formatted files. Together with apis, the MediaMasherElement web component class supports three attributes that can be used to control the data loading process:

AttributeTypeDefaultData Format
apisstring""ApiOptions[]
iconsstring’./client/json/icons.json’StringRecord
phrasesstring’./client/json/phrases.json’StringRecord

When populated, the MEDIA-MASHER element will trigger a GET request to the URL supplied, expecting a JSON object with the correct format in return.

The default for the apis property is an empty string. This is what keeps buttons and other elements associated with API calls to not be included in the UI.

The following HTML explicitly defines values for all the loading attributes by supplying relative URLs for each:

Example: MEDIA-MASHER with laoding attributes
<media-masher 
  apis='./client/json/apis.json' 
  icons='./client/json/icons.json' 
  phrases='./client/json/phrases.json'
></media-masher>

A drawback to this approach is that requests will always use the GET method, so no additional headers or body can be supplied. A more complex request can be constructed by directly setting options on the element (see below). It’s even possible to supply the data directly, bypassing the need for a request altogether.

Options

Under the hood, the attribute values above are used to prepopulate the MEDIA-MASHER element’s options property, which follows the MediaMasherOptions interface. This is a fairly complex data structure containing options for each of the Plugins supported by the system.

For instance, the apis attribute is passed to the SyncPlugin along with other options described by the SyncPluginOptions interface. The icons and phrases are passed to the ElementPlugin along with other options in the ElementPluginOptions interface.

When manually setting the options detailed below, it’s important to keep the following rules in mind:

  1. Populated default attributes must be unset on the element with an empty string.
  2. Plugin options must be set immediately after importing the client library.

Given the example HTML page above, the JavaScript in the sections below can just replace the content of the second SCRIPT element to accommodate the second rule. The following HTML snippet demonstrates unsetting the relevant attributes:

Example: MEDIA-MASHER with attribute overrides
<media-masher icons='' phrases=''></media-masher>

Phrases

The ElementPlugin is responsible for loading the text that appears in buttons, links, and other UI elements. As the UI is loading, calls are made to the MEDIA-MASHER element’s phrase method which first loads the plugin and then calls its phrase method. The argument to these methods is an ElementPhraseArgs object, which includes an id property containing a unique string ID for each phrase.

This plugin has many methods which respond differently depending on its options, which follow the ElementPluginOptions interface. These options are found under the element property of the MEDIA-MASHER element’s options.

One property of this interface is phrases which will contain the value of the phrases attribute copied from the MEDIA-MASHER element. When this string is not populated, the method will fallback to the phrase property, which is an object following the ElementPhraseOptions interface:

ElementPhraseOptionsTypeDefault
recordStringRecord{}
requeststring | EndpointRequest | StringRecord""

For each phrase ID used in the editor, the plugin first checks for a value in the record. If not found, it falls back to the request, which triggers the following behavior based on its type:

  • When a string is supplied, it will be used to make a GET request and the response will be copied to the StringRecord for subsequent lookups.
  • When an EndpointRequest object is supplied, it will be used to make a request and the response will be copied to the StringRecord for subsequent lookups.
  • When a StringRecord object is supplied, it will be used directly for lookups.

If a request is made, it is made just once. Then the StringRecord object is checked for the ID and if not found, the ID itself is returned.

Example: supplying raw data for phrase.record
import '@mashermedia/client/media-masher.js'
const [element] = document.getElementsByTagName('media-masher')
element.options.phrase.record = { 'play': 'Start' }
Example: supplying URL for phrase.request
import '@mashermedia/client/media-masher.js'
const [element] = document.getElementsByTagName('media-masher')
element.options.phrase.request = './client/json/phrases.json'
Example: supplying EndpointRequest for phrase.request
import '@mashermedia/client/media-masher.js'
const [element] = document.getElementsByTagName('media-masher')
element.options.phrase.request = { 
  endpoint: './client/json/phrases.json', 
  init: { method: 'GET' }
}

Icons

The ElementPlugin is also responsible for loading the icons that appear in buttons, links, and other UI elements. As the UI is loading, calls are made to the MEDIA-MASHER element’s icon method which first loads the plugin and then calls its icon method. The argument to these methods is an ElementIconArgs object, which includes an id property containing a unique string ID for each icon.

This plugin has many methods which respond differently depending on its options, which follow the ElementPluginOptions interface. These options are found under the element property of the MEDIA-MASHER element’s options.

One property of this interface is icons which will contain the value of the icons attribute copied from the MEDIA-MASHER element. When this string is not populated, the method will fallback to the icon property, which is an object following the ElementIconOptions interface:

ElementIconOptionsTypeDefault
prefixstring""
recordStringRecord{}
requeststring | EndpointRequest | StringRecord""

For each icon ID used in the editor, the plugin first checks for a value in the record. If not found, it falls back to the request, which triggers the following behavior based on its type:

  • When a string is supplied, it will be used to make a GET request and the response will be copied to the StringRecord for subsequent lookups.
  • When an EndpointRequest object is supplied, it will be used to make a request and the response will be copied to the StringRecord for subsequent lookups.
  • When a StringRecord object is supplied, it will be used directly for lookups.

If a request is made, it is made just once. Then the StringRecord object is checked for the ID - the value can either be a URL or SVG string. If not found and the prefix is nonempty, a URL is constructed by appending it with the ID and ‘svg’ extension. Ultimately an SVG element is returned, either by fetching the URL or parsing the string.

Example: supplying raw data for icon.record
import '@mashermedia/client/media-masher.js'
const [element] = document.getElementsByTagName('media-masher')
element.options.element.icon.record = { 'play': 'svg/start.svg' }
Example: supplying URL for icon.request
import '@mashermedia/client/media-masher.js'
const [element] = document.getElementsByTagName('media-masher')
element.options.element.icon.request = './client/json/icons.json'
Example: supplying EndpointRequest for icon.request
import '@mashermedia/client/media-masher.js'
const [element] = document.getElementsByTagName('media-masher')
element.options.element.icon.request = { 
  endpoint: './client/json/icons.json', 
  init: { method: 'GET' }
}

Media

On the client, the SyncPlugin is ultimately responsible for delivering the mash that appears in the player and timeline, as well as populating the media browser. As these components are loaded they call the MEDIA-MASHER element’s mash and find methods. These calls are ultimately passed to the plugin which will attempt to pass them on to a configured API, while caching the results for subsequent calls.

There are several different mechanisms that can be used to configure APIs, each of them involving the ApisPlugin in some way. Its options follow the ApisPluginOptions interface and can be found under the apis property of the MEDIA-MASHER element’s options. Its apiOptions property is an array of ApiOptions objects that it uses to create Api instances as needed.

Directly configuring an API

In cases where the client has all the information needed to configure an API, the most straightforward approach is to simply add it to the apiOptions array:

Example: supplying raw client data for apis.apiOptions
import '@mashermedia/client/media-masher.js'
const [element] = document.getElementsByTagName('media-masher')
element.options.apis.apiOptions = [{
  id: 'api-id',
  userId: 'user-id',
  endpoints: { find: 'find', mash: 'mash', add: 'add' }
}]

Each API must have an id and userId associated with it. The endpoints object is used to determine which of the API methods are supported. The keys correspond to method names and the values are URLs used to build the actual method requests.

Buttons that required a particular method will only display if there is a key for their method. For instance, the save button will only display if the add key is present as it is in this example.

The find method is enabled as well, so results from calling that endpoint are used to populate the media browser. The mash method is also enabled, so results from calling that endpoint are loaded into the player and timeline.

Relative URLs are resolved to the current page. The ApiOptions interface also supports an optional prefix string property that will be prepended to relative URLs before resolution. There is also an optional request object following the EndpointRequest interface, which can be used to further control the API requests being made. Its endpoint property is also prepended to relative URLs before resolution.

If a value is defined as an empty string, that method is consider enabled within the UI, but no API requests are made. It’s expected that some other mechanism will handle the method.

Directly supplying media

In cases where the client already has media data it wants to load, an API can be configured to simply load it without making a request. The data that would have been returned from its mash or find API methods can instead be defined under the same key in its options:

Example: supplying raw media data in ApiOptions
import '@mashermedia/client/media-masher.js'
const [element] = document.getElementsByTagName('media-masher')
element.options.apis.apiOptions = [{
  id: 'api-id',
  userId: 'user-id',
  mash: {
    id: 'mash-id', label: 'Mash',
    type: 'video', source: 'mash',
    color: '#00FF00'
  },
  find: [{
    id: 'media-id', label: 'Butcherman',
    type: 'image', source: 'text',
    resources: [{
      id: 'resource-id',
      type: 'raw', source: 'woff2',
      decoded: { family: 'Butcherman' },
      request: { endpoint: 'https://fonts.gstatic.com/s/butcherman/v24/2EbiL-thF0loflXUBOdb5zK5qtxtX88.woff2'
      }
    }]
  }]
}]

In this example, we’re loading an empty mash having a green background color and a font from the Google Fonts API. Note that the mash and media require an id be supplied.

Fetching API configuration

The options for the SyncPlugin can be configured to initialize the APIs the first time one of these methods is called. These are found under the sync property of the MEDIA_MASHER element’s options and follow the SyncPluginOptions interface:

SyncPluginOptionsTypeDefault
apisstringundefined
mediaIdstringundefined
requestEndpointRequestundefined

When apis is empty (which it is by default) the plugin falls back to request, otherwise it builds a GET request from the URL provided. If neither is defined, the find method simply returns an empty array and the mash method returns a MashMedia instance with a single empty Track.

Example: supplying URL for sync.apis
import '@mashermedia/client/media-masher.js'
const [element] = document.getElementsByTagName('media-masher')
element.options.sync.apis = './client/json/apis.json'
Example: supplying EndpointRequest for sync.request
import '@mashermedia/client/media-masher.js'
const [element] = document.getElementsByTagName('media-masher')
element.options.sync.request = { 
  endpoint: './client/json/apis.json', 
  init: { method: 'GET' }
}

The request being made here is to the ApisPlugin rather than to an API itself. When the mash or find methods are first called, this request is passed to its fetch method to initialize the APIs themselves. If multiple APIs are configured to support the find method, a SELECT element will appear in the media browser so the user can choose between them.

Once the APIs are initialized, the corresponding method is called on the appropriate Api instance. If the mediaId is defined, it’s added to the arguments supplied to the mash method. See the Deploying Guide for more information on configuring APIs.