Upgrading v1 -> v2
Please note that the event based API has changed. In version 1, you can subscribe to plugin events with pluginInstance.on()
. In version 2, the same functionality is available with pluginInstance.registerListener()
. The following docs are for the latest version.
About
Background
Collaboration between videographers and clients can be tedious, with emails and phone calls that waste time trying to reference specific frames and areas of the screen. This plugin enables more efficient collaboration from the browser.
This plugin was conceived and developed as a Hack Week project at Contently by Evan Carothers and Jack Pope. Continuing our focus and commitment to multimedia support at Contently, the entire team productized and bulletproofed the plugin as a flexible solution to be used in our product and other open-source use cases.
Goals
- Efficient for videographers and clients alike - Provides useful collaboration features including annotations, comments/replies, ranged time markers, and more, with intuitive controls.
- SIMPLE & LIGHTWEIGHT - Everything is contained within the plugin and player element. There is no need to build additional UI components. Just install VideoJS, register the plugin, setup whatever backend storage you wish, and start collaborating.
- EXTENSIBLE - The plugin can be integrated with existing commenting systems (as we did within Contently), and makes very few assumptions about how to store annotations. Custom events are available for communicating with external APIs, providing support for on-page interactions and data persistence. Simple CSS overrides can also allow for branding customizations with minimal effort, or completely custom UI/UX.
VideoJS Plugins
VideoJS is a popular open-source HTML5 video player library used by 400k+ sites. As of v6, there is an extendable plugin architecture which was used to create this plugin. This plugin is built and tested against VideoJS v7
Use it!
Install
yarn add @contently/videojs-annotation-comments
OR
npm install @contently/videojs-annotation-comments
Add it to your VideoJS player
As a script from build
var player = videojs('video-id');
var plugin = player.annotationComments(pluginOptions)
As a module
import videojs from 'video.js'
import AnnotationComments from '@contently/videojs-annotation-comments'
videojs.registerPlugin('annotationComments', AnnotationComments(videojs))
var player = videojs('video-id')
var plugin = player.annotationComments(pluginOptions)
Plugin options / configuration
When initializing the plugin, you can pass in an options array to override default options. Any excluded options are set to their default values, listed below:
const pluginOptions = {
annotationsObjects: [],
meta: { user_id: null, user_name: null },
bindArrowKeys: true,
showControls: true,
showCommentList: true,
showFullScreen: true,
showMarkerShapeAndTooltips: true,
internalCommenting: true,
startInAnnotationMode: false
};
Annotation Data Structure
To initialize the plugin with the annotationsObjects
collection, use the following structure:
const annotationsObjects = [{
id: 1,
range: {
start: 10,
end: 15
},
shape: {
x1: 23.47,
y1: 9.88,
x2: 60.83,
y2: 44.2
},
comments: [{
id: 1,
meta: {
datetime: '2017-03-28T19:17:32.238Z',
user_id: 1,
user_name: 'Jack Pope'
},
body: 'The first comment!'
}]
}];
Programmatic Control
If you'd like to drive the plugin or render plugin data through external UI elements, you can configure the plugin to hide the internal components and pass data through custom events. There are two kinds of AnnotationComments API events, externally fired and internally fired.
Waiting for Plugin Ready
Before triggering any events on the plugin, you must wait for it to be ready. You can use the onReady
function on the plugin:
plugin.registerListenerReady(() => {
});
Supported Externally Fired Events:
These events are external actions that can be called from your scripts to trigger events within the plugin:
plugin.fire('openAnnotation', { id: myAnnotationId });
plugin.fire('closeActiveAnnotation');
plugin.fire('newAnnotation', {
id: 1,
range: { start: 20, end: null },
shape: {
x1: null,
x2: null,
y1: null,
y2: null
},
commentStr: "This is my comment."
});
plugin.fire('destroyAnnotation', { id: 1 });
plugin.fire('newComment', { annotationId: 1, body: "My comment string" });
plugin.fire('destroyComment', { id: 1 });
plugin.fire('addingAnnotation');
plugin.fire('cancelAddingAnnotation');
plugin.fire('toggleAnnotations');
Supported Internally Fired Events:
These are events that are triggered from within the running plugin and can be listened for by binding to plugin.registerListener
within your scripts:
plugin.registerListener('annotationOpened', (event) => {
});
plugin.registerListener('annotationClosed', (event) => {
});
plugin.registerListener('addingAnnotationDataChanged', (event) => {
var newRange = event.detail.range;
var newShape = event.detail.shape;
});
plugin.registerListener('annotationDeleted', (event) => {
});
plugin.registerListener('enteredAddingAnnotation', (event) => {
var startTime = event.detail.range.start;
});
plugin.registerListener('onStateChanged', (event) => {
});
plugin.registerListener('playerBoundsChanged', (event) => {
var bounds = event.detail;
});
plugin.registerListener('annotationModeEnabled', (event) => {
});
plugin.registerListener('annotationModeDisabled', (event) => {
});
Develop and Build
We're using yarn for package management and gulp as our build system.
The fastest way to get started:
- Clone the repo
- Run
yarn install
- Run
yarn build
- Run
yarn watch
- Visit
http://localhost:3004/test.html
to see the magic happen.
Templates
We're using the Handlebars templating library to render various components within the plugin. For performance, the templates are pre-compiled into a JS file within the development environment. That way we only need to require the Handlebars runtime, saving nearly 100kb from the minified build! ⚡️
The gulp templates
task is used to precompile every template to /src/js/compiled/templates.js
. This file should not be modified directly, but rather the templates themselves in /src/templates
should be modified if changes are needed. The templates task will run automatically within gulp watch
.
UI / CSS Customization
The plugin uses SASS and all styles are defined in annotaitons.scss. There is extenssive commenting on classes and styles in the file. The plugin uses a deep level of specificity to prevent styles from polluting elements on the page, and all classes are prefixed with vac-
to prevent classname collisions in the global namespace.
You can extend/modify colors and elements quite easily by writing an overrides stylesheet to address the specific elements that you wish to modify. You can also change the variable colors in the stylesheet and compile yourself for more customization.
NOTE - our gulp build tasks use an auto-prefixer to make the styles work cross-browser, so be sure to run that yourself if you compile the SASS files with changes.
Testing
Feature tests
Feature tests are currently browser-based and run by visiting http://localhost:3004/mocha/features/index.html
. Feature tests can be added as files in the /test/mocha/features/
directory and then included within the index.html
file as a external scripts.
Unit tests
Unit tests are run through the gulp test
task. If the tdd
task is included in gulp watch
, the tests will run with every change to the test files. Each module should have a corresponding unit test file within the /test/mocha/modules
directory.
Gulp commands
gulp watch
: Fires up webserver @ http://localhost:3004/test.html
, watches for any file changes in /src
, including js, css (scss), and templates (.hbs), repackages, and transpiles to an unminified file in /build
on change.
gulp transpile
: Transpiles modules/files to build file in /build
with JS maps
gulp build
: Runs transpilation, browserify, sass, then minifies to distribution filename in /build
with attribution
gulp templates
: Uses Handlebars to pre-compile templates into a javascript file. See Templates section above.
gulp test
: Runs the mocha unit tests within the /test/mocha/modules/
directory.
gulp lint
: Runs jshint linter on javascript files in /src
License
This plugin is licensed under the Apache License, Version 2.0, which is the same license used by Video.js