{"id":17213408,"url":"https://github.com/trilogy-group/contently-videojs-annotation-comments","last_synced_at":"2025-10-12T07:30:17.324Z","repository":{"id":57103806,"uuid":"85613694","full_name":"trilogy-group/contently-videojs-annotation-comments","owner":"trilogy-group","description":"A plugin for video.js to add support for timeline moment/range comments and annotations","archived":false,"fork":false,"pushed_at":"2025-03-19T22:39:27.000Z","size":109259,"stargazers_count":177,"open_issues_count":20,"forks_count":50,"subscribers_count":28,"default_branch":"master","last_synced_at":"2025-10-02T18:43:53.676Z","etag":null,"topics":["annotations","product-contently","videojs","videojs-player","videojs-plugin"],"latest_commit_sha":null,"homepage":"https://contently.github.io/videojs-annotation-comments/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/trilogy-group.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-03-20T18:46:55.000Z","updated_at":"2025-06-28T18:52:55.000Z","dependencies_parsed_at":"2024-04-16T03:43:44.564Z","dependency_job_id":"86c50f80-777d-4563-8d0b-2a1cf539e1f4","html_url":"https://github.com/trilogy-group/contently-videojs-annotation-comments","commit_stats":null,"previous_names":["contently/videojs-annotation-comments"],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/trilogy-group/contently-videojs-annotation-comments","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trilogy-group%2Fcontently-videojs-annotation-comments","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trilogy-group%2Fcontently-videojs-annotation-comments/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trilogy-group%2Fcontently-videojs-annotation-comments/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trilogy-group%2Fcontently-videojs-annotation-comments/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/trilogy-group","download_url":"https://codeload.github.com/trilogy-group/contently-videojs-annotation-comments/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trilogy-group%2Fcontently-videojs-annotation-comments/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278477817,"owners_count":25993540,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-05T02:00:06.059Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["annotations","product-contently","videojs","videojs-player","videojs-plugin"],"created_at":"2024-10-15T03:01:09.910Z","updated_at":"2025-10-12T07:30:13.784Z","avatar_url":"https://github.com/trilogy-group.png","language":"JavaScript","funding_links":[],"categories":["HarmonyOS"],"sub_categories":["Windows Manager"],"readme":"[![CircleCI](https://circleci.com/gh/contently/videojs-annotation-comments/tree/master.svg?style=svg)](https://circleci.com/gh/contently/videojs-annotation-comments/tree/master)\n\n# AnnotationComments : Collaborate in your VideoJS player\n\n![AnnotationComments Screenshot1](test/screenshot.png)\n\n\u003e## Upgrading v1 -\u003e v2\n\u003ePlease 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.\n\n## About\n\n### Background\n\nCollaboration 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.\n\nThis plugin was conceived and developed as a Hack Week project at [Contently](http://www.contently.com) by [Evan Carothers](http://www.github.com/ecaroth) and [Jack Pope](http://www.github.com/jackpope). 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.\n\n ### Goals\n\n- **Efficient for videographers and clients alike** - Provides useful collaboration features including annotations, comments/replies, ranged time markers, and more, with intuitive controls.\n- **SIMPLE \u0026 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.\n- **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.\n\n### VideoJS Plugins\n\n[VideoJS](http://videojs.com/) 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](https://www.npmjs.com/package/video.js/)\n\n## Use it!\n\n### Install\n\n```\nyarn add @contently/videojs-annotation-comments\n```\n\nOR\n\n```\nnpm install @contently/videojs-annotation-comments\n```\n### Add it to your VideoJS player\n\n#### As a script from build\n\n```javascript\n// ...videojs \u0026 videojs-annotation-comments have been loaded in script tags...\nvar player = videojs('video-id');\nvar plugin = player.annotationComments(pluginOptions)\n```\n\n#### As a module\n\n```javascript\nimport videojs from 'video.js'\nimport AnnotationComments from '@contently/videojs-annotation-comments'\n videojs.registerPlugin('annotationComments', AnnotationComments(videojs))\n var player = videojs('video-id')\nvar plugin = player.annotationComments(pluginOptions)\n```\n\n### Plugin options / configuration\n\nWhen 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:\n\n```javascript\nconst pluginOptions = {\n    // Collection of annotation data to initialize\n    annotationsObjects: [],\n    // Flexible meta data object (currently used for user data, but addl data can be provided to wrap each comment with metadata - provide the id of the current user and fullname of the current user at minimum, which are required for the UI)\n    meta: { user_id: null, user_name: null },\n    // Use arrow keys to move through annotations when Annotation mode is active\n    bindArrowKeys: true,\n    // Show or hide the control panel and annotation toggle button (NOTE - if controls are hidden you must provide custom UI and events to drive the annotations - more on that in \"Programmatic Control\" below)\n    showControls: true,\n    // Show or hide the comment list when an annotation is active. If false, the text 'Click and drag to select', will follow the cursor during annotation mode\n    showCommentList: true,\n    // If false, annotations mode will be disabled in fullscreen\n    showFullScreen: true,\n    // Show or hide the tooltips with comment preview, and annotation shape, on marker hover or timeline activate\n    showMarkerShapeAndTooltips: true,\n    // If false, step two of adding annotations (writing and saving the comment) will be disabled\n    internalCommenting: true,\n    // If true, toggle the player to annotation mode immediately after init. (NOTE - \"annotationModeEnabled\" event is not fired for this initial state)\n    startInAnnotationMode: false\n};\n```\n\n### Annotation Data Structure\n\nTo initialize the plugin with the `annotationsObjects` collection, use the following structure:\n```javascript\nconst annotationsObjects = [{\n    id: 1,\n    range: {\n        start: 10,\n        end: 15\n    },\n    shape: {\n        x1: 23.47,\n        y1: 9.88,\n        x2: 60.83,\n        y2: 44.2\n    },\n    comments: [{\n        id: 1,\n        meta: {\n            datetime: '2017-03-28T19:17:32.238Z',\n            user_id: 1,\n            user_name: 'Jack Pope'\n        },\n        body: 'The first comment!'\n    }]\n}];\n```\n\n### Programmatic Control\n\nIf 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_.\n\n#### Waiting for Plugin Ready\n\nBefore triggering any events on the plugin, you must wait for it to be ready. You can use the `onReady` function on the plugin:\n\n```javascript\nplugin.onReady(() =\u003e {\n    // do stuff with the plugin, such as fire events or setup listeners\n});\n```\n\n#### Supported Externally Fired Events:\n\n These events are external actions that can be called from your scripts to trigger events within the plugin:\n\n```javascript\n// openAnnotation : Opens an annotation within the player given an ID\nplugin.fire('openAnnotation', { id: myAnnotationId });\n // closeActiveAnnotation : Closes any active annotation\nplugin.fire('closeActiveAnnotation');\n // newAnnotation : Adds a new annotation within the player and opens it given comment data\nplugin.fire('newAnnotation', {\n    id: 1,\n    range: { start: 20, end: null },\n    shape: { // NOTE - x/y vals are % based (Floats) in video, not pixel values\n        x1: null,\n        x2: null,\n        y1: null,\n        y2: null\n    },\n    commentStr: \"This is my comment.\"\n});\n // destroyAnnotation : Removes an annotation and it's marker within the player given comment data\nplugin.fire('destroyAnnotation', { id: 1 });\n // newComment : Adds a new comment to an Annotation given an Annotation ID and a body\nplugin.fire('newComment', { annotationId: 1, body: \"My comment string\" });\n // destroyComment : Removes a comment from an Annotation given a Comment ID\nplugin.fire('destroyComment', { id: 1 });\n // addingAnnotation : Plugin enters the adding annotation state (adding an annotation at the current player timestamp)\nplugin.fire('addingAnnotation');\n // cancelAddingAnnotation : Plugin exits the adding annotation state\nplugin.fire('cancelAddingAnnotation');\n // toggleAnnotationMode : toggle annotation mode to alternative on/off value\nplugin.fire('toggleAnnotationMode');\n```\n\n#### Supported Internally Fired Events:\nThese are events that are triggered from within the running plugin and can be listened for by binding to `plugin.registerListener` within your scripts:\n\n ```javascript\n// annotationOpened : Fired whenever an annotation is opened\nplugin.registerListener('annotationOpened', (event) =\u003e {\n    // event.detail =\n    // {\n    //      annotation: (object) annotation data in format {id:.., comments:..., range:..., shape:...},\n    //      triggered_by_timeline: (boolean) TRUE = the event was triggered via a timeline action (like scrubbing or playing), FALSE = the annotation was opened via marker click, UI button interactions, or API/event input\n    // }\n});\n // annotationClosed : Fired whenever an annotation is closed\nplugin.registerListener('annotationClosed', (event) =\u003e {\n    // event.detail = annotation (object) in format {id:.., comments:..., range:..., shape:...}\n});\n // addingAnnotationDataChanged : Fired from adding annotation state if:\n//  1. the marker is dragged\n//  2. the start of the marker is moved via control buttons\n//  3. the shape is dragged\nplugin.registerListener('addingAnnotationDataChanged', (event) =\u003e {\n    var newRange = event.detail.range; // returns range data if range was changed\n    var newShape = event.detail.shape; // returns shape data if shape was changed\n    // do something with the data\n});\n // annotationDeleted : Fired when an annotation has been deleted via the UI\nplugin.registerListener('annotationDeleted', (event) =\u003e {\n    // annotationId = event.detail\n});\n // enteredAnnotationMode : Fired when the plugin enters adding annotation mode\n// includes initial range data\nplugin.registerListener('enteredAddingAnnotation', (event) =\u003e {\n    var startTime = event.detail.range.start;\n    // do something when adding annotation state begins\n});\n // onStateChanged: Fired when plugin state has changed (annotation added, removed, etc)\n// This is a way to watch global plugin state, as an alternative to watching various annotation events\nplugin.registerListener('onStateChanged', (event) =\u003e {\n    // event.detail = annotation state data\n});\n // playerBoundsChanged : Fired when the player boundaries change due to window resize or fullscreen mode\nplugin.registerListener('playerBoundsChanged', (event) =\u003e {\n    var bounds = event.detail;\n    // do something with the new boundaries\n});\n // Entering annotation mode (annotation icon was clicked when previously 'off')\nplugin.registerListener('annotationModeEnabled', (event) =\u003e {\n    // do something\n});\n // Exiting annotation mode (annotation icon was clicked when previously 'on')\nplugin.registerListener('annotationModeDisabled', (event) =\u003e {\n    // do something\n});\n```\n\n## Develop and Build\n\nWe're using [yarn](https://yarnpkg.com/en/) for package management and [gulp](https://github.com/gulpjs/gulp) as our build system.\n\nThe fastest way to get started:\n- Clone the repo\n- Run `yarn install`\n- Run `yarn build`\n- Run `yarn watch`\n- Visit `http://localhost:3004/test.html` to see the magic happen.\n\n### Templates\n\n We're using the [Handlebars](http://handlebarsjs.com/) 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! ⚡️\n\nThe `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`.\n\n\n### UI / CSS Customization\n\nThe plugin uses SASS and all styles are defined in [annotaitons.scss](src/css/annotations.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.\n\nYou 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.\n\n_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.\n\n### Testing\n\n#### Feature tests\n\nFeature 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.\n\n#### Unit tests\n\nUnit 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.\n\n### Gulp commands\n\n`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.\n\n`gulp transpile`: Transpiles modules/files to build file in `/build` with JS maps\n\n`gulp build`: Runs transpilation, browserify, sass, then minifies to distribution filename in `/build` with attribution\n\n`gulp templates`: Uses Handlebars to pre-compile templates into a javascript file. See Templates section above.\n\n`gulp test`: Runs the mocha unit tests within the `/test/mocha/modules/` directory.\n\n`gulp lint`: Runs jshint linter on javascript files in `/src`\n\n### License\n\nThis plugin is [licensed](license.md) under the Apache License, Version 2.0, which is the same license used by Video.js\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftrilogy-group%2Fcontently-videojs-annotation-comments","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftrilogy-group%2Fcontently-videojs-annotation-comments","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftrilogy-group%2Fcontently-videojs-annotation-comments/lists"}