The form settings in Accurate Video makes it possible to create custom metadata forms for timeline markers, assets and their video-, audio- and subtitle files. This can be used for a wide array of use cases, such as: Reduce the amount of metadata fields to only what is needed, use different sets of metadata fields for different rows in the timeline, or to create advanced metadata forms that fit complex business requirements.
Creating new forms settings can generally be broken down into three steps: 1. Define the metadata format using a json schema 2. Define the layout of the form with a UI schema 3. Declare which form should be used for a marker track, assets, videos, audio or subtitles.
The full documentation for the json forms settings can be found here: accurate.video/docs/install-setup/frontend-configurations/#forms
Below are a couple of examples following the outline described above.
First up, a basic setup with a single metadata field which apply for all markers.
Defining the metadata format for this example is fairly straightforward. The root element always has to be an object, all metadata fields are defined as properties on this object. For this example there will be a single field which will be called "exampleField" and it will be a
string
markerDefault
You can read more about the available types here: json-schema.org/understanding-json-schema/reference/type.html Add the following to the root object in settings file:
{ "forms": { "markerDefault": { "schema": { "type": "object", "properties": { "exampleField": { "type": "string" } } } } } }
The Next step is to define how the form should look like, this is done by defining a
uischema
schema
VerticalLayout
exampleField
elements
label
scope
Read more about available layout elements here: jsonforms.io/docs/uischema/layouts Combining the
with the previous step results in the following:uischema
{ "forms": { "defaultMarker": { "schema": { "type": "object", "properties": { "exampleField": { "type": "string" } } }, "uischema": { "type": "VerticalLayout", "elements": [ { "type": "Control", "label": "Example data", "scope": "#/properties/exampleField" } ] } } } }
When there is only a single form that is to be used for all markers there is no need to deal with row assignment. There will be more information on how to do this in the next example.
This should result in a settings file that looks something like this:
{ "forms": { "defaultMarker": { "schema": { "type": "object", "properties": { "exampleField": { "type": "string" } } }, "uischema": { "type": "VerticalLayout", "elements": [ { "type": "Control", "label": "Example data", "scope": "#/properties/exampleField" } ] } } } }
The resulting form should look something like:
This example explains how to set up the player with two different forms, one simple form with a single field and a more complex form with some validation rules that's assigned to markers on a specific row.
Multiple forms can be defined by adding several objects to the forms array, it is important to assign unique
id
maxlength
issue
enum
type
required
issueForm
More information about string validation rules can be found here: json-schema.org/understanding-json-schema/reference/string.html Add the following to the settings file:
{ "forms": { "defaultMarker": { "schema": { "type": "object", "properties": { "name": { "type": "string" } } } }, "issueForm": { "schema": { "type": "object", "properties": { "issue": { "type": "string", "maxlength": 16 }, "type": { "type": "string", "enum": ["audio", "video", "subtitles"] } }, "required": ["type"] } } } }
A
uischema
HorizontalLayout
label
scope
enum
format
uischema
{ "forms": { "defaultMarker": { "schema": { "type": "object", "properties": { "name": { "type": "string" } } }, "uischema": { "type": "VerticalLayout", "elements": [ { "type": "Control", "label": "Name", "scope": "#/properties/name" } ] } }, "issueForm": { "schema": { "type": "object", "properties": { "issue": { "type": "string", "maxlength": 16 }, "type": { "type": "string", "enum": ["audio", "video", "subtitles"] } }, "required": ["type"] }, "uischema": { "type": "HorizontalLayout", "elements": [ { "type": "Control", "label": "Issue", "scope": "#/properties/issue" }, { "type": "Control", "label": "Issue type", "scope": "#/properties/type", "options": { "format": "radio" } } ] } } } }
The form with the key
defaultMarker
form
tooltip
{ "markers": { "groups": [ { "match": (marker, track) => marker?.type === "Manual" || track?.type === "Manual", "title": "Manual", "id": "Manual", "alwaysShow": true, "allowCreateTrack": true, "trackType": "Manual", "rows": [ { "match": (marker) => marker?.metadata.get("trackId") === "av:track:video:issue", "track": "av:track:video:issue", "title": "Notes", "markerType": "Manual", "order": 0, "markerStyle": { backgroundColor: "var(--AP-PRIMARY)" }, "form": "defaultMarker", "tooltip": (marker) => marker.metadata.get("name"), }, { "match": (marker) => marker?.metadata.get("trackId") === "av:track:video:default", "track": "av:track:video:default", "title": "Issues", "markerType": "Manual", "order": 1, "markerStyle": { backgroundColor: "var(--AP-WARNING)" }, "form": "issueForm", "tooltip": (marker) => marker.metadata.get("type") + " issue: " + marker.metadata.get("issue"), } ] } ] } }
This should result in a settings file that looks something like this:
{ "forms": { "defaultMarker": { "schema": { "type": "object", "properties": { "name": { "type": "string" } } }, "uischema": { "type": "VerticalLayout", "elements": [ { "type": "Control", "label": "Name", "scope": "#/properties/name" } ] } }, "issueForm": { "schema": { "type": "object", "properties": { "issue": { "type": "string", "maxlength": 16 }, "type": { "type": "string", "enum": ["audio", "video", "subtitles"] } }, "required": ["type"] }, "uischema": { "type": "HorizontalLayout", "elements": [ { "type": "Control", "label": "Issue", "scope": "#/properties/issue", }, { "type": "Control", "label": "Issue type", "scope": "#/properties/type", "options": { "format": "radio" } } ] } } }, "markers": { "groups": [ { "match": (marker, track) => marker?.type === "Manual" || track?.type === "Manual", "title": "Manual", "id": "Manual", "alwaysShow": true, "allowCreateTrack": true, "trackType": "Manual", "rows": [ { "match": (marker) => marker?.metadata.get("trackId") === "av:track:video:issue", "track": "av:track:video:issue", "title": "Notes", "markerType": "Manual", "order": 0, "form": "markerDefault", "tooltip": (marker) => marker.metadata.get("name"), "markerStyle": { backgroundColor: "var(--AP-PRIMARY)" }, }, { "match": (marker) => marker?.metadata.get("trackId") === "av:track:video:default", "track": "av:track:video:default", "title": "Issues", "markerType": "Manual", "order": 1, "form": "issueForm", "tooltip": (marker) => marker.metadata.get("type") + " issue: " + marker.metadata.get("issue"), "markerStyle": { backgroundColor: "var(--AP-WARNING)" }, } ] } ] } }
This is how the resulting player will look like:
We start off by defining a form under the
forms
assetMetadataForm
title
description
genre
You can read more about the available types here: json-schema.org/understanding-json-schema/reference/type.html Add the following to the root object in settings file:
{ "forms": { "assetMetadataForm": { "schema": { "type": "object", "properties": { "title": { "type": "string" }, "description": { "type": "string" }, "genre": { "type": "string", "enum": [ "Action", "Drama", "Horror", "Comedy", "Thriller", "Western" ] } } } } } }
The Next step is to define how the form should look like, this is done by defining a
uischema
schema
Read more about available layout elements here: jsonforms.io/docs/uischema/layouts Combining the
with the previous step results in the following:uischema
{ "forms": { "assetMetadataForm": { "schema": { "type": "object", "properties": { "title": { "type": "string" }, "description": { "type": "string" }, "genre": { "type": "string", "enum": [ "Action", "Drama", "Horror", "Comedy", "Thriller", "Western" ] } } }, "uischema": { "type": "VerticalLayout", "elements": [ { "type": "Control", "label": "Title", "scope": "#/properties/title" }, { "type": "Control", "label": "Description", "scope": "#/properties/description", "options": { "format": "textarea" } }, { "type": "Control", "label": "Genre", "scope": "#/properties/genre" } ] } } } }
What metadata form to use for assets, videos, audio or subtitle files are configured in the Metadata View settings. You select the form you want to use under the different field sets using the
form
{ "metadataViews": [ { "id": "defaultMetadataView", "name": "Default", "description": "The default metadata view", "active": true, "fieldSets": { "asset": { "form": "assetMetadataForm", // <-- Form selector for assets "readOnlyFields": [ ... ] }, "videoFile": { "form": "videoFileForm", // <-- Form selector for video files "readOnlyFields": [ ... ] }, "audioFile": { "form": "audioFileForm", // <-- Form selector for audio files "readOnlyFields": [ ... ] }, "subtitleFile": { "form": "subtitleFileForm", // <-- Form selector for subtitle files "readOnlyFields": [ ... ] } } } ] }
This should result in a settings file that looks something like this:
{ "forms": { "assetMetadataForm": { "schema": { "type": "object", "properties": { "title": { "type": "string" }, "description": { "type": "string" }, "genre": { "type": "string", "enum": [ "Action", "Drama", "Horror", "Comedy", "Thriller", "Western" ] } } }, "uischema": { "type": "VerticalLayout", "elements": [ { "type": "Control", "label": "Title", "scope": "#/properties/title" }, { "type": "Control", "label": "Description", "scope": "#/properties/description", "options": { "format": "textarea" } }, { "type": "Control", "label": "Genre", "scope": "#/properties/genre" } ] } } }, "metadataViews": [ { "id": "defaultMetadataView", "name": "Default", "description": "The default metadata view", "active": true, "fieldSets": { "asset": { "form": "assetMetadataForm", // <-- Form selector for assets "readOnlyFields": [ ... ] }, "videoFile": { "form": "videoFileForm", // <-- Form selector for video files "readOnlyFields": [ ... ] }, "audioFile": { "form": "audioFileForm", // <-- Form selector for audio files "readOnlyFields": [ ... ] }, "subtitleFile": { "form": "subtitleFileForm", // <-- Form selector for subtitle files "readOnlyFields": [ ... ] } } } ] }
The resulting form should look something like:
This section describes how to add custom forms to the asset status modals.
Multiple forms can be defined by adding several objects to the forms array, it is important to assign unique id fields so that they can be referenced later. This example contains three different custom forms with some validation rules.
The first form,
inProgressAssetStatusForm
asset_status_assignee
enum
required
The second form,
approvedAssetStatusForm
asset_status_metadata
asset_status_audio
asset_status_video
enum
null
The third form,
rejectedAssetStatusForm
{ forms: { inProgressAssetStatusForm: { schema: { type: "object", properties: { asset_status_assignee: { type: "array", items: { enum: ["Anna", "Bert", "Adam", "Nina", "Poro"], }, }, }, required: ["asset_status_assignee"], }, }, approvedAssetStatusForm: { schema: { type: "object", properties: { asset_status_metadata: { type: "boolean", default: null, enum: [null, true], }, asset_status_audio: { type: "boolean", default: null, enum: [null, true], }, asset_status_video: { type: "boolean", default: null, enum: [null, true], }, asset_status_comment: { type: "string", }, }, required: [ "asset_status_metadata", "asset_status_audio", "asset_status_video", ], }, }, rejectedAssetStatusForm: { schema: { type: "object", properties: { asset_status_comment: { type: "string", }, }, required: ["asset_status_comment"], }, }, } }
A
uischema
HorizontalLayout
VerticalLayout
enum
format
{ forms: { inProgressAssetStatusForm: { schema: {...}, uischema: { type: "VerticalLayout", elements: [ { type: "Control", label: "Assigned to", scope: "#/properties/asset_status_assignee", options: { format: "multiselect", }, }, ], }, }, approvedAssetStatusForm: { schema: {...}, uischema: { type: "VerticalLayout", elements: [ { type: "Control", label: "Metadata reviewed", scope: "#/properties/asset_status_metadata", options: { toggle: true, }, }, { type: "Control", label: "Audio reviewed", scope: "#/properties/asset_status_audio", options: { toggle: true, }, }, { type: "Control", label: "Video inspected", scope: "#/properties/asset_status_video", options: { toggle: true, }, }, { type: "Control", label: "Additional comments", scope: "#/properties/asset_status_comment", options: { format: "textarea", }, }, ], }, }, rejectedAssetStatusForm: { schema: {...}, uischema: { type: "VerticalLayout", elements: [ { type: "Control", label: "Leave comment", scope: "#/properties/asset_status_comment", }, ], }, }, } }
To assign a form to a specific status you need to add the
form
{ assetStatus: { statusMetadataFieldName: "asset_status", commentMetadataFieldName: "asset_status_comment", statusSetByMetadataFieldName: "asset_status_set_by", statuses: [ { key: "in_progress", labels: { status: "In progress", }, form: "inProgressAssetStatusForm", // <-- Form selector for In progress status color: "var(--AP-FOREGROUND-2)", }, { key: "pending", labels: { status: "Pending", }, color: "var(--AP-FOREGROUND-2)", }, { key: "approved", type: "approved", labels: { status: "Approved", assign: "Approve", }, form: "approvedAssetStatusForm", // <-- Form selector for Approve status color: "var(--AP-SUCCESS)", }, { key: "rejected", labels: { status: "Rejected", assign: "Reject", }, form: "rejectedAssetStatusForm", // <-- Form selector for Rejected status color: "var(--AP-ERROR)", }, ], } }
This should result in a settings file that looks something like this:
{ forms: { inProgressAssetStatusForm: { schema: { type: "object", properties: { asset_status_assignee: { type: "array", items: { enum: ["Anna", "Bert", "Adam", "Nina", "Poro"], }, }, }, required: ["asset_status_assignee"], }, uischema: { type: "VerticalLayout", elements: [ { type: "Control", label: "Assigned to", scope: "#/properties/asset_status_assignee", options: { format: "multiselect", }, }, ], }, }, approvedAssetStatusForm: { schema: { type: "object", properties: { asset_status_metadata: { type: "boolean", default: null, enum: [null, true], }, asset_status_audio: { type: "boolean", default: null, enum: [null, true], }, asset_status_video: { type: "boolean", default: null, enum: [null, true], }, asset_status_comment: { type: "string", }, }, required: [ "asset_status_metadata", "asset_status_audio", "asset_status_video", ], }, uischema: { type: "VerticalLayout", elements: [ { type: "Control", label: "Metadata reviewed", scope: "#/properties/asset_status_metadata", options: { toggle: true, }, }, { type: "Control", label: "Audio reviewed", scope: "#/properties/asset_status_audio", options: { toggle: true, }, }, { type: "Control", label: "Video inspected", scope: "#/properties/asset_status_video", options: { toggle: true, }, }, { type: "Control", label: "Additional comments", scope: "#/properties/asset_status_comment", options: { format: "textarea", }, }, ], }, }, rejectedAssetStatusForm: { schema: { type: "object", properties: { asset_status_comment: { type: "string", }, }, required: ["asset_status_comment"], }, uischema: { type: "VerticalLayout", elements: [ { type: "Control", label: "Leave comment", scope: "#/properties/asset_status_comment", }, ], }, }, }, assetStatus: { statusMetadataFieldName: "asset_status", commentMetadataFieldName: "asset_status_comment", statusSetByMetadataFieldName: "asset_status_set_by", statuses: [ { key: "in_progress", labels: { status: "In progress", }, form: "inProgressAssetStatusForm", // <-- Form selector for In progress status color: "var(--AP-FOREGROUND-2)", }, { key: "pending", labels: { status: "Pending", }, color: "var(--AP-FOREGROUND-2)", }, { key: "approved", type: "approved", labels: { status: "Approved", assign: "Approve", }, form: "approvedAssetStatusForm", // <-- Form selector for Approve status color: "var(--AP-SUCCESS)", }, { key: "rejected", labels: { status: "Rejected", assign: "Reject", }, form: "rejectedAssetStatusForm", // <-- Form selector for Rejected status color: "var(--AP-ERROR)", }, ], } }
The resulting forms should look something like:
It's possible to have markers assigned to different timeline rows based on data entered into the form, similar to how tooltips reference data from the form. For the second example it would be possible to add rows for each of the different types of issues and assign markers among them by changing the match function, like this:
match: (marker) => marker?.metadata.get("type") === "audio",
Json forms has a powerful rules system which allows controls to be hidden or disabled depending on values in the underlying metadata, there's more information available about that here: jsonforms.io/docs/uischema/rules Json forms also provides a wide array of examples on how to set up different types of forms over here: jsonforms.io/examples/basic