Squarebox

CatDV JavaScript Support

You can use JavaScript as a simple and powerful mechanism to extend and customise the functionality of CatDV in a number of ways:

A calculated user-defined field can be based on evaluating a JavaScript expression. (A calculated expression can also be used in many other situations in both CatDV and the worker where text is specified, for example to burn in text when exporting a movie, to calculate the path in a worker processing step, when performing a bulk edit command, etc.)

A UI script is executed within the desktop client and web client and can respond to fields being edited, buttons being clicked, or clips being loaded in order to customise the appearance of fields or values shown in a picklist, to show or hide details panel tabs based on the value of certain clip fields, to perform validation, and so on.

An ‘Execute JavaScript’ processing step in the Pegasus Worker can be used as an alternative to executing a batch or shell script through the command line or using a tool like Node.js.

Similarly, you can include an ‘Execute JavaScript’ processing step in a Pegasus custom action.

A worker plugin provides broadly similar functionality to executing a JavaScript processing step but encapsulates the script, plus a user interface to configure it and any additional resources or libraries, as a single deployable extension that appears as a named processing step such as "Upload to YouTube", similar to other built in processing steps

There is a lot of commonality and overlap between the API provided in each of these situations but there are also some differences, highlighted later in this document.

Please note that JavaScript support is not an end user feature and is provided for the use of the Square Box professional services team or by approved resellers and systems integrators who have completed an advanced CatDV U training course. The features described below are subject to change without notice. Technical support on JavaScript will normally constitute chargeable work.

Also, please note that the use of JavaScript worker plugins, other than in development mode, will normally require a deployment license. Please enquire before relying on the use of JavaScript in your worker actions!

Object model

To allow the JavaScript script to interact with the rest of CatDV the host environment provides a simple object model.

· In most cases you will have a clip object available, ie. the current selected clip which the command applies to. This is normally passed in to the current function as a variable called ‘clip’ and has a number of fields and methods available, described below.

· In some cases the command is applied to a collection of clips and you are passed an array ‘clips’

· Other objects represent clip event markers, a catalog, or a sequence

· The global variable ‘CatDV’ provides access to a number of utility functions

Accessing clip fields

Once you have a reference to a clip you can read clip fields in various ways:

clip.get("NM1") – using a legacy attribute id

clip.get("@FNumber") – access a media metadata field using a legacy attribute id

clip.get("clip[my.user.1]") – using canonical name of a user field (same as REST API)

clip.name – directly access a clip field as if it was a member using its canonical name (same as REST API)

clip.fields["my.user.1"] – directly access a user-defined clip field by its id, but now you’re accessing a javascript associative array clip.fields by looking up a string so you need to quote the id.

clip.media.fields["FNumber"] – access a media metadata field via an associative array so you need to quote the id

clip.media.audioFormat – access a built in media field

clip.catalog.fields["my.catalog.field"] – access a custom catalog field

clip.markers – access to the markers on a clip, where each marker has fields 'name', 'in', 'out', 'description' and 'category', eg. clip.markers[0].name

clip.poster – access to the jpeg bytes of a poster thumbnail as a byte array. When setting the poster the image data can be provided: as a byte array, as a filename (to load the thumbnail from an image file), or as a timecode value (to generate a thumbnail from the movie at that frame).

Note that in the past it was possible to access user-defined clips using a shorter notation like clip[‘myField’] or even clip.myField if the name didn’t contain special characters but this was an implementation side-effect and wasn’t recommended, and is no longer supported unless specifically enabled with legacy.identifiers=true. Instead you should update your scripts to use clip.fields[‘myField’] or clip.get(‘clip[myField]’). You should also write ${clip[myField]} instead of ${myField} in variable expressions.

As well as reading clip fields you can modify them, eg. clip.set("NM1", "New name") or clip.name = "New name".

Note that multi value fields (multi checkbox, multi linked hierarchy, etc.) store the value as a newline separated list of values, so if you want the values as an array use clip.fields['my.multi.field'].split('\n'). Note also that a hierarchy field stores the full value like "France/Paris" even though just "Paris" is shown in the user interface.

To look up the name of a field you can use tool tip text in the details panel, refer to the REST API documentation, or consult the Professional Services team.

Note that versions of the REST API prior to version 8 used an older syntax to access fields, clip.userFields[‘my.user.1’], media.metadata[‘FNumber’] instead of the newer more consistent use of ‘fields’ everywhere.

Variable expressions and calculated fields

In the desktop client and in the Worker Node there are lots of situations where a textual parameter needs to be specified, for example when transcoding a movie with burnt-in text, or when specifying a file or directory in a scripted worker action or Pegasus custom action, and so on.

For some time CatDV and the worker have allowed variable expressions such as “Title: ${NM1}” or “$p{s/.mov$//}” to be used in these situations, where the expression is evaluated based on fields from the current clip, or variables like $p and $i that have been set based on the current file.

Variable expressions are also used when defining the value of a user-defined calculated field.

When accessing clip fields in a variable expression, originally only old-style attribute ids like ${NM1}, ${U3}, ${MF}, ${@FNumber} were supported, but this has since been extended to also support more mnemonic attribute ids similar to those use in the server REST API, like ${clip.name}, ${custom.clip.field.id} for user-defined fields, and so on. To see the attribute id for a field look at the tool tip over fields in the details panel, in the field chooser when customising views, and in Preferences > Field Definitions > Built-In Fields.

As well as substituting the value of a clip field or path variable, with an optional regular expression to modify the value, it is possible to use a javascript expression instead. To do this, the expression must start with “javascript:” (or “js:” as a more compact short cut) and return a value, eg. “javascript:2+3” would evaluate to “5”. When evaluating a javascript expression the $() function can be used to access variables like $p, eg. js:$(‘$p’), and also clip fields using old style attribute ids.

Taken together his means there are quite a few different ways to access clip fields in a variable expression:

· ${NM1}, ${U1}, ${@MXF UMID} – using old attribute id

· ${clip.name}, ${my.field.id}, ${media[MXF UMID]}, ${importSource[UnixFileMode]}, ${catalog[my.project.id]} – new attribute id (though strictly speaking there is no attribute in the desktop client for custom catalog fields, which means they can’t directly be shown in a view definition or details panel layout, but can be accessed via a variable expression or calculated field)

· js:clip.name, js:clip.fields['my.field.id'], js:clip.media.fields['MXF UMID'], js:clip.catalog.fields['my.project.id'] – using clip object

· js:$('NM1'}, js:$('clip.name'), js:$('my.field.id'), js:$('media[MXF UMID]') – using the $() function

· js:clip.get(‘NM1’) – using get() function

Most of these should work equally in the desktop client, worker, or web interface, though the internal implementations are different between the desktop/worker and web interface, so it’s worth testing on both platforms. Which to use will largely depend on the complexity of the expression and personal preferences.

If you want to access a basic variable expression or regular expression from javascript from within a worker script or Pegasus custom action then you can use expand(expr), or the underlying CatDV.expandVariables(expr, params, clip) function if you want to provide parameters like $g in a map or apply it to a different clip.

Calculated picklists

The calculated picklist field provides the same functionality as a regular picklist field, but the set of values the user selects from is calculated on the fly using JavaScript. The script can also make use of any of the usual CatDV.* functions from the CatDV JavaScript API, and can access the following scope variables:

· picklistValues – an array of strings containing the pre-defined picklist values if present

· clip – the currently selected clip

To allow asynchronous operations to be performed the picklist values are returned to CatDV using the following callback:

· setValues(values:string[]) – callback function to return the clip list values to CatDV

For example

var values = [];
for(var i=0; i < 10; i++)
{
   values.push(“Value “ + i);
}
setValues(values);

UI script

The UI script is available when logged on to the Enterprise server. It is stored on the server in the settings table so that it is loaded when the user logs on. The current implementation uses a single script across all production groups. The script needs to be installed by the Professional Services team.

The UI script has handlers that respond to various events, such as creating a field in the details panel when the user interface is loaded, responding to changes to a clip, etc.

· catdv_onCreateField(group, fieldId) – return an object to override a field definition (see below)

· catdv_onLoad(clip) – called when a clip is selected, for example to show or hide details panel tabs or change pick list values depending on the value of a field

· catdv_onUpdate(clip, fieldId, interim) – called after a clip is edited, with similar capabilities to the onLoad handler. In the web client, this is called for every amendment (potentially every keypress), with interim=true; test for interim=false if you only want to process updates when a field loses focus. (In the desktop client, interim is always false.)

· catdv_onImport(clips) – called after clips are imported into the catalog, allowing other fields to be populated automatically (but note that this function isn't guaranteed always to be called, for example if the user isn't logged on at the time, if a clip is created by the worker, etc.)

· catdv_onValidateClip(clip) – called when a clip is about to be saved or another clip is being loaded into the details panel. This function can return an error message that is displayed if the clip isn't valid (for example, to say that some fields are missing or contain inconsistent values) but this is only advisory and isn't guaranteed to prevent the changes from being saved

· catdv_onAction(type, fieldId, value) – unlike onUpdate, this is called before an action is performed, for example a select button is pressed, a value is chosen from a drop down pick list, or a toolbar button or menu command is invoked. This method can return true to silently intercept and reject a button click so it isn't performed, or a message that is displayed explaining why the action isn't valid. Return null or false to let the action proceed normally. The type argument is one of "MenuItem", "CheckBox", "ComboBox", "RadioButton", "TextField" or "Button".

· catdv_onMarkerSelected(clip, marker) – this function is called when the movie play head moves over an event marker (or a marker is selected from the drop down in the movie controller). The method is also called (with marker set to null) when the play head leaves the marker again. For range markers the specified range of the marker is used, for event markers the marker stays active for a second or two after the playhead moves over the marker.

· catdv_onCreateMarker(clip, marker) – this function is called when the user creates a new marker, just before the marker dialog is displayed, giving the UI script the chance to populate default values for the fields. If required the function can return an error message to prevent the marker being created.

· catdv_onNotification(notifications) – this function is called whenever one or more notifications are received from the server

· catdv_onConfirmDownload(clips, confirm) – this function is called from the web client when the user attempts to download a file and could display a copyright message or perform other validation before calling confirm() to permit the download to proceed, eg.

function catdv_onConfirmDownload(clips, confirm)
{
  CatDV.askOptionsAsync(“Confirm”, “This is copyrighted!”, ["Decline", "Accept"], function(answer)
  {
      if (answer=="Accept") confirm();
  });
}

You don't need to provide all the event handler functions, only ones you are interested in responding to. If a function throws an error for any reason it is automatically disabled until the script is reloaded.

Certain handlers relate to particular fields being edited, in which case the canonical field id (eg. "clip.name") is passed in to the method.

Uses of UI scripts

One common use of a UI script is to override the definition of a field, either based on the current production group (if the same field has different definitions in different groups for example), or if the same list of pick list values is shared between fields.

The onCreateField handler can return the following properties to change the field definition: label – the displayed field label, readOnly – set the field read only or not, twoColumns – set the field to be displayed in two details panel columns, multiline – set the field to be displayed over multiple rows, hideIfBlank – suppress the field unless it has a value, visible – show or hide the field, fieldID – inherit all the values from another field definition (including the label of that field), basedOn – inherit all the values from another field definition except for the field label.

Another common use of UI scripts is to show or hide certain tabs dynamically based on the current clip, for example to show different logging fields depending on the type of asset. This is done using the onLoad and onUpdate handlers. These can also be used to populate pick list values depending on the current clip to provide functionality similar to linked hierarchy fields, or to load pick lists from another source.

Other advanced capabilities include implementing action buttons, highlighting fields depending on the current marker, and more.

Worker plugin extensions

A worker extension can be installed by placing a .catdv Worker Plugin Bundle in the appropriate extensions directory:

Mac OS X: /Library/Application Support/Square Box/Extensions

Windows: %ProgramData%\Square Box\Extensions

Linux: /usr/local/catdvWorker/extensions

Each extension function is defined by a .jx (or .jxe) JavaScript file. This file should set various fields on the global 'plugin' object that is passed in when the script is loaded to describe the plugin:

plugin.name = "My plugin";
plugin.description = "This is a test plugin";
plugin.version = “1.0.1”;
plugin.batchMode = false;
plugin.section = “File actions”;
plugin.licenseKey = “My Plugin”;
plugin.fields = [ … ];
plugin.onLoad = function(params) {return plugin.fields;}
plugin.dynamicUI = true;
plugin.onValidate = function(params) {
  if (!params[‘xyz]’) return “Missing xyz”;
  else return null;
}
plugin.method = function(clip, clips, params, variables) { … }

The 'batchMode' flag should be set to true if the extension can be used in bulk query, quick folder scan, and batch file watch actions, in which case the method is invoked just once to process all the matching files or clips in one go. (Normally the processing step is applied to each clip or file individually in turn and isn't available in bulk processing watch actions.) This will also enable the extension to be used in a scheduled task, in which case ‘clips’ will be null.

The plugin 'fields' parameter is a list of fields that will be displayed when configuring the processing step, for example:

plugin.fields = [ 
{ id: "method", label: "Method:", type: "radio", values: [ "Remove spaces", "Remove punctuation", "Remove both" ], default: "Remove spaces" },
	{ id: "strict", label: "Strict:", type: "checkbox", value: "Avoid anything nasty", default: “true” },
	{ id: "maxlen", label: "Maximum length:", type: "number", default: “32” },
	{ id: "pattern", label: "Pattern:", type: "text" }
];

This would display four fields in the worker configuration editor, and the values entered can then be accessed as params['method'] etc. when the processing step is performed.

'id' is the identifier used to access the value of a parameter, 'label' is the label shown in the configuration editor, 'type' is one of "radio", "checkbox", "combo", "multiline", "password", "fieldChooser", "openDir", "openFile", "saveDir", "saveFile", or "text". 'values' specifies the values for a radio button or combo box field, and optionally for a checkbox field (a checkbox field can have a single value, in which case the returned value is “true” or “false”, or multiple values, in which case the returned value is a new-line separated list of selected values). 'default' is an optional initial default value.

You can optionally specify an onLoad() function instead of a fixed fields array to dynamically define the fields to show when editing the processing step. If you set dynamicUI then onLoad() will be called again every time there is a change to a drop down or checkbox or radio button, allow you to dynamically refresh the user interface. You can also specify an onValidate() function that is called after editing the processing step and return a message if there is a validation error.

When the processing step is performed the method is called with both the main clip and a list of clips. In normal use with a watch folder or server query action there will just be a single clip (which could be a metaclip), but if you import a file with scene detection enabled, or import a Final Cut XML file or similar, then importing a single file could result in multiple clips. You would also use the 'clips' argument in bulk processing steps.

The 'params' argument gives access to parameters entered when the processing step is configured. Just as with other types of processing steps, any variables in the parameters would already have been expanded by the time the function is called, but you can also directly access any worker variable, for example variables['$g'].

If the function returns a string the message is logged in the task summary but other than that the return value has no effect. To indicate a fatal error which will cause the watch action task to fail you should throw an exception, eg. throw "Failed to copy file".

Approved plugins need to be encrypted and will need a license code to fully function but can be tested in development mode (which runs one task at a time) without needing a license. To allow several plugins to share the same license key you can specify plugin.licenseKey. You can also set this to “none” if the plugin doesn’t need to be licensed (though it still needs to be encrypted).

Worker extension bundles

During development create a subdirectory in the extensions directory and place .jx file or files in this (it’s possible for one plugin bundle to implement more than one extension function). You can also include other scripts and resources that are used by those extension(s) in that directory, eg.

myplugin/archive.jx
myplugin/restore.jx
myplugin/sharedLibrary.js
myplugin/image.png

In development mode you can directly run the extensions from these .jx files.

To deploy them the .jx files need to be encrypted (and the other files can be encrypted too if desired) This needs to be done by the CatDV Professional Services team:

myplugin/archive.jxe
myplugin/restore.jxe
myplugin/sharedLibrary.jse
myplugin/image.png.enc

Finally, package them up “jar cf myplugin.catdv myplugin” to create an installable plugin bundle. The ‘myplugin.catdv’ file can then be dropped onto a worker node editor and the user will be prompted to install it.

While developing plugins take care not to leave both the myplugin directory and myplugin.catdv file, or both .jx and .jxe files, alongside each other in the extensions directory otherwise you will get duplicate definitions when the extensions are loaded.

When an extension runs the CatDV.readFile() and CatDV.requires() functions will automatically read a file from the bundle, and decrypt is as required, eg.

var image = CatDV.readFile(“myplugin/image.png”);

The extensions .jxe (encrypted .jx extension), .jse (encrypted .js script) and .enc (other encrypted resources) are recognised for encrypted files.

For final distribution you would normally combine myplugin.catdv and a ReadMe.txt file as a ZIP archive myplugin1.0.1.zip whose name includes the version number, but not put a version number on the myplugin.catdv file.

Worker JavaScript triggers

If you have a worker watch folder or quick folder scan watch action you can add a JavaScript filter step to customise the behaviour of the watch action at the trigger stage, ie. before or when the watch folder is scanned, rather than just once a task is queued and runs as you do with a normal JavaScript processing step.

One or more of the following functions can be defined, though you wouldn’t use all of them together as some of them are mutually incompatible or provide alternative ways of achieving similar results.

expandRoots(existingRoot) – override the watch folder root directory and dynamically determine the directory (or directories) to be scanned, perhaps as a result of a REST API call. The root directory as configured is passed in and this function returns an array of zero or more strings representing the paths of directories to be scanned.

acceptsFile(file) – optionally filter files before they are queued, return false to reject the file. This filters files one at a time.

filterFiles(files) – filter the entire collection of files that the watch folder has identified before they are queued. This filters all the files in one go, an array of file paths is passed in and returned by the function.

generateFiles() – return an array of file paths to dynamically determine the files to be queued. These files can be anywhere and do not need to exist in the watch folder directory (in fact, unless you explicitly define a watch folder with expandRoot the watch folder isn’t scanned at all).

The above methods are all called periodically at the watch folder scan time, based on the check interval. Slightly different is the following function which is called whenever a notification is received by the main worker process.

onNotification(notification) – a JSON notification object is passed in

notificationFilter() – optionally return an array of notification types such as [ ‘clip.update’, ‘mediaStorePath.insert’ ] that this handler is interested in, otherwise all notifications other than ‘worker.progress’ are passed in which could have a negative impact on performance.

Pegasus custom actions & Pegasus Worker scripts

If you use CatDV Pegasus you can include a Javascript processing step when defining a custom action, giving you full access to all the capabilities provided by the CatDV Javascript API:

function perform(clip, clips, variables)
{
}

Similarly, with Pegasus Worker, you can define a Javascript processing step. This can either directly accesses the variables ‘clip’, ‘clips’ and ‘variables’ or define a function with the same prototype as above, in which case that function will be called.

In both cases global functions defined on the global ‘CatDV’ object can be called, as well as a function called expand() which is provided as a convenience to access CatDV variable expressions, eg.

var path = expand(“$f{s,/[^/]+,,}”);

Global functions

The ‘CatDV’ object provides access to a number of utility functions and the current context in which the script is executing.

The following methods give basic information about the environment in which the script is running:

· CatDV.isDesktopClient() – returns true in the CatDV desktop client, false elsewhere

· CatDV.isWebClient() – returns true when running in the web interface

· CatDV.isWorkerNode() – returns true in worker scripts

· CatDV.getSoftwareVersion() – return current software eg. "CatDV Pegasus 12.1b6"

· CatDV.getUserName(), getUserRole() – return the current user name (and permissions role)

· CatDV.getProperty(name) – return additional information, for example 'serverVersion', 'groupName', 'currentTab', 'catalogName', 'ffmpegPath', and java system properties like 'user.dir' or 'os.name' are provided in the desktop client

· CatDV.getPreferenceItem(name) – return a preference item eg. tree.style, or a worker advanced property value. You can also modify a value with setPreferenceItem(name, value) though the effect of this is not guaranteed.

· CatDV.getScriptFile() – return the path to the current script file if this is a worker extension

These methods give access to useful lists of data:

· getAllGroups() – returns an array of group names

· getMyGroups() – returns an array of group names that the user is a member of

· getAllUsers() – return a list of all user names on the system

· getPicklistValuesAsync(fieldId, callback) – return the picklist values for a field as an array, passing the data to a callback function once it is available

· getPicklistValues(fieldId) – return the picklist values immediately

These utility methods relate to JavaScript:

· CatDV.stringify(object) – equivalent to JSON.stringify() in case that isn't present (this implementation may behave slightly differently if passed a java rather than javascript object – need to check)

· CatDV.parse(json) – equivalent to JSON.parse() in case that isn't available (again, this might slightly different objects based on a java map rather than a javascript bridge object – need to check)

· CatDV.log(object, …) – log one or more messages to the error log for debugging etc.

· CatDV.requires(module) – similar to the Node.js ‘requires’ keyword, this will load a library from another file, returning whatever is placed in the global 'exports' variable. For example, in myScript.js you could define:

exports.doSomething = function() ...

Then to access this in main script:

var myModule = CatDV.requires('myScript.js');
myModule.doSomething();

If you write CatDV.requires("myModule") this will load my myModule/index.js, where myModule is a subdirectory of the CatDV worker extensions directory.

You can also write

exports = { doSomething: function() … ,  }

User interface functions

The following methods relate to UI scripts only (and to Pegasus custom actions), and provide interaction with the desktop client user interface (the details panel, movie panel, menu commands, etc.):

· CatDV.setTabVisible(tabName, visible) – show or hide a details panel tab (and select it)

· CatDV.setPicklistValues(fieldID, values[]) – set the pick list values for a drop down pick list

· CatDV.setFieldOptions(fieldID, options) – show or hide a field on a details panel tab or make it read only. The options object can contain the parameters 'tabName' (to narrow down which tab to apply to if the field occurs in more than one place), 'readOnly', and 'visible' (also ‘redraw’ so that if you're setting options on lots of fields you can delay the panel redraw to until the end, because redrawing after every change is very slow).

· CatDV.getClip() – get the current clip (though it's normally passed in to handler functions)

· CatDV.getMovieTime() – return current play head timecode

· CatDV.setMovieTime(timecode) – set the play head position

· CatDV.getPlaybackRate() – get current movie playback rate

· CatDV.setPlaybackRate(rate) – set movie playback rate

· CatDV.performMenuCommand(name, [arg, callback]) – experimental feature to invoke a desktop client menu command by name. Very limited interaction with the user interface of the command is available. If the command prompts for a file, or prompts for a simple string, this can be provided by specifying the argument ‘arg’ as a string. The optional callback function is called when the command completes and is passed two arguments: any completion message which is displayed by the command, and any exception or error message reported by the command.

· CatDV.selectClip(clip, action) – select a particular clip in the main window and optionally perform a double click action on it (one of: ‘editSequence’, ‘mediaDialog’, ‘play’, ‘fullscreen’, ‘verbatim’, ‘html’, ‘movieInfo’, ‘details’, ‘launchApp’, or null). Return true if the clip was found in the main window.

· CatDV.openClip(clipId, title) – load a clip from server (given its unique database id) and open it in a new tab with the given title. If that clip is already open in the current catalog, and the title is null, the user is given the option to select that clip instead in the current tab instead.

· CatDV.openTab(clips, title, [temporary]) – display a list of clips (eg. as returned by importClips or performQuery) as a regular or temporary tab in the main window. Opening a temporary tab will replace an existing temporary tab.

Displaying messages and getting user input

There are several methods for displaying messages and getting input in UI scripts, though only the asynchronous versions are available in the web interface:

· CatDV.showMessage(msg) – display a simple message dialog

· CatDV.askOKCancel(msg) – display a simple confirmation dialog

· CatDV.askQuestion(prompt, defaultValue) – display a simple input dialog

· CatDV.askOptions(title, prompt,

· CatDV.askOKCancelAsync(title: string, msg: string, okCallback: () => void)) – display a simple confirmation dialog and call okCallback if OK is pressed

· CatDV.askQuestionAsync(title:string, prompt: string, defaultValue: string, callback: (answer) => void)) – display a simple input dialog and pass the answer to the callback

· CatDV.askOptionsAsync(title:string, prompt: string, options : string[], callback: (option) => void) – display a confirmation dialog with a list of button choices such as “Yes”, “No”, “Cancel”.

· CatDV.chooseFile(prompt:string, defaultFile:file-or-path, showDirChooser:bool, forSaving:bool) – display a file or directory chooser and return the selected file path or null if cancel was pressed.

File utilities

The following file handling functions are available:

· CatDV.listFiles(dir) – return an array of file names given a directory path, or null if the directory doesn't exist

· CatDV.fileExists(pathOrClip) – given either a file path as a string, or a clip with a media path, returns true if the media file exists

· CatDV.getFileInfo(pathOrClip) – given an absolute file path returns an object containing information about the file. The returned object has the following properties: exists, isDirectory, length (in bytes), and lastModified (as a timestamp object).

· CatDV.createFolder(path) – creates a directory or folder at the specified absolute path. Will attempt to create any required parent folders.

· CatDV.deleteFile(path) – returns true if the file given by the file path was successfully deleted

· CatDV.copyFile(pathOrClip, destination) – copy a file or files to a new destination directory and return true for success, false for failure.

· CatDV.moveFile(pathOrClip, destination) – move a file or files, or rename a file and return true for success, false for failure.

For copyFile and moveFile the source file can either be specified by a file path (as a string), or it can be a clip, in which case the media file for that clip is copied. In the case of a metaclip, all the files are copied, and relative paths are preserved. In the case of copying or moving a single file the destination path is taken to be the complete file path (so you can rename a file) unless it ends in a file separator slash or refers to an existing directory.

· CatDV.readFile(path) – read the entire contents of a file and return it as a byte array (see below)

· CatDV.readChunk(file, offset, length) – read part of a file (up to length bytes, starting at offset) as a byte array (see below).

· CatDV.writeFile(path, text) – create (or overwrite) a file with the given text or byte array

· CatDV.appendFile(path, text) – append the given text or byte array to an existing file (which is created if it doesn’t already exist)

· CatDV.getAllFiles(pathOrClip) – search the media stores and return an array with all the proxies and hi-res files that exist for a path. Each file is returned as an object eg. { path: ‘/Volumes/Media/File.mp4’, rootDir: ‘/Volumes/Media’, relativePath: ‘/File.mp4’, pathType: ‘Original/Hi-res’, mediaType=’hires’ }

· CatDV. getAllMediaFiles(rootPrefix) – return an array of all the known media paths below the given root directory as stored in the sourceMedia table on the server. Each file is described by a file info object eg. {fileDir:"/Users/rolf/Media/Recent Test Files/FolderB/", fileName:"466_0247.MXF", fileSize:1654507056, mtime:1596815514000 }

Executing commands and making http requests

· CatDV.execute(cmd, args, …) – execute an external command via the command line, specified by a file path and a list of arguments, and return the stdout output of the command

· CatDV.executeWithStatus(cmd, args, …) – execute an external command and return an object with fields exitStatus, stdout, and stderr.

· CatDV.executeWithEnvironment(args, envp, cwd) – as above but with the option to set environment environment variables. Pass in args as an array, and environment as an array of strings of the form name=value.

· CatDV.execJava(className, args, …) – launch an external java process using the embedded worker JVM specifying the class name. Note that it isn’t currently possible to specify additional files on the class path, but if you pass the path to a jar file instead of a class name it is executed like ‘java -jar xxx.jar’.

· CatDV.httpRequest(request) – make an HTTP request and return a result object. The request object has 'protocol', 'method', 'hostname', 'port', 'path', 'headers' and 'postData' fields. You can also directly pass in a URL as a string. The result object has 'statusCode', 'error', 'data' and 'headers' fields. The returned ‘data’ field is a byte array.

· CatDV.encryptPassword(password) – encrypt a password using the public key that the CatDV server we are connected to is using, for when we make a REST API http request to that server. But see also callRESTAPI().

· CatDV.setRESTAPIVersion(apiVersion) – sets the REST API version that the callRESTAPI() method will call. If not set the default is 7.

· CatDV.callRESTAPI(method, query, postData) – make a REST API call to the CatDV server we are connected to. This actually uses RMI as a short cut to get data provided by the REST API. If full access to headers etc. is required then make an http request instead. This request is synchronous and is only available in the worker. For example

CatDV.callRESTAPI("GET", "/info", null)

· CatDV.httpRequestAsync(request, callback) – similar to httpRequest but asynchronous, and call a callback function with the result.

· CatDV.callRESTAPIAsync(method, query, postData, callback) – similar to callRESTAPI but asynchronous, and call a callback function with the result.

Worker functions

· CatDV.callProcessingStep(action, params, clips) – programmatically invoke another worker processing step, for example

  CatDV.callProcessingStep("Dump Variables",
    { path: "/tmp/dump.out" }, null );

CatDV.callExtension(extension, args, clips) – programmatically invoke a worker extension, eg.

  CatDV.callExtension("S3/copy_to_storage",
    {serviceName:"Amazon S3", bucket:"B01"}, clip);

CatDV.failAndRetry(cause) – tell the worker to abort the current job because a transient error (eg. a networking issue) occurred and set the status to ‘Retry’. This will keep the task on the worker queue and retry it at progressively longer intervals (starting with a few minutes and increasing to several days apart). If it doesn’t succeed within around 10 days it will eventually fail. ‘cause’ is an optional message that describes the error.

CatDV.submit(jobName, clipsOrFiles) – tell the worker to directly queue a new task (or batch of tasks). ‘jobName’ is the name of the watch action and ‘clipsOrFiles’ is either a single item or an array of items; for file watch actions it’s a file name or list of file names, for server jobs it’s a clip id or list of clip ids. For cron jobs the argument is ignored. Returns the batch or task id.

· CatDV.sendEmail(recipients, subject, body) – send an email to a comma-separated list of recipients, using SMTP settings that have been set up in the worker config.

· CatDV.resyncWithServer(discardChanges) – refresh the current catalog or result set to pick up latest changes from the server, either publishing or discarding any changes that have been made so far.

· CatDV.setPersistentProperty(key, value) & getPersistentProperty(key) – store a value in the work set (alongside the task list) that persists between tasks and even restarting the worker.

Remote file access

In the Worker the following methods give access to remote file volumes:

CatDV.getRemoteVolumeDetails(identifier) – return details about a remote volume or file path (or null if not found), eg.

var vol = CatDV.getRemoteVolumeDetails(“[s3bucket]”);
CatDV.log(“Type “+vol.fs+”, url “+vol.url);

· CatDV.copyRemote(src, dest) – upload a local file to a remote volume, or download a remote file to a local path, eg.

CatDV.copyRemote(“[s3bucket]/media/myfile.mp4”, “/tmp/myfile.mp4”);

· CatDV.getRemoteFileInfo(path) – return details of a remote file, or null if it doesn’t exist and that can be determined easily (which it can’t always, depending on the file system), eg.

{ volume: “[s3bucket]”, path: “/media/myfile.mp4”, name: “myfile.mp4”, length: 16483341, modified: 1624468699887, isDirectory: false }

Miscellaneous functions

· CatDV.getMediaStores() – get a list of media stores defined on the server (as an array of SMediaStore objects, with ‘name’ and ‘paths’ fields).

CatDV.expandVariables(expr, params, clip) – expand a variable expression such as “${NM1}{s/x/y/}”

CatDV.put(key, value) & CatDV.get(key) – each time a JavaScript function is invoked in the worker or desktop client a new JavaScript engine is created, so functions that are defined or variables that are set will be lost from one invocation to the next. To get around this you can store values against the CatDV object itself, eg.

  CatDV.put(‘tempfile’, ‘/tmp/myfile.txt’);
  var data = CatDV.readFile(CatDV.tempfile);

· CatDV.sleep(time) – wait a fixed time in milliseconds. Return true for success or false if it was interrupted.

· CatDV.signSHA256RSA(input, privateKey) – sign a string using SHA256withRSA encoding (to support creating signed JSON Web Tokens)

· CatDV.base64UrlEncode(input) – encode a string or byte array using base64 url encoding

· CatDV.base64UrlDecode(text) – decode a base64 url encoded string and return the original contents as a byte array

· CatDV.setProgress(String msg, int val, int max) – report progress for long running tasks.

· CatDV.getDistinctValues(fieldId, catalogId, groupId) – return an array of strings with distinct values of a column that occur on the server. Set catalogId and groupId to -1 to return values across all catalogs and production groups. The field can be a numeric field id (as used by the worker command line or defined in the AttributeID class, eg. 103 for “Video format”) or the name/id of a field like “clip.name” or “U25”). See also performColumnQuery().

· CatDV.toJavascript(javaObject) – attempts to convert a native Java object as into the equivalent JavaScript object.

Timecode functions

These functions allow calculations to be performed with timecodes:

· CatDV.timecodeDiff(out, in) – subtract two timecode values and return the difference in seconds. You can also use this to convert a timecode value to seconds by passing null for the second parameter.

· CatDV.timecodeAdd(start, diff) – add a number of seconds (or a second timecode value) to a base timecode

· clip.getTimecode(timecode) – utility function method on the clip object to convert a string, eg. “0:00:05:00” or “00:00:10;00(5994)”, or a number of seconds since the start of the clip, to a timecode value. Unless a specific timecode format is specified the timecode will have the same time base as the clip.

· CatDV.getTimecode(timecode, format) – convert a string or number of seconds to a timecode value in the specified format. Format can be a clip to use the timecode format of that clip, a frame rate, or be left as null. The timecode can also be specified as a JavaScript object such as { “frm”: 120, “fmt”: 24, “secs”: 5.0, “txt”: “0:00:05’00” }.

· CatDV.getJSTimecode(timecode) – converts a native CatDV timecode (as returned by methods such as clip.get(“in”) into a JavaScript object with properties: txt, fmt, frm and secs.

Markers

You can access the markers for a clip using the clip.markers field, which returns an array of marker objects. The marker object has built-in fields 'name', 'in', 'out', 'description' and 'category'. You can also access custom marker fields using ‘fields, eg.

clip.markers[1].fields[‘my.custom.field’] = …

The marker object is a copy of the marker data in the clip, so after making any changes to markers you have to call explicit methods to persist the changes to the clip:

· clip.deleteMarker(marker) – remove a marker from the clip

· clip.saveMarker(marker) – save changes back to the clip after modifying any fields of a marker, e.g.

  clip.markers[0].name = “Modified”;
  clip.saveMarker(markers[0]);  // or change isn’t saved

· clip.addMarker(markerFields) – define a new marker by passing in an object with 'timecode' or 'in' as a minimum, plus optional 'out', 'name', 'category' and 'description' fields. 


See also the catdv_onMarkerCreated() and catdv_onMarkerSelected() UI handlers.

Performing queries and creating new clips

These methods provide access to clips other than the current selection and allow creation of new clips and catalog. Note that after creating a new clip you will need to add it to a catalog yourself (see ‘Catalog object’ below).

· CatDV.findClips(queryText) – perform a REST API-like query and return an array of clip objects (not simply JSON objects). You can also pass an XML query string instead if preferred.

· CatDV.countClips(queryText) – return a count of matching clips without fetching back all the clip data

· CatDV.performQuery(queryText) – perform a REST API-like query (or an XML query) and return a ‘catalog’ or ‘result set’ object (see below). For details of the query language see http://www.squarebox.com/server7-clip-query-filter-syntax/

· CatDV.performColumnQuery(field, queryText) – perform a query and return an array of strings (or other objects) for the chosen field only. If you don’t need all the clip fields then this is more efficient that returning a lot of metadata you don’t need. See also getDistinctValues().

· CatDV.importClip(filePath) – perform a basic import the file and always return a single clip representing the file. If the file can't be imported as a media file a generic clip is created instead.

You can attach media to a clip that didn’t previously have media using clip.media = CatDV.importClip(filePath).media.

· CatDV.importClips(filePath, options) – perform a full import the file and return a list of clips (e.g. if it is an XML or batch file or if scene detection is in use more than one clip may be returned). If it is part of a complex clip then a metaclip may be returned. For example

  var clip = CatDV.importClips("~/file.mts",
    { importer: "MPEG", "thumbnail.mode": 0 }) [0];

· CatDV.newCatalog() – create a new empty in-memory catalog

· CatDV.findCatalogs(queryText) – perform a catalog query and return an array of “light” SCatalog objects (with members ID, name, etc.)

· CatDV.getCatalogs() – return all the catalogs on the server (as an array of SCatalog objects)

· CatDV.getReadableCatalogs() – return all the accessible catalogs (as an array of SCatalog objects)

· CatDV.openCatalog(id) – open a catalog from the server based on its id and return a full catalog object

· CatDV.deleteCatalog(id) – delete a catalog from the server by its id

· CatDV.newSequence(name, start) – create a new empty sequence with the specified start timecode

· CatDV.createMetaClip(name, type) – create a new empty metaclip of a particular type

· clip.duplicate() – create a copy of an existing clip that you can add to another catalog

Catalog object

The Catalog object gives access to the catalog or underlying clip list for the current window that contains clips.

There are different sorts of ‘catalog’, including a locally saved .cdv catalog file, a catalog that has been published to the server, an unsaved in-memory catalog containing temporary clips, and a result set after querying the server for matching clips. The contents of a clip list, which might contain clips from different catalogs on the server, are also presented in a ‘catalog’ object.

You obtain a catalog object in various ways:

· clip.getCatalogObject() – returns the catalog object or result set that this clip is part of

· CatDV.performQuery(), CatDV.openCatalog(), CatDV.newCatalog() return or create catalog objects, as does CatDV.openClipList().

The following methods are available on catalog objects, though some of these methods are only relevant to certain types of catalog. For example you can only call publishChanges() on a server catalog or query result set or clip list, not a local or temporary in-memory catalog.

· getName() – get the name of the catalog

· getComment() – get the notes field of the catalog

· setComment(String comment) – set the catalog notes

· isOnServer() – return true if this is a server catalog or server query results

· getSCatalog() – return the underlying server object if this is the result of opening a complete catalog from the server

· getFilePath() – return the path if this is a .cdv catalog file or null otherwise

· getClips() – return an array of all the clips in the catalog or query result set. Note that changes made to this array are not automatically persisted to the catalog, for that you need to call add() or remove().

· getNumClips() – return a count of clips in the catalog or result set

· getClip(index) – return a clip by its index in the clip list

· getField(id) – get the value of a custom catalog field

· setField(id, value) – set a custom catalog field

· add(clip/clips) – append a single clip or a list of clips to the catalog

· remove(clip/clips) – delete one or more clips from the catalog

· saveToFile(filePath) – save the catalog or query results to a file

· publishChanges() – publish changes to the catalog to the server (and return a string summarising the operation)

· publish(catalogName) – publish to server as a new catalog and return an SCatalog

Manipulating metaclips

You can access and manipulate the contents of a metaclip in various ways:

· meta.contents() – if the clip is a metaclip (or a summary clip) this will give you the constituent clips

· meta.add(clips, position) – add a clip or list of clips to the metaclip. Use position -1 to append the clips at the end of the metaclip.

· meta.remove(clips, detach) – remove a clip or list of clips from the metaclip (pass in null to delete the entire contents). If detach is false the clips are deleted, if true they are removed from the metaclip but added to the catalog as top level clips instead.

Sequence object

The sequence object extends the clip object and allows a sequence to be edited. You can create a sequence with CatDV.newSequence() and test whether an existing clip is a sequence or not by calling getNumTracks(), see below.

· getNumTracks() – return the number of tracks the sequence has. If this is called on a clip which isn’t a sequence -1 is returned. A valid track index from 0 to getNumTracks()-1 must be passed to most of the functions below.

· getTrackType(track) – return the type of the track, 1=video, 2=audio, 3=both

· setTrackType(track, type) – change the type of the track

· addTrack(type) – add a new track of the specified type

· removeTrack(track) – remove the track with that index

· getNumClips(track) – get the number of items in the specified track

· getSourceClip(track, index) – get the source clip for an item in a track

· getSourceIn(track, index) – get the start timecode within the source clip that this sequence is refererencing

· getSourceOut(track, index) – get the end timecode within the source clip that this sequence is refererencing

· getIn(track, index) – get the start timecode of an item within the sequence

· getOut(track, index) – get the end timecode of an item within the sequence

· addClip(track, clip, srcIn, srcOut) – add a reference to part of a source clip at the end of the sequence track

· addClip(track, clip, srcIn, srcOut, destIn, destOut) – add a reference to a source clip at a particular point in the sequence. Take care not to overlap existing clips.

· removeClip(track, index, shiftUp) – remove an item from the sequence. Pass true or false depending on whether to shift subsequent clips up or not.

· adjustPosition(track, index, offset) – shift an item in the sequence. Take care not to overlap existing clips or the sequence will become corrupted!

Manipulating clip lists

These methods provide access to clip lists stored on the server:

· CatDV.getClipLists() – return a list of all the clip lists as “light” SFolder objects

· CatDV.findClipList(name) – return the id of a clip list given its name (or -1 if not found)

· CatDV.createClipList(name) – create a clip list (by default shared with everyone in the current group) and return its id

· CatDV.getClipList(id) – get a clip list by its id as an SFolder object

· CatDV.saveClipList(sfolder) – save changes to the name or ownership of a clip list

· CatDV.deleteClipList(id) – delete a clip list by id

· CatDV.openClipList(id) – open a clip list and the clips within it and return it as a catalog object on which you can call methods like add, remove, getClips, and publishChanges to edit the clip list.

Server objects

Some methods like CatDV.findCatalogs(), CatDV.getCatalogs(), CatDV.getReadableCatalogs(), CatDV.getClipLists(), CatDV.findClipList(), and CatDV.getMediaStores() return “lightweight” server data transfer objects like SCatalog, SFolder, SClip, SImportSource etc. You can also access server objects via the clip.sclip, clip.smedia, clip.scatalog, and clip.ssource fields.

These server objects all have an ‘ID’ field, which gives the server database id of that object, a toString() method, and other fields like ‘name’ etc. that depend on the specific object.

Server objects are just data structures that correspond to rows in the database and don’t have any methods. So while an SCatalog object gives access to basic details about a catalog on the server, it’s only once you open the catalog up that you get a full catalog object containing the clips, with methods like publishChanges() etc.

Confusingly, if you use the REST API then you will end up with yet another representation of clips, catalogs etc., namely as a JSON object. These are broadly similar to server objects, in that they don’t have methods, but store their data as a map rather than a structure.

Byte arrays

CatDV.readFile(), CatDV.httpRequest() and clip.poster all return binary data as a byte array, with the following fields and methods:

· length – return the number of bytes

· toString() – interpret the data as a UTF-8 string

· getByte(int pos) – read the byte at the given position

· getInt2(int pos) – read a 2-byte integer (big endian)

· getInt4(int pos) – read a 4 byte integer (big endian)

· slice(int start, int len) – return a subarray

· isEmpty() – return whether the array has a non-zero length

The converse methods, eg. writing data to a file with CatDV.writeFile() or passing ‘postData’ to CatDV.httpRequest(), all accept either an existing byte array or data passed as a UTF8 string so there is usually no need to explicitly create a byte array but if required you can create an empty byte array of a given size by writing

var ByteArray = Java.type(
  "squarebox.common.scripting.ByteArray" );
var a = new ByteArray(16);

To create a ByteArray containing arbitrary binary data you can use CatDV.base64UrlDecode() to create one from a base64 encoded string.

You can mutate a byte array using:

· append(ByteArray other)

· setLength(int len)

· setByte(int pos, int byte)

Timestamps

clip.modifiedDate, clip.recordedDate, and clip.importSource.importedDate return a timestamp object, which has a number of useful methods including:

· toString() – format the date using default format

· toISOString() – return an ISO-style string

· toString(“YYYY-MM-DD”) – format times using the specified format string (see online documentation for java.util.SimpleDateFormat for details).

· toYMDHMString(), toYMDHMSString(), toShortString(), toHMSString() – other formats

· getComponents() – return an array of 6 integers with yr,mth,day,hr,min,sec

· toNow() – how many seconds ago that timestamp is

· getSeconds() – Unix time (number of seconds since Jan 1, 1970)

· addSeconds(), diffInSeconds(), diffInDays(), compareTo() – other date arithmetic methods

Media functions (scene detection, image extraction, and transcoding)

You can perform programmatic scene detection:

· CatDV.detectScenes(movie, threshold, advanced, minLength) – perform scene detection on a movie (specified by a clip or a filename) and return an array of times (seconds from the start of the movie). The ‘threshold’ is typically in the range 25 (more sensitive) to 150 (less sensitive). ‘advanced’ is true or false depending on which algorithm to use, while ‘minLength’ is the minimum gap in seconds between scene boundaries if the advanced algorithm is being used.

You can get a java image from a file and then scale and compress it to JPEG encoded bytes, or use it in a custom rendering function. You can also set the poster thumbnail for a clip by assigning clip.poster to an image or byte array (or calling clip.setPoster()).

· CatDV.getImage(src, time) – return a java image from the specified time within a movie. The source parameter can be a clip or a file name, and the time can be a timecode value or a number of seconds from the start of the clip or file. The source parameter can also be an image decoder, see below.

· CatDV.getImageDecoder(src) – if you are going to read many images from the same file it is more efficient to open up the movie just once and keep it open as an image decoder

· CatDV.dispose(imageDecoder) – once you have finished you should close the movie

· CatDV.encodeJPEG(image, maxWidth, maxHeight, orientation, quality) – encode an image as JPEG bytes and return a byte array object. Orientation is one of 0, 90, 180, 270 and quality is from 0 (highest compression) to 1 (highest quality).

This method enables a basic transcode to be performed:

· CatDV.transcode(clip, destFile, settings) – transcode a clip to a destination file path using xml transcode settings. As some exporters (eg. the multitrack audio exporter) can create multiple output files a list of filenames is returns.

These functions support rendering out a movie based on JavaScript, for example with programmatically calculated transitions, custom title slates, etc.

· CatDV.transcode2(destFile, settings, src) – transcode a clip using the new segment based exporter. Settings are provided as a map.

· CatDV.transcode2(destFile, settings, videoSegments, audioSegments) – transcode a movie defined by a list of video and audio segments. These segments can be obtained using one of the functions below, or defined programmatically.

· CatDV.deconstructVideo(clip, showMarkers, markerCategory, includeDescriptions, markerDuration) – return a list of video segments to render out a clip (including metaclips and sequences) by creating a separate segment for each file, and also by looking at markers and creating a separate segment depending on the marker text being shown

· CatDV.deconstructAudio(clip) – return a list of audio segments to render out a clip or (sequence or metaclip)

The transcode() method uses xml exporter settings such as

<exportSettings format="ffmpeg" container="mp4" vcodec="libx264" acodec="aac" scaleMode="4" maxWidth="480" maxHeight="270" deinterlace="true" textPos="5" textColour="2" fontSize="18" visibleTimecode="true" bitcPos="2" bitcColour="7" bitcSize="30" quality="32" videoBitRate="0" videoFrameRate="0" audioBitRate="0" audioSampleRate="0" audioTrack="0" audioChans="0" options="-preset slow -movflags +faststart" even="true" smartStereo="true" />

The transcode2() method simplifies this by accepting a javascript object, eg.

{ container: ‘mp4’, vcodec: ‘libx264’, width: 1024, height: 720 }

Video and audio segments are specified as an array of objects, eg.

[ { text: ‘My title’, duration: 2.0}, { src: clip, in: clip.in2, out: clip.out2 }, { file: ‘input.avi’, start: 0.5, duration: 5.0 } ]

or

[ { duration: 2.0 }, { frequency: 1000, duration: 0.1 }, { file: ‘music.mp3’ } ]

Video segments can be defined by ‘src’ (a source clip), ‘file’ (a file path), ‘text’ (a title page), or be left blank (for a black filler). Other than for source clips the duration must be specified. Similarly for audio segments.

Video segments can also be defined completely programmatically by executing the script for each frame and using java AWT methods:

[ { duration: 5.0, script: function(g, i, numFrames, frameRate, width, height) {
  var image = CatDV.getImage(source, i/frameRate);
  g.drawImage(image, 0, 0, null);
  g.setColor(java.awt.Color.RED);
  g.drawString(“Time = “+i/frameRate, width/2, height/2);
}} ] 

Availability of features

Not all features are relevant in every situation where JavaScript is available. For example, some relate to the interactive user interface and are only relevant in the desktop and web, while some are provided in both synchronous and asynchronous versions (as JavaScript in web browsers is single threaded and enforces the use of callbacks rather than blocking calls).

As far as possible the desktop and web interface provide the same UI script capabilities as the same script is run in both contexts, though there are some minor differences for implementation reasons.

Function or capability

Desktop UI Script

Web UI Script

Desktop custom action

Worker script/ plugin

catdv_onAction()

Y

Y

-

-

catdv_onCreateField()

Y

Y

-

-

catdv_onCreateMarker()

Y

N

-

-

catdv_onDownload()

-

Y

-

-

catdv_onImport()

Y

-

-

-

catdv_onLoad()

Y

Y

-

-

catdv_onMarkerSelected()

Y

N

-

-

catdv_onUpdate()

Y

Y

-

-

catdv_onValidate()

Y

N

-

-

CatDV.appendFile()

--

-

Y

Y

CatDV.askOKCancel()

Y

-

Y

-

CatDV.askOKCancelAsync()

Y

Y

Y

-

CatDV.askOptions()

Y

-

Y

-

CatDV.askOptionsAsync()

Y

Y

Y

-

CatDV.askQuestion()

Y

-

Y

-

CatDV.askQuestionAsync()

Y

Y

Y

-

CatDV.callExtension()

-

-

-

Y

CatDV.callProcessingStep()

-

-

-

Y

CatDV.callRESTAPI()

--

-

Y

Y

CatDV.callRESTAPIAsync()

Y

Y

Y

-

CatDV.chooseFile()

Y

-

Y

-

CatDV.copyFile()

--

-

Y

Y

CatDV.copyRemote()

-

-

-

Y

CatDV.createClipList ()

Y

N

Y

Y

CatDV.createFolder()

Y

-

Y

Y

CatDV.createMetaClip()

Y

-

Y

Y

CatDV.deleteCatalog()

Y

N

Y

Y

CatDV.deleteClipList()

Y

N

Y

Y

CatDV.deleteFile()

--

-

Y

Y

CatDV.detectScenes()

--

-

Y

Y

CatDV.encryptPassword()

Y

-

Y

Y

CatDV.execJava()

-

-

N

Y

CatDV.execute()

--

-

Y

Y

CatDV.expandVariables()

Y

N

Y

Y

CatDV.failAndRetry()

-

-

-

Y

CatDV.fileExists()

--

-

Y

Y

CatDV.findCatalogs()

Y

N

Y

Y

CatDV.findClipList ()

Y

N

Y

Y

CatDV.findClips()

--

-

Y

Y

CatDV.get()

Y

N

Y

Y

CatDV.getAllFiles()

Y

N

Y

Y

CatDV.getAllGroups()

Y

N

Y

Y

CatDV.getAllMediaFiles()

Y

N

Y

Y

CatDV.getAllUsers()

Y

N

Y

Y

CatDV.getCatalogs()

Y

N

Y

Y

CatDV.getClip()

Y

Y

Y

-

CatDV.getClipList()

Y

N

Y

Y

CatDV.getClipLists ()

Y

N

Y

Y

CatDV.getDistinctColumns()

Y

-

Y

Y

CatDV.getFileInfo()

Y

-

Y

Y

CatDV.getImage()

--

-

Y

Y

CatDV.getMediaStores()

Y

N

Y

Y

CatDV.getMovieTime()

Y

N

Y

-

CatDV.getMyGroups()

Y

N

Y

Y

CatDV.getPersistentProperty()

-

-

-

Y

CatDV.getPicklistValues()

Y

-

Y

Y

CatDV.getPicklistValuesAsync()

Y

Y

Y

-

CatDV.getPlaybackRate()

Y

N

Y

-

CatDV.getPreferenceItem()

Y

-

Y

Y

CatDV.getProperty()

Y

Y

Y

Y

CatDV.getReadableCatalogs ()

Y

N

Y

Y

CatDV.getRemoteVolumeDetails()

-

-

-

Y

CatDV.getRemoteFileInfo()

-

-

-

Y

CatDV.getScriptFile()

-

-

-

Y

CatDV.getSoftwareVersion()

Y

Y

Y

Y

CatDV.getUserName()

Y

Y

Y

Y

CatDV.httpRequest()

--

-

Y

Y

CatDV.httpRequestAsync()

Y

-

Y

-

CatDV.importClip()

--

-

Y

Y

CatDV.importClips()

--

-

Y

Y

CatDV.isDesktopClient()

Y

Y

Y

Y

CatDV.isWebClient()

Y

Y

Y

Y

CatDV.isWorkerNode()

Y

Y

Y

Y

CatDV.listFiles()

--

-

Y

Y

CatDV.log()

Y

Y

Y

Y

CatDV.moveFile()

--

-

Y

Y

CatDV.newCatalog()

--

-

Y

Y

CatDV.openCatalog()

Y

N

Y

Y

CatDV.openClip()

Y

N

Y

-

CatDV.openClipList()

Y

N

Y

Y

CatDV.openTab()

Y

-

Y

-

CatDV.parse()

Y

-

Y

Y

CatDV.performColumnQuery()

--

-

Y

Y

CatDV.performMenuCommand()

--

-

Y

-

CatDV.performQuery()

--

-

Y

Y

CatDV.put()

Y

N

Y

Y

CatDV.readChunk()

--

-

Y

Y

CatDV.readFile()

--

-

Y

Y

CatDV.requires()

--

-

Y

Y

CatDV.resyncWithServer()

-

-

-

Y

CatDV.saveClipList()

Y

N

Y

Y

CatDV.saveMarker()

Y

N

Y

-

CatDV.selectClip()

Y

-

Y

-

CatDV.sendEmail()

-

-

-

Y

CatDV.setFieldOptions()

Y

N

Y

-

CatDV.setMovieTime()

Y

N

Y

-

CatDV.setPersistentProperty()

-

-

-

Y

CatDV.setPicklistValues()

Y

Y

Y

-

CatDV.setPlaybackRate()

Y

N

Y

-

CatDV.setPreferenceItem()

Y

-

Y

Y

CatDV.setProgress()

--

-

Y

Y

CatDV.setRESTAPIVersion()

--

-

Y

Y

CatDV.setTabVisible()

Y

Y

Y

-

CatDV.showMessage()

Y

Y

Y

-

CatDV.stringify()

Y

-

Y

Y

CatDV.submit()

-

-

-

Y

CatDV.transcode()

--

-

Y

Y

CatDV.transcode2()

--

-

Y

Y

CatDV.writeFile()

--

-

Y

Y

clip.get()

Y

Y

Y

Y

clip.poster

--

-

Y

Y

clip.set()

Y

Y

Y

Y

-- indicates the feature is provided on the client for use in custom actions only. Although it may be present it shouldn't be used in UI scripts and may give unpredictable results there (eg. causing the user interface to lock up).

- means the feature is not relevant in that context and is unlikely ever to be implemented.

N indicates the feature is not currently implemented but might be added in future if there is demand for the feature.

Points to note

JavaScript within a web browser is single threaded and system calls are not allowed to block, so only asynchronous calls like callRestAPIAsync() that provide the results via a callback function can be used from there.

Within the desktop client you can make both synchronous and asynchronous calls but should avoid making any synchronous calls from UI scripts, both because they can cause the UI to lock up and because they will prevent the script running in the web interface.

Many of these methods can throw exceptions, eg. CatDV.execute() if the command doesn’t exist, catalog.publishChanges() if there’s a server error, etc.

If the JavaScript code doesn’t catch these exceptions, or throws an exception of its own, then they are handled as follows on the desktop client:

A UI script that throws an exception will display an error message then disable the offending handler (eg. catdv_onUpdate) to prevent runaway script problems. The handler is disabled until the script is next reloaded (eg. by logging off and on again).

A worker processing step or extension or a custom action processing step will cause that action to terminate with an error, just as it would for any other kind of failure.

System requirements

JavaScript support within the CatDV desktop application and worker node is provided by Java’s “Nashorn” JavaScript interpreter which requires Java 8 or later and implements ECMAScript 5.1.

JavaScript within the web interface is executed within the web browser, and so the exact version of JavaScript provided depends on the web browser being used.

To use the features as described in this document you need CatDV 12.1, Worker 7.0, and Server 7.2.0 (or later). The worker and desktop versions that a feature first appeared in are listed below.

Change history

9 Dec 2021 (14.0.2)

· Add ‘redraw’ to setFieldOptions()

· Allow timecode to be specified by a Javascript object with frm & fmt fields.

27 July 2021 (9.0a13)

· Add setPersistentProperty() and getPersistentProperty()

23 June 2021 (9.0a12)

· Add copyRemote() and getRemoteFileInfo()

· Add resyncWithServer()

16 Feb 2021 (14.0a8)

· Add openTab()

23 Jan 2021 (9.0a8, 14.0a7)

· Add getAllFiles()

· Add getAllMediaFiles()

· Add createMetaClip()

· Add add() and remove() methods to edit the contents of a metaclip

· Make media.start editable (to apply a timecode offset to the whole clip)

· Make the clip.owner field editable

1 Oct 2020 (8.1.6, 13.0.13)

· Add lastModified to getFileInfo()

· Allow setting fields like media.filePath even if they’re not editable via UI

13 Aug 2020 (8.1.5, 13.0.12)

· Add sendEmail()

· Add documentation on ‘Worker JavaScript triggers’ (originally implemented in 8.1.2), plus new onNotification() handler

· Add calculated pick lists

1 July 2020 (8.1.3, 13.0.12)

· Add submit() to programmatically submit worker jobs

· Add getRemoteVolumeDetails()

· Fix an issue with parse() and stringify() in the desktop

· Change transcode() so it returns a list of output files

· Add support for calculated pick lists

· Minor documentation fixes

1 May 2020 (8.1.1, 13.0.11)

· Add getPreferenceItem() and setPreferenceItem()

· Add dynamicUI to worker extensions

25 Mar 2020 (8.0.9)

· Add failAndRetry() to tell worker to re-queue a task

· Add callExtension() to tell worker to programmatically invoke a worker extension

14 Feb 2020 (8.0.9, 13.0.9)

· Fix JSON.parse/stringify

· Add getJSTimecode() to convert timecodes to JSON objects

· Add getFileInfo(), createFolder(), and setRESTAPIVersion() functions

9 Jan 2020 (8.0.9, 13.0.8)

· Make clip.in2 and clip.out2 writable

· Clarify how to create an empty ByteArray

2 Dec 2019 (8.0.8, 13.0.7)

· Implement CatDV.readChunk() to read part of a file

· Add ByteArray.isEmpty().

· Allow setting clip.media

2 Oct 2019 (8.0.6, 13.0.6)

· Expose CatDV.parse/stringify as JSON.parse/stringify as well.

23 Apr 2019 (8.0.1)

· Add execJava().

· Clarify that legacy user-defined clip field identifiers like clip[‘my.user.field’] no longer work and should be written as clip.fields[‘my.user.field’], and also that worker plugins shouldn’t include version number in the file name.

25 Feb 2019 (13.0b16)

· Add ‘interim’ parameter to catdv_onUpdate()

1 Feb 2019 (13.0b15, 8.0b7)

· Add getNumClips() and getClip() to catalog object.

· Return empty result set if performQuery() finds no clips rather than an error.

· Give clips and catalogs a more useful toString() representation.

26 Nov 2018 (13.0a13)

· Add getUserRole()

30 Oct 2018 (12.1.10, 13.0a11)

· Add duplicate() method to clip

22 Sep 2018 (13.0a7)

· Add catdv_onNotification() handler. Add arg and callback to performMenuCommand().

23 July 2018 (7.0.8, 12.1.8)

· Add getDistinctValues() and performColumnQuery()

· Add chooseFile()

· Change selectClip() to return true for success (if the clip is found) and add openClip()

23 June 2018 (7.0.6, 12.1.7)

· Add access to server objects like clip.sclip, clip.scatalog

· Add clip.ID to give you the server clip id as an integer (unlike clip.id which is a string like “12345.1” and includes the sequence number)

· Add appendFile() and setProgress().

· Restructure documentation into topics and add new or updated sections on markers, timestamp functions, the catalog object, server objects, byte arrays, etc.

24 May 2018 (7.0.4, 12.1.6)

· Add detectScenes() and catdv_onCreateMarker().

· Provide access to custom marker fields

17 May 2018 (7.0.3)

· Add support for versioning of worker plugins

13 April 2018 (7.0.1, 12.1.5)

· Add sleep(),signSHA256RSA(),base64UrlEncode(),base64UrlDecode() and encodeJPEG() methods. Fix httpRequest() to return server error response.

8 March 2018 (7.0rc3, 12.1.5)

· Add Timestamp.toString(“YYYY-MM-DD”)

7 February 2018 (7.0rc1)

· Add onLoad() and onValidate() to worker plugins

29 January 2018 (12.1.3, 7.3x)

· Implement askOKCancelAsync, askOptionAsync, askQuestionAsync

25 January 2018 (12.1.2, 7.0b21)

· Implement ${importSource[Xyz]}, ${catalog[Abc]}, etc.

· Add documentation on catdv_onDownload and on variable expressions

1 December 2017 (12.1.0, 7.0b19)

· Add getTimecode(timecode, format)

· Add programmatic rendering methods

· Worker plugin enhancements, including additional field types and the ability to specify a licenseKey category.

23 November 2017 (12.1rc4)

· Fix timecodeDiff() and getTimecode()

· Improved sequence editing

· Add selectClip()

· Add executeWithEnvironment()

12 October 2017 (7.0b18, 12.1rc2)

· Add getCatalogs(), getReadableCatalogs(), getMediaStores(), and countClips()

· Add methods to open, create and edit clip lists

· Change extension for Worker Extensions to .jx or .jxe and add support for encrypted packages

9 October 2017 (12.1rc1)

· Allow ‘CatDV’ to be used as a global map variable to store values and functions between invocations.

· Provide access to ‘CatDV’ object when evaluating a js: variable expression.

3 October 2017 (7.0a17)

· Change worker javascript processing step so it doesn’t pre-expand variable expressions like $x that occur within the text of the script inline. Instead, provide functions expand() and CatDV.expandVariables().

22 August 2017 (12.1b10)

· Change readFile(), httpRequest(), executeWithStatus() to return data as a byte array rather than a string

· Add methods to get and set clip poster thumbnails

26 July 2017 (12.1b9, 7.0a14)

· Change unadorned method names like getPicklistValues() etc. so they are synchronous by default, and add corresponding xxxAsync() methods when the version taking a callback is needed

July 2017 (12.1b8, 7.0a13)

· Use global CatDV object instead of context variable passed to the catdv_onXXX methods

· Remove CatDV.saveMarker() and add methods to clip instead

· Change callRESTAPI so that it works with objects not strings (so JSON.stringify and JSON.parse are called for you)

· Deprecate clip['my.user.1'], clip.media['FNumber'] and clip.catalog ['my.catalog.field'] and use 'fields' instead when referring to fields on any of these objects, eg. clip.fields['my.user.1']

Mar 2017

· Switch to JDK 1.8 and Nashorn interpreter. Major changes to API.

Feb 2015

· Initial internal release with support for customising picklist values only