├── .babelrc
├── .esdoc.json
├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE.md
└── workflows
│ └── main.yml
├── .gitignore
├── .htmlhintrc
├── .ncurc.json
├── .npmignore
├── CHANGES.md
├── LICENSE
├── README.md
├── build-config
├── fragments
│ ├── common.js
│ ├── dev.js
│ ├── main.js
│ └── prod.js
├── webpack.dev.main.js
└── webpack.prod.main.js
├── docs
├── .nojekyll
├── README.md
├── _sidebar.md
├── angular.md
├── api
│ ├── ast
│ │ └── source
│ │ │ ├── .external-ecmascript.js.json
│ │ │ └── js
│ │ │ ├── defaults.js.json
│ │ │ ├── event.js.json
│ │ │ ├── middleware.js.json
│ │ │ ├── utils
│ │ │ ├── format-time.js.json
│ │ │ └── log.js.json
│ │ │ └── videojs.wavesurfer.js.json
│ ├── badge.svg
│ ├── class
│ │ └── src
│ │ │ └── js
│ │ │ ├── event.js~Event.html
│ │ │ └── videojs.wavesurfer.js~Wavesurfer.html
│ ├── coverage.json
│ ├── css
│ │ ├── github.css
│ │ ├── identifiers.css
│ │ ├── manual.css
│ │ ├── prettify-tomorrow.css
│ │ ├── search.css
│ │ ├── source.css
│ │ ├── style.css
│ │ └── test.css
│ ├── file
│ │ └── src
│ │ │ └── js
│ │ │ ├── defaults.js.html
│ │ │ ├── event.js.html
│ │ │ ├── middleware.js.html
│ │ │ ├── utils
│ │ │ ├── format-time.js.html
│ │ │ └── log.js.html
│ │ │ └── videojs.wavesurfer.js.html
│ ├── function
│ │ └── index.html
│ ├── identifiers.html
│ ├── image
│ │ ├── badge.svg
│ │ ├── esdoc-logo-mini-black.png
│ │ ├── esdoc-logo-mini.png
│ │ ├── github.png
│ │ ├── manual-badge.svg
│ │ └── search.png
│ ├── index.html
│ ├── index.json
│ ├── lint.json
│ ├── script
│ │ ├── inherited-summary.js
│ │ ├── inner-link.js
│ │ ├── manual.js
│ │ ├── patch-for-local.js
│ │ ├── prettify
│ │ │ ├── Apache-License-2.0.txt
│ │ │ └── prettify.js
│ │ ├── pretty-print.js
│ │ ├── search.js
│ │ ├── search_index.js
│ │ └── test-summary.js
│ ├── source.html
│ └── variable
│ │ └── index.html
├── change-device.md
├── controls.md
├── css
│ └── style.css
├── demo
│ ├── fluid.html
│ ├── index.html
│ ├── input.html
│ ├── live.html
│ ├── media
│ │ ├── example.mp4
│ │ ├── hal-peaks.json
│ │ ├── hal.mp3
│ │ ├── hal.vtt
│ │ └── hal.wav
│ ├── multi.html
│ ├── output.html
│ ├── peaks.html
│ ├── plugin.html
│ ├── react
│ │ ├── index.html
│ │ └── index.js
│ ├── safari-workaround.js
│ ├── texttrack.html
│ └── video.html
├── development.md
├── donate.md
├── events.md
├── examples.md
├── img
│ ├── screenshot.png
│ └── text-tracks.png
├── index.html
├── install.md
├── methods.md
├── microphone.md
├── options.md
├── peaks.md
├── plugins.md
├── react.md
├── responsive.md
├── text-tracks.md
├── tools
│ └── update-videojs.js
├── usage.md
├── vue.md
└── webpack.md
├── eslint.config.js
├── examples
├── fluid.html
├── index.html
├── input.html
├── live.html
├── media
│ ├── example.mp4
│ ├── hal-peaks.json
│ ├── hal.vtt
│ └── hal.wav
├── multi.html
├── output.html
├── peaks.html
├── plugin.html
├── react
│ ├── index.html
│ └── index.js
├── safari-workaround.js
├── texttrack.html
└── video.html
├── karma.conf.js
├── package-lock.json
├── package.json
├── src
├── css
│ ├── fluid.scss
│ ├── main.scss
│ └── videojs.wavesurfer.scss
└── js
│ ├── defaults.js
│ ├── event.js
│ ├── middleware.js
│ ├── utils
│ ├── format-time.js
│ └── log.js
│ └── videojs.wavesurfer.js
└── test
├── defaults.spec.js
├── fluid.spec.js
├── live.spec.js
├── options.spec.js
├── support
├── demo-peaks-invalid.json
├── demo-peaks.json
├── demo.vtt
├── demo.wav
└── stars.mp4
├── test-helpers.js
├── tracks.spec.js
├── utils.spec.js
├── video.spec.js
└── videojs.wavesurfer.spec.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["@babel/preset-env", {
4 | "targets": "last 3 version",
5 | "debug": false,
6 | "modules": "commonjs"
7 | }]
8 | ],
9 | "plugins": ["add-module-exports"],
10 | "env": {
11 | "test": {
12 | "plugins": [
13 | ["istanbul", {
14 | "exclude": [
15 | "test/**/*.spec.js"
16 | ]
17 | }]
18 | ]
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/.esdoc.json:
--------------------------------------------------------------------------------
1 | {
2 | "source": "./src",
3 | "destination": "./docs/api",
4 | "plugins": [{"name": "esdoc-standard-plugin"}]
5 | }
6 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | open_collective: thijs-triemstra
2 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ## Description
2 | Briefly describe the issue.
3 |
4 | ## Steps to reproduce
5 | Explain in detail the exact steps necessary to reproduce the issue.
6 |
7 | ## Results
8 | ### Expected
9 | Please describe what you expected to see.
10 |
11 | ### Actual
12 | Please describe what actually happened.
13 |
14 | ### Error output
15 | If there are any errors at all, please include them here.
16 |
17 | ## Versions
18 | Make sure to include the following versions:
19 | ### videojs/wavesurfer
20 | what version of video.js, videojs-wavesurfer and wavesurfer.js does this occur with?
21 | ### Browsers
22 | what browser(s) are affected? Make sure to test with all third-party browser **extensions disabled**.
23 | ### OSes
24 | what platforms (operating systems and devices) are affected?
25 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: videojs-wavesurfer
2 |
3 | on:
4 | push:
5 | branches: [ master ]
6 | pull_request:
7 | branches: [ master ]
8 |
9 | jobs:
10 | build:
11 | runs-on: ${{ matrix.os }}
12 | strategy:
13 | matrix:
14 | os:
15 | - ubuntu-latest
16 | - windows-latest
17 | node-version:
18 | - 20
19 | architecture:
20 | - x64
21 | name: ${{ matrix.os }} - Node ${{ matrix.node-version }} (${{ matrix.architecture }})
22 | steps:
23 | - uses: actions/checkout@v4
24 | - name: Update system
25 | if: runner.os == 'Linux'
26 | run: sudo apt-get update
27 | - name: Install system dependencies
28 | if: runner.os == 'Linux'
29 | run: sudo apt-get install -y ubuntu-restricted-addons chromium-codecs-ffmpeg-extra gstreamer1.0-libav gstreamer1.0-plugins-ugly gstreamer1.0-vaapi
30 | - name: Using Node.js ${{ matrix.node-version }}
31 | uses: actions/setup-node@v4
32 | with:
33 | node-version: ${{ matrix.node-version }}
34 | architecture: ${{ matrix.architecture }}
35 | - name: Get npm cache directory
36 | id: npm-cache-dir
37 | shell: bash
38 | run: echo "dir=$(npm config get cache)" >> ${GITHUB_OUTPUT}
39 | - name: Cache npm modules
40 | uses: actions/cache@v4
41 | id: npm-cache # use this to check for `cache-hit` ==> if: steps.npm-cache.outputs.cache-hit != 'true'
42 | with:
43 | path: ${{ steps.npm-cache-dir.outputs.dir }}
44 | key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
45 | restore-keys: |
46 | ${{ runner.os }}-node-
47 | - name: Install Node.js modules
48 | run: npm install
49 | - name: Build
50 | run: npm run build
51 | - name: Test
52 | run: npm run test
53 | - name: Coveralls
54 | if: runner.os == 'Linux'
55 | uses: coverallsapp/github-action@v2
56 | with:
57 | path-to-lcov: ${{ github.workspace }}/coverage/lcov/lcov.info
58 | github-token: ${{ secrets.GITHUB_TOKEN }}
59 | - name: Documentation
60 | run: npm run docs
61 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # osx
2 | .DS_Store
3 |
4 | # IDE
5 | .project
6 | .pydevproject
7 | .vscode
8 |
9 | *.pid
10 | *.log
11 |
12 | # examples
13 | examples/media/test
14 |
15 | # Dependency-related directories
16 | node_modules
17 | bower_components
18 |
19 | # Build-related
20 | dist/
21 | tmp
22 | coverage/
23 | .build_cache/
24 |
25 | # Test
26 | .chrome
27 |
--------------------------------------------------------------------------------
/.htmlhintrc:
--------------------------------------------------------------------------------
1 | {
2 | "tagname-lowercase": true,
3 | "attr-lowercase": true,
4 | "attr-value-double-quotes": false,
5 | "doctype-first": true,
6 | "tag-pair": true,
7 | "spec-char-escape": true,
8 | "id-unique": true,
9 | "src-not-empty": true,
10 | "attr-no-duplication": true,
11 | "title-require": true
12 | }
13 |
--------------------------------------------------------------------------------
/.ncurc.json:
--------------------------------------------------------------------------------
1 | {
2 | "reject": [
3 | "parse-ms",
4 | "video.js",
5 | "wavesurfer.js"
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | # Intentionally left blank, so that npm does not ignore anything by default,
2 | # but relies on the package.json "files" array to explicitly define what ends
3 | # up in the package.
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014-2024 Collab
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | videojs-wavesurfer
2 | ==================
3 |
4 | A [video.js](https://www.videojs.com/) plugin that adds a navigable waveform
5 | for audio and video files, using the [wavesurfer.js](https://github.com/katspaugh/wavesurfer.js)
6 | library. Includes support for fullscreen mode and [real-time visualization of microphone
7 | input](https://collab-project.github.io/videojs-wavesurfer/#/microphone).
8 |
9 | 
10 |
11 | [](https://www.npmjs.com/package/videojs-wavesurfer)
12 | [](https://github.com/collab-project/videojs-wavesurfer/releases)
13 | [](LICENSE)
14 | [](https://github.com/collab-project/videojs-wavesurfer/actions?workflow=videojs-wavesurfer)
15 | [](https://coveralls.io/github/collab-project/videojs-wavesurfer?branch=master)
16 | 
17 | 
18 |
19 | ## Documentation
20 |
21 | The documentation and examples can be found on: https://collab-project.github.io/videojs-wavesurfer
22 |
23 | ## License
24 |
25 | This work is licensed under the [MIT License](LICENSE).
26 |
--------------------------------------------------------------------------------
/build-config/fragments/common.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file common.js
3 | * @since 2.3.0
4 | */
5 |
6 | const path = require('path');
7 | const webpack = require('webpack');
8 | const datefns = require('date-fns');
9 | const MiniCssExtractPlugin = require('mini-css-extract-plugin');
10 |
11 | const date = datefns.format(new Date(), 'yyyy');
12 | const rootDir = path.resolve(__dirname, '..', '..');
13 | const pckg = require(path.join(__dirname, '..', '..', 'package.json'));
14 |
15 | process.traceDeprecation = true;
16 |
17 | // add JS banner with copyright and version info
18 | let jsBanner = `${pckg.name}
19 | @version ${pckg.version}
20 | @see ${pckg.homepage}
21 | @copyright 2014-${date} ${pckg.author}
22 | @license ${pckg.license}`;
23 | let jsBannerPlugin = new webpack.BannerPlugin({
24 | banner: jsBanner,
25 | test: /\.js$/
26 | });
27 |
28 | // add CSS banner with version info
29 | let cssBanner = `/*!
30 | Default styles for ${pckg.name} ${pckg.version}
31 | */`;
32 | let cssBannerPlugin = new webpack.BannerPlugin({
33 | banner: cssBanner,
34 | raw: true,
35 | test: /\.css$/
36 | });
37 |
38 | // inject JS version number
39 | let jsVersionPlugin = new webpack.DefinePlugin({
40 | '__VERSION__': JSON.stringify(pckg.version)
41 | });
42 |
43 | module.exports = {
44 | devtool: false,
45 | context: rootDir,
46 | output: {
47 | libraryTarget: 'umd',
48 | umdNamedDefine: true
49 | },
50 | performance: {
51 | hints: false
52 | },
53 | stats: {
54 | colors: true
55 | },
56 | // specify dependencies for the library that are not resolved by webpack,
57 | // but become dependencies of the output: they are imported from the
58 | // environment during runtime and never directly included in the
59 | // videojs-wavesurfer library
60 | externals: {
61 | 'video.js': {
62 | commonjs: 'video.js',
63 | commonjs2: 'video.js',
64 | amd: 'video.js',
65 | root: 'videojs' // indicates global variable
66 | },
67 | 'wavesurfer.js': {
68 | commonjs: 'wavesurfer.js',
69 | commonjs2: 'wavesurfer.js',
70 | amd: 'wavesurfer.js',
71 | root: 'WaveSurfer' // indicates global variable
72 | }
73 | },
74 | module: {
75 | rules: [
76 | {
77 | // javascript
78 | test: /\.js$/,
79 | include: path.resolve(rootDir, 'src', 'js'),
80 | exclude: /(node_modules|bower_components|test)/,
81 | use: {
82 | loader: 'babel-loader',
83 | options: {
84 | comments: false
85 | }
86 | }
87 | },
88 | {
89 | // scss
90 | test: /\.scss$/,
91 | include: path.resolve(rootDir, 'src', 'css'),
92 | exclude: /(node_modules|bower_components|test)/,
93 | use: [
94 | MiniCssExtractPlugin.loader,
95 | 'css-loader',
96 | 'sass-loader'
97 | ]
98 | }
99 | ]
100 | },
101 | plugins: [
102 | jsBannerPlugin,
103 | jsVersionPlugin,
104 | cssBannerPlugin
105 | ]
106 | };
107 |
--------------------------------------------------------------------------------
/build-config/fragments/dev.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file dev.js
3 | * @since 2.3.0
4 | */
5 |
6 | const path = require('path');
7 | const MiniCssExtractPlugin = require('mini-css-extract-plugin');
8 | const RemoveEmptyScriptsPlugin = require('webpack-remove-empty-scripts');
9 |
10 | const contentBase = path.resolve(__dirname, '..', '..');
11 |
12 | module.exports = {
13 | mode: 'development',
14 | devServer: {
15 | port: 8080,
16 | static: [
17 | {
18 | directory: contentBase,
19 | staticOptions: {},
20 | publicPath: '/',
21 | serveIndex: true,
22 | watch: {
23 | ignored: [
24 | /.build_cache/,
25 | /.chrome/,
26 | /docs/,
27 | /node_modules/,
28 | /bower_components/,
29 | /coverage/,
30 | /build-config/,
31 | /test/,
32 | /vendor/
33 | ]
34 | }
35 | }
36 | ]
37 | },
38 | plugins: [
39 | new RemoveEmptyScriptsPlugin(),
40 | new MiniCssExtractPlugin({
41 | filename: 'css/videojs.wavesurfer.css',
42 | chunkFilename: 'css/[id].css'
43 | })
44 | ]
45 | };
46 |
--------------------------------------------------------------------------------
/build-config/fragments/main.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file main.js
3 | * @since 2.3.0
4 | */
5 |
6 | const path = require('path');
7 | const rootDir = path.resolve(__dirname, '..', '..');
8 | const sourceDir = path.join(rootDir, 'src');
9 |
10 | module.exports = {
11 | entry: {
12 | code: {
13 | import: path.join(sourceDir, 'js', 'videojs.wavesurfer.js'),
14 | filename: 'videojs.wavesurfer.js'
15 | },
16 | style: {
17 | import: path.join(sourceDir, 'css', 'videojs.wavesurfer.scss')
18 | }
19 | },
20 | output: {
21 | path: path.join(rootDir, 'dist'),
22 | library: 'VideojsWavesurfer'
23 | }
24 | };
25 |
--------------------------------------------------------------------------------
/build-config/fragments/prod.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file prod.js
3 | * @since 2.3.0
4 | */
5 |
6 | const TerserPlugin = require('terser-webpack-plugin');
7 | const MiniCssExtractPlugin = require('mini-css-extract-plugin');
8 | const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
9 | const RemoveEmptyScriptsPlugin = require('webpack-remove-empty-scripts');
10 |
11 | module.exports = {
12 | mode: 'production',
13 | optimization: {
14 | minimize: true,
15 | minimizer: [
16 | new TerserPlugin({
17 | parallel: true,
18 | extractComments: false,
19 | terserOptions: {
20 | output: {
21 | // preserve license comments
22 | comments: /@license/i
23 | }
24 | }
25 | }),
26 | new CssMinimizerPlugin()
27 | ]
28 | },
29 | plugins: [
30 | new RemoveEmptyScriptsPlugin(),
31 | new MiniCssExtractPlugin({
32 | filename: 'css/videojs.wavesurfer.min.css',
33 | chunkFilename: 'css/[id].css'
34 | })
35 | ]
36 | };
37 |
--------------------------------------------------------------------------------
/build-config/webpack.dev.main.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @since 2.3.0
3 | */
4 |
5 | const { merge } = require('webpack-merge');
6 | const path = require('path');
7 |
8 | const common = require('./fragments/common');
9 | const dev = require('./fragments/dev');
10 | const main = require('./fragments/main');
11 |
12 | module.exports = merge(common, dev, main);
13 |
--------------------------------------------------------------------------------
/build-config/webpack.prod.main.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @since 2.3.0
3 | */
4 |
5 | const { merge } = require('webpack-merge');
6 | const path = require('path');
7 |
8 | const common = require('./fragments/common');
9 | const prod = require('./fragments/prod');
10 | const main = require('./fragments/main');
11 |
12 | module.exports = merge(common, prod, main, {
13 | entry: {
14 | code: {
15 | filename: 'videojs.wavesurfer.min.js'
16 | }
17 | }
18 | });
19 |
--------------------------------------------------------------------------------
/docs/.nojekyll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/collab-project/videojs-wavesurfer/cd3284bf8e2da59039fe59af8ce33003d69bd6ae/docs/.nojekyll
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # videojs-wavesurfer
2 |
3 | A [video.js](https://www.videojs.com/) plugin that adds a navigable waveform
4 | for audio and video files, using the [wavesurfer.js](https://github.com/katspaugh/wavesurfer.js)
5 | library.
6 |
7 | Includes support for fullscreen mode and [real-time visualization of microphone
8 | input](microphone.md).
9 |
10 | 
11 |
--------------------------------------------------------------------------------
/docs/_sidebar.md:
--------------------------------------------------------------------------------
1 | - Getting started
2 | - [Installation](install.md)
3 | - [Usage](usage.md)
4 | - [Examples](examples.md)
5 |
6 | - Guide
7 | - [Options](options.md)
8 | - [Methods](methods.md)
9 | - [Events](events.md)
10 | - [Customizing controls](controls.md)
11 | - [Reponsive layout](responsive.md)
12 | - [Text tracks](text-tracks.md)
13 | - [Microphone plugin](microphone.md)
14 | - [Using peaks for large audio files](peaks.md)
15 | - [Change audio device](change-device.md)
16 | - [More features using other plugins](plugins.md)
17 | - [API documentation](https://collab-project.github.io/videojs-wavesurfer/api/)
18 |
19 | - Frameworks
20 | - [Webpack](webpack.md)
21 | - [React](react.md)
22 | - [Angular](angular.md)
23 | - [Vue](vue.md)
24 |
25 | - [Development](development.md)
26 | - [Changelog](/changelog)
27 | - [Github](https://github.com/collab-project/videojs-wavesurfer)
28 | - [License](https://github.com/collab-project/videojs-wavesurfer/blob/master/LICENSE)
29 | - [Donate](donate.md)
30 |
--------------------------------------------------------------------------------
/docs/api/badge.svg:
--------------------------------------------------------------------------------
1 |
18 |
--------------------------------------------------------------------------------
/docs/api/class/src/js/event.js~Event.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Event | videojs-wavesurfer
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
28 |
29 |
42 |
43 |
52 |
53 |
54 |
Event
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
--------------------------------------------------------------------------------
/docs/api/coverage.json:
--------------------------------------------------------------------------------
1 | {
2 | "coverage": "60.34%",
3 | "expectCount": 58,
4 | "actualCount": 35,
5 | "files": {
6 | "src/js/defaults.js": {
7 | "expectCount": 1,
8 | "actualCount": 1,
9 | "undocumentLines": []
10 | },
11 | "src/js/event.js": {
12 | "expectCount": 1,
13 | "actualCount": 1,
14 | "undocumentLines": []
15 | },
16 | "src/js/middleware.js": {
17 | "expectCount": 1,
18 | "actualCount": 1,
19 | "undocumentLines": []
20 | },
21 | "src/js/utils/format-time.js": {
22 | "expectCount": 1,
23 | "actualCount": 1,
24 | "undocumentLines": []
25 | },
26 | "src/js/utils/log.js": {
27 | "expectCount": 3,
28 | "actualCount": 1,
29 | "undocumentLines": [
30 | 8,
31 | 9
32 | ]
33 | },
34 | "src/js/videojs.wavesurfer.js": {
35 | "expectCount": 51,
36 | "actualCount": 30,
37 | "undocumentLines": [
38 | 18,
39 | 20,
40 | 21,
41 | 22,
42 | 25,
43 | 26,
44 | 27,
45 | 55,
46 | 56,
47 | 57,
48 | 58,
49 | 59,
50 | 60,
51 | 61,
52 | 140,
53 | 162,
54 | 164,
55 | 165,
56 | 199,
57 | 200,
58 | 832
59 | ]
60 | }
61 | }
62 | }
--------------------------------------------------------------------------------
/docs/api/css/github.css:
--------------------------------------------------------------------------------
1 | /* github markdown */
2 | .github-markdown {
3 | font-size: 16px;
4 | }
5 |
6 | .github-markdown h1,
7 | .github-markdown h2,
8 | .github-markdown h3,
9 | .github-markdown h4,
10 | .github-markdown h5 {
11 | margin-top: 1em;
12 | margin-bottom: 16px;
13 | font-weight: bold;
14 | padding: 0;
15 | }
16 |
17 | .github-markdown h1:nth-of-type(1) {
18 | margin-top: 0;
19 | }
20 |
21 | .github-markdown h1 {
22 | font-size: 2em;
23 | padding-bottom: 0.3em;
24 | }
25 |
26 | .github-markdown h2 {
27 | font-size: 1.75em;
28 | padding-bottom: 0.3em;
29 | }
30 |
31 | .github-markdown h3 {
32 | font-size: 1.5em;
33 | }
34 |
35 | .github-markdown h4 {
36 | font-size: 1.25em;
37 | }
38 |
39 | .github-markdown h5 {
40 | font-size: 1em;
41 | }
42 |
43 | .github-markdown ul, .github-markdown ol {
44 | padding-left: 2em;
45 | }
46 |
47 | .github-markdown pre > code {
48 | font-size: 0.85em;
49 | }
50 |
51 | .github-markdown table {
52 | margin-bottom: 1em;
53 | border-collapse: collapse;
54 | border-spacing: 0;
55 | }
56 |
57 | .github-markdown table tr {
58 | background-color: #fff;
59 | border-top: 1px solid #ccc;
60 | }
61 |
62 | .github-markdown table th,
63 | .github-markdown table td {
64 | padding: 6px 13px;
65 | border: 1px solid #ddd;
66 | }
67 |
68 | .github-markdown table tr:nth-child(2n) {
69 | background-color: #f8f8f8;
70 | }
71 |
72 | .github-markdown hr {
73 | border-right: 0;
74 | border-bottom: 1px solid #e5e5e5;
75 | border-left: 0;
76 | border-top: 0;
77 | }
78 |
79 | /** badge(.svg) does not have border */
80 | .github-markdown img:not([src*=".svg"]) {
81 | max-width: 100%;
82 | box-shadow: 1px 1px 1px rgba(0,0,0,0.5);
83 | }
84 |
--------------------------------------------------------------------------------
/docs/api/css/identifiers.css:
--------------------------------------------------------------------------------
1 | .identifiers-wrap {
2 | display: flex;
3 | align-items: flex-start;
4 | }
5 |
6 | .identifier-dir-tree {
7 | background: #fff;
8 | border: solid 1px #ddd;
9 | border-radius: 0.25em;
10 | top: 52px;
11 | position: -webkit-sticky;
12 | position: sticky;
13 | max-height: calc(100vh - 155px);
14 | overflow-y: scroll;
15 | min-width: 200px;
16 | margin-left: 1em;
17 | }
18 |
19 | .identifier-dir-tree-header {
20 | padding: 0.5em;
21 | background-color: #fafafa;
22 | border-bottom: solid 1px #ddd;
23 | }
24 |
25 | .identifier-dir-tree-content {
26 | padding: 0 0.5em 0;
27 | }
28 |
29 | .identifier-dir-tree-content > div {
30 | padding-top: 0.25em;
31 | padding-bottom: 0.25em;
32 | }
33 |
34 | .identifier-dir-tree-content a {
35 | color: inherit;
36 | }
37 |
38 |
--------------------------------------------------------------------------------
/docs/api/css/manual.css:
--------------------------------------------------------------------------------
1 | .github-markdown .manual-toc {
2 | padding-left: 0;
3 | }
4 |
5 | .manual-index .manual-cards {
6 | display: flex;
7 | flex-wrap: wrap;
8 | }
9 |
10 | .manual-index .manual-card-wrap {
11 | width: 280px;
12 | padding: 10px 20px 10px 0;
13 | box-sizing: border-box;
14 | }
15 |
16 | .manual-index .manual-card-wrap > h1 {
17 | margin: 0;
18 | font-size: 1em;
19 | font-weight: 600;
20 | padding: 0.2em 0 0.2em 0.5em;
21 | border-radius: 0.1em 0.1em 0 0;
22 | border: none;
23 | }
24 |
25 | .manual-index .manual-card-wrap > h1 span {
26 | color: #555;
27 | }
28 |
29 | .manual-index .manual-card {
30 | height: 200px;
31 | overflow: hidden;
32 | border: solid 1px rgba(230, 230, 230, 0.84);
33 | border-radius: 0 0 0.1em 0.1em;
34 | padding: 8px;
35 | position: relative;
36 | }
37 |
38 | .manual-index .manual-card > div {
39 | transform: scale(0.4);
40 | transform-origin: 0 0;
41 | width: 250%;
42 | }
43 |
44 | .manual-index .manual-card > a {
45 | position: absolute;
46 | top: 0;
47 | left: 0;
48 | width: 100%;
49 | height: 100%;
50 | background: rgba(210, 210, 210, 0.1);
51 | }
52 |
53 | .manual-index .manual-card > a:hover {
54 | background: none;
55 | }
56 |
57 | .manual-index .manual-badge {
58 | margin: 0;
59 | }
60 |
61 | .manual-index .manual-user-index {
62 | margin-bottom: 1em;
63 | border-bottom: solid 1px #ddd;
64 | }
65 |
66 | .manual-root .navigation {
67 | padding-left: 4px;
68 | margin-top: 4px;
69 | }
70 |
71 | .navigation .manual-toc-root > div {
72 | padding-left: 0.25em;
73 | padding-right: 0.75em;
74 | }
75 |
76 | .github-markdown .manual-toc-title a {
77 | color: inherit;
78 | }
79 |
80 | .manual-breadcrumb-list {
81 | font-size: 0.8em;
82 | margin-bottom: 1em;
83 | }
84 |
85 | .manual-toc-title a:hover {
86 | color: #039BE5;
87 | }
88 |
89 | .manual-toc li {
90 | margin: 0.75em 0;
91 | list-style-type: none;
92 | }
93 |
94 | .navigation .manual-toc [class^="indent-h"] a {
95 | color: #666;
96 | }
97 |
98 | .navigation .manual-toc .indent-h1 a {
99 | color: #555;
100 | font-weight: 600;
101 | display: block;
102 | }
103 |
104 | .manual-toc .indent-h1 {
105 | display: block;
106 | margin: 0.4em 0 0 0.25em;
107 | padding: 0.2em 0 0.2em 0.5em;
108 | border-radius: 0.1em;
109 | }
110 |
111 | .manual-root .navigation .manual-toc li:not(.indent-h1) {
112 | margin-top: 0.5em;
113 | }
114 |
115 | .manual-toc .indent-h2 {
116 | display: none;
117 | margin-left: 1.5em;
118 | }
119 | .manual-toc .indent-h3 {
120 | display: none;
121 | margin-left: 2.5em;
122 | }
123 | .manual-toc .indent-h4 {
124 | display: none;
125 | margin-left: 3.5em;
126 | }
127 | .manual-toc .indent-h5 {
128 | display: none;
129 | margin-left: 4.5em;
130 | }
131 |
132 | .manual-nav li {
133 | margin: 0.75em 0;
134 | }
135 |
--------------------------------------------------------------------------------
/docs/api/css/prettify-tomorrow.css:
--------------------------------------------------------------------------------
1 | /* Tomorrow Theme */
2 | /* Original theme - https://github.com/chriskempson/tomorrow-theme */
3 | /* Pretty printing styles. Used with prettify.js. */
4 | /* SPAN elements with the classes below are added by prettyprint. */
5 | /* plain text */
6 | .pln {
7 | color: #4d4d4c; }
8 |
9 | @media screen {
10 | /* string content */
11 | .str {
12 | color: #718c00; }
13 |
14 | /* a keyword */
15 | .kwd {
16 | color: #8959a8; }
17 |
18 | /* a comment */
19 | .com {
20 | color: #8e908c; }
21 |
22 | /* a type name */
23 | .typ {
24 | color: #4271ae; }
25 |
26 | /* a literal value */
27 | .lit {
28 | color: #f5871f; }
29 |
30 | /* punctuation */
31 | .pun {
32 | color: #4d4d4c; }
33 |
34 | /* lisp open bracket */
35 | .opn {
36 | color: #4d4d4c; }
37 |
38 | /* lisp close bracket */
39 | .clo {
40 | color: #4d4d4c; }
41 |
42 | /* a markup tag name */
43 | .tag {
44 | color: #c82829; }
45 |
46 | /* a markup attribute name */
47 | .atn {
48 | color: #f5871f; }
49 |
50 | /* a markup attribute value */
51 | .atv {
52 | color: #3e999f; }
53 |
54 | /* a declaration */
55 | .dec {
56 | color: #f5871f; }
57 |
58 | /* a variable name */
59 | .var {
60 | color: #c82829; }
61 |
62 | /* a function name */
63 | .fun {
64 | color: #4271ae; } }
65 | /* Use higher contrast and text-weight for printable form. */
66 | @media print, projection {
67 | .str {
68 | color: #060; }
69 |
70 | .kwd {
71 | color: #006;
72 | font-weight: bold; }
73 |
74 | .com {
75 | color: #600;
76 | font-style: italic; }
77 |
78 | .typ {
79 | color: #404;
80 | font-weight: bold; }
81 |
82 | .lit {
83 | color: #044; }
84 |
85 | .pun, .opn, .clo {
86 | color: #440; }
87 |
88 | .tag {
89 | color: #006;
90 | font-weight: bold; }
91 |
92 | .atn {
93 | color: #404; }
94 |
95 | .atv {
96 | color: #060; } }
97 | /* Style */
98 | /*
99 | pre.prettyprint {
100 | background: white;
101 | font-family: Consolas, Monaco, 'Andale Mono', monospace;
102 | font-size: 12px;
103 | line-height: 1.5;
104 | border: 1px solid #ccc;
105 | padding: 10px; }
106 | */
107 |
108 | /* Specify class=linenums on a pre to get line numbering */
109 | ol.linenums {
110 | margin-top: 0;
111 | margin-bottom: 0; }
112 |
113 | /* IE indents via margin-left */
114 | li.L0,
115 | li.L1,
116 | li.L2,
117 | li.L3,
118 | li.L4,
119 | li.L5,
120 | li.L6,
121 | li.L7,
122 | li.L8,
123 | li.L9 {
124 | /* */ }
125 |
126 | /* Alternate shading for lines */
127 | li.L1,
128 | li.L3,
129 | li.L5,
130 | li.L7,
131 | li.L9 {
132 | /* */ }
133 |
--------------------------------------------------------------------------------
/docs/api/css/search.css:
--------------------------------------------------------------------------------
1 | /* search box */
2 | .search-box {
3 | position: absolute;
4 | top: 10px;
5 | right: 50px;
6 | padding-right: 8px;
7 | padding-bottom: 10px;
8 | line-height: normal;
9 | font-size: 12px;
10 | }
11 |
12 | .search-box img {
13 | width: 20px;
14 | vertical-align: top;
15 | }
16 |
17 | .search-input {
18 | display: inline;
19 | visibility: hidden;
20 | width: 0;
21 | padding: 2px;
22 | height: 1.5em;
23 | outline: none;
24 | background: transparent;
25 | border: 1px #0af;
26 | border-style: none none solid none;
27 | vertical-align: bottom;
28 | }
29 |
30 | .search-input-edge {
31 | display: none;
32 | width: 1px;
33 | height: 5px;
34 | background-color: #0af;
35 | vertical-align: bottom;
36 | }
37 |
38 | .search-result {
39 | position: absolute;
40 | display: none;
41 | height: 600px;
42 | width: 100%;
43 | padding: 0;
44 | margin-top: 5px;
45 | margin-left: 24px;
46 | background: white;
47 | box-shadow: 1px 1px 4px rgb(0,0,0);
48 | white-space: nowrap;
49 | overflow-y: scroll;
50 | }
51 |
52 | .search-result-import-path {
53 | color: #aaa;
54 | font-size: 12px;
55 | }
56 |
57 | .search-result li {
58 | list-style: none;
59 | padding: 2px 4px;
60 | }
61 |
62 | .search-result li a {
63 | display: block;
64 | }
65 |
66 | .search-result li.selected {
67 | background: #ddd;
68 | }
69 |
70 | .search-result li.search-separator {
71 | background: rgb(37, 138, 175);
72 | color: white;
73 | }
74 |
75 | .search-box.active .search-input {
76 | visibility: visible;
77 | transition: width 0.2s ease-out;
78 | width: 300px;
79 | }
80 |
81 | .search-box.active .search-input-edge {
82 | display: inline-block;
83 | }
84 |
85 |
--------------------------------------------------------------------------------
/docs/api/css/source.css:
--------------------------------------------------------------------------------
1 | table.files-summary {
2 | width: 100%;
3 | margin: 10px 0;
4 | border-spacing: 0;
5 | border: 0;
6 | border-collapse: collapse;
7 | text-align: right;
8 | }
9 |
10 | table.files-summary tbody tr:hover {
11 | background: #eee;
12 | }
13 |
14 | table.files-summary td:first-child,
15 | table.files-summary td:nth-of-type(2) {
16 | text-align: left;
17 | }
18 |
19 | table.files-summary[data-use-coverage="false"] td.coverage {
20 | display: none;
21 | }
22 |
23 | table.files-summary thead {
24 | background: #fafafa;
25 | }
26 |
27 | table.files-summary td {
28 | border: solid 1px #ddd;
29 | padding: 4px 10px;
30 | vertical-align: top;
31 | }
32 |
33 | table.files-summary td.identifiers > span {
34 | display: block;
35 | margin-top: 4px;
36 | }
37 | table.files-summary td.identifiers > span:first-child {
38 | margin-top: 0;
39 | }
40 |
41 | table.files-summary .coverage-count {
42 | font-size: 12px;
43 | color: #aaa;
44 | display: inline-block;
45 | min-width: 40px;
46 | }
47 |
48 | .total-coverage-count {
49 | position: relative;
50 | bottom: 2px;
51 | font-size: 12px;
52 | color: #666;
53 | font-weight: 500;
54 | padding-left: 5px;
55 | }
56 |
--------------------------------------------------------------------------------
/docs/api/css/test.css:
--------------------------------------------------------------------------------
1 | table.test-summary thead {
2 | background: #fafafa;
3 | }
4 |
5 | table.test-summary thead .test-description {
6 | width: 50%;
7 | }
8 |
9 | table.test-summary {
10 | width: 100%;
11 | margin: 10px 0;
12 | border-spacing: 0;
13 | border: 0;
14 | border-collapse: collapse;
15 | }
16 |
17 | table.test-summary thead .test-count {
18 | width: 3em;
19 | }
20 |
21 | table.test-summary tbody tr:hover {
22 | background-color: #eee;
23 | }
24 |
25 | table.test-summary td {
26 | border: solid 1px #ddd;
27 | padding: 4px 10px;
28 | vertical-align: top;
29 | }
30 |
31 | table.test-summary td p {
32 | margin: 0;
33 | }
34 |
35 | table.test-summary tr.test-interface .toggle {
36 | display: inline-block;
37 | float: left;
38 | margin-right: 4px;
39 | cursor: pointer;
40 | font-size: 0.8em;
41 | padding-top: 0.25em;
42 | }
43 |
44 | table.test-summary tr.test-interface .toggle.opened:before {
45 | content: '▼';
46 | }
47 |
48 | table.test-summary tr.test-interface .toggle.closed:before {
49 | content: '▶';
50 | }
51 |
52 | table.test-summary .test-target > span {
53 | display: block;
54 | margin-top: 4px;
55 | }
56 | table.test-summary .test-target > span:first-child {
57 | margin-top: 0;
58 | }
59 |
--------------------------------------------------------------------------------
/docs/api/file/src/js/defaults.js.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | src/js/defaults.js | videojs-wavesurfer
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
28 |
29 |
42 |
43 | src/js/defaults.js
44 |
/**
45 | * @file defaults.js
46 | * @since 2.0.0
47 | */
48 |
49 | // plugin defaults
50 | const pluginDefaultOptions = {
51 | // Display console log messages.
52 | debug: false,
53 | // Boolean indicating if milliseconds should be included,
54 | // e.g. "00:00:000" vs "00:00".
55 | displayMilliseconds: true
56 | };
57 |
58 | export default pluginDefaultOptions;
59 |
60 |
61 |
62 |
63 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/docs/api/file/src/js/event.js.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | src/js/event.js | videojs-wavesurfer
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
28 |
29 |
42 |
43 | src/js/event.js
44 |
/**
45 | * @file event.js
46 | * @since 2.8.0
47 | */
48 |
49 | class Event {}
50 |
51 | // video.js
52 | Event.READY = 'ready';
53 | Event.ERROR = 'error';
54 | Event.VOLUMECHANGE = 'volumechange';
55 | Event.FULLSCREENCHANGE = 'fullscreenchange';
56 | Event.TIMEUPDATE = 'timeupdate';
57 | Event.ENDED = 'ended';
58 | Event.PAUSE = 'pause';
59 |
60 | // wavesurfer.js
61 | Event.FINISH = 'finish';
62 | Event.SEEK = 'seek';
63 | Event.REDRAW = 'redraw';
64 | Event.AUDIOPROCESS = 'audioprocess';
65 | Event.DEVICE_READY = 'deviceReady';
66 | Event.DEVICE_ERROR = 'deviceError';
67 |
68 | // videojs-wavesurfer
69 | Event.AUDIO_OUTPUT_READY = 'audioOutputReady';
70 | Event.WAVE_READY = 'waveReady';
71 | Event.PLAYBACK_FINISH = 'playbackFinish';
72 | Event.ABORT = 'abort';
73 |
74 | // dom
75 | Event.RESIZE = 'resize';
76 |
77 | // after the freeze, any attempts of altering the class will have no result
78 | Object.freeze(Event);
79 |
80 | export default Event;
81 |
82 |
83 |
84 |
85 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
--------------------------------------------------------------------------------
/docs/api/file/src/js/middleware.js.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | src/js/middleware.js | videojs-wavesurfer
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
28 |
29 |
42 |
43 | src/js/middleware.js
44 |
/**
45 | * @file middleware.js
46 | * @since 3.0.0
47 | */
48 |
49 | const WavesurferMiddleware = {
50 | /**
51 | * Setup the routing between a specific source and middleware
52 | * and eventually set the source on the Tech.
53 | *
54 | * @param {Tech~SourceObject} [srcObj] - Source object to manipulate.
55 | * @param {Function} [next] - The next middleware to run.
56 | */
57 | setSource(srcObj, next) {
58 | // check if this player is using the videojs-wavesurfer plugin
59 | if (this.player.usingPlugin('wavesurfer')) {
60 | let backend = this.player.wavesurfer().surfer.params.backend;
61 | let src = srcObj.src;
62 | let peaks = srcObj.peaks;
63 |
64 | switch (backend) {
65 | case 'WebAudio':
66 | // load url into wavesurfer
67 | this.player.wavesurfer().load(src);
68 | break;
69 |
70 | default:
71 | // load source into video.js
72 | next(null, srcObj);
73 |
74 | // load media element into wavesurfer
75 | let element = this.player.tech_.el();
76 | if (peaks === undefined) {
77 | // element without peaks
78 | this.player.wavesurfer().load(element);
79 | } else {
80 | // element with peaks
81 | this.player.wavesurfer().load(element, peaks);
82 | }
83 | break;
84 | }
85 | } else {
86 | // ignore middleware (this player isn't using the videojs-wavesurfer
87 | // plugin) and load source into video.js
88 | next(null, srcObj);
89 | }
90 | }
91 | };
92 |
93 | export default WavesurferMiddleware;
94 |
95 |
96 |
97 |
98 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
--------------------------------------------------------------------------------
/docs/api/file/src/js/utils/log.js.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | src/js/utils/log.js | videojs-wavesurfer
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
28 |
29 |
42 |
43 | src/js/utils/log.js
44 |
/**
45 | * @file log.js
46 | * @since 2.0.0
47 | */
48 |
49 | import videojs from 'video.js';
50 |
51 | const ERROR = 'error';
52 | const WARN = 'warn';
53 |
54 | /**
55 | * Log message (if the debug option is enabled).
56 | *
57 | * @private
58 | * @param {Array} args - The arguments to be passed to the matching console
59 | * method.
60 | * @param {string} logType - The name of the console method to use.
61 | * @param {boolean} debug - Whether or not the debug option is enabled or not.
62 | */
63 | const log = function(args, logType, debug)
64 | {
65 | if (debug === true) {
66 | if (logType === ERROR) {
67 | videojs.log.error(args);
68 | } else if (logType === WARN) {
69 | videojs.log.warn(args);
70 | } else {
71 | videojs.log(args);
72 | }
73 | }
74 | };
75 |
76 | export default log;
77 |
78 |
79 |
80 |
81 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/docs/api/image/badge.svg:
--------------------------------------------------------------------------------
1 |
18 |
--------------------------------------------------------------------------------
/docs/api/image/esdoc-logo-mini-black.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/collab-project/videojs-wavesurfer/cd3284bf8e2da59039fe59af8ce33003d69bd6ae/docs/api/image/esdoc-logo-mini-black.png
--------------------------------------------------------------------------------
/docs/api/image/esdoc-logo-mini.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/collab-project/videojs-wavesurfer/cd3284bf8e2da59039fe59af8ce33003d69bd6ae/docs/api/image/esdoc-logo-mini.png
--------------------------------------------------------------------------------
/docs/api/image/github.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/collab-project/videojs-wavesurfer/cd3284bf8e2da59039fe59af8ce33003d69bd6ae/docs/api/image/github.png
--------------------------------------------------------------------------------
/docs/api/image/manual-badge.svg:
--------------------------------------------------------------------------------
1 |
18 |
--------------------------------------------------------------------------------
/docs/api/image/search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/collab-project/videojs-wavesurfer/cd3284bf8e2da59039fe59af8ce33003d69bd6ae/docs/api/image/search.png
--------------------------------------------------------------------------------
/docs/api/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Home | videojs-wavesurfer
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
28 |
29 |
42 |
43 |
59 |
60 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/docs/api/lint.json:
--------------------------------------------------------------------------------
1 | []
--------------------------------------------------------------------------------
/docs/api/script/inherited-summary.js:
--------------------------------------------------------------------------------
1 | (function(){
2 | function toggle(ev) {
3 | var button = ev.target;
4 | var parent = ev.target.parentElement;
5 | while(parent) {
6 | if (parent.tagName === 'TABLE' && parent.classList.contains('summary')) break;
7 | parent = parent.parentElement;
8 | }
9 |
10 | if (!parent) return;
11 |
12 | var tbody = parent.querySelector('tbody');
13 | if (button.classList.contains('opened')) {
14 | button.classList.remove('opened');
15 | button.classList.add('closed');
16 | tbody.style.display = 'none';
17 | } else {
18 | button.classList.remove('closed');
19 | button.classList.add('opened');
20 | tbody.style.display = 'block';
21 | }
22 | }
23 |
24 | var buttons = document.querySelectorAll('.inherited-summary thead .toggle');
25 | for (var i = 0; i < buttons.length; i++) {
26 | buttons[i].addEventListener('click', toggle);
27 | }
28 | })();
29 |
--------------------------------------------------------------------------------
/docs/api/script/inner-link.js:
--------------------------------------------------------------------------------
1 | // inner link(#foo) can not correctly scroll, because page has fixed header,
2 | // so, I manually scroll.
3 | (function(){
4 | var matched = location.hash.match(/errorLines=([\d,]+)/);
5 | if (matched) return;
6 |
7 | function adjust() {
8 | window.scrollBy(0, -55);
9 | var el = document.querySelector('.inner-link-active');
10 | if (el) el.classList.remove('inner-link-active');
11 |
12 | // ``[ ] . ' " @`` are not valid in DOM id. so must escape these.
13 | var id = location.hash.replace(/([\[\].'"@$])/g, '\\$1');
14 | var el = document.querySelector(id);
15 | if (el) el.classList.add('inner-link-active');
16 | }
17 |
18 | window.addEventListener('hashchange', adjust);
19 |
20 | if (location.hash) {
21 | setTimeout(adjust, 0);
22 | }
23 | })();
24 |
25 | (function(){
26 | var els = document.querySelectorAll('[href^="#"]');
27 | var href = location.href.replace(/#.*$/, ''); // remove existed hash
28 | for (var i = 0; i < els.length; i++) {
29 | var el = els[i];
30 | el.href = href + el.getAttribute('href'); // because el.href is absolute path
31 | }
32 | })();
33 |
--------------------------------------------------------------------------------
/docs/api/script/manual.js:
--------------------------------------------------------------------------------
1 | (function(){
2 | var matched = location.pathname.match(/\/(manual\/.*\.html)$/);
3 | if (!matched) return;
4 |
5 | var currentName = matched[1];
6 | var cssClass = '.navigation .manual-toc li[data-link="' + currentName + '"]';
7 | var styleText = cssClass + '{ display: block; }\n';
8 | styleText += cssClass + '.indent-h1 a { color: #039BE5 }';
9 | var style = document.createElement('style');
10 | style.textContent = styleText;
11 | document.querySelector('head').appendChild(style);
12 | })();
13 |
--------------------------------------------------------------------------------
/docs/api/script/patch-for-local.js:
--------------------------------------------------------------------------------
1 | (function(){
2 | if (location.protocol === 'file:') {
3 | var elms = document.querySelectorAll('a[href="./"]');
4 | for (var i = 0; i < elms.length; i++) {
5 | elms[i].href = './index.html';
6 | }
7 | }
8 | })();
9 |
--------------------------------------------------------------------------------
/docs/api/script/pretty-print.js:
--------------------------------------------------------------------------------
1 | (function(){
2 | prettyPrint();
3 | var lines = document.querySelectorAll('.prettyprint.linenums li[class^="L"]');
4 | for (var i = 0; i < lines.length; i++) {
5 | lines[i].id = 'lineNumber' + (i + 1);
6 | }
7 |
8 | var matched = location.hash.match(/errorLines=([\d,]+)/);
9 | if (matched) {
10 | var lines = matched[1].split(',');
11 | for (var i = 0; i < lines.length; i++) {
12 | var id = '#lineNumber' + lines[i];
13 | var el = document.querySelector(id);
14 | el.classList.add('error-line');
15 | }
16 | return;
17 | }
18 |
19 | if (location.hash) {
20 | // ``[ ] . ' " @`` are not valid in DOM id. so must escape these.
21 | var id = location.hash.replace(/([\[\].'"@$])/g, '\\$1');
22 | var line = document.querySelector(id);
23 | if (line) line.classList.add('active');
24 | }
25 | })();
26 |
--------------------------------------------------------------------------------
/docs/api/script/search.js:
--------------------------------------------------------------------------------
1 | (function(){
2 | var searchIndex = window.esdocSearchIndex;
3 | var searchBox = document.querySelector('.search-box');
4 | var input = document.querySelector('.search-input');
5 | var result = document.querySelector('.search-result');
6 | var selectedIndex = -1;
7 | var prevText;
8 |
9 | // active search box and focus when mouse enter on search box.
10 | searchBox.addEventListener('mouseenter', function(){
11 | searchBox.classList.add('active');
12 | input.focus();
13 | });
14 |
15 | // search with text when key is upped.
16 | input.addEventListener('keyup', function(ev){
17 | var text = ev.target.value.toLowerCase();
18 | if (!text) {
19 | result.style.display = 'none';
20 | result.innerHTML = '';
21 | return;
22 | }
23 |
24 | if (text === prevText) return;
25 | prevText = text;
26 |
27 | var html = {class: [], method: [], member: [], function: [], variable: [], typedef: [], external: [], file: [], test: [], testFile: []};
28 | var len = searchIndex.length;
29 | var kind;
30 | for (var i = 0; i < len; i++) {
31 | var pair = searchIndex[i];
32 | if (pair[0].indexOf(text) !== -1) {
33 | kind = pair[3];
34 | html[kind].push('' + pair[2] + '');
35 | }
36 | }
37 |
38 | var innerHTML = '';
39 | for (kind in html) {
40 | var list = html[kind];
41 | if (!list.length) continue;
42 | innerHTML += '' + kind + '\n' + list.join('\n');
43 | }
44 | result.innerHTML = innerHTML;
45 | if (innerHTML) result.style.display = 'block';
46 | selectedIndex = -1;
47 | });
48 |
49 | // down, up and enter key are pressed, select search result.
50 | input.addEventListener('keydown', function(ev){
51 | if (ev.keyCode === 40) {
52 | // arrow down
53 | var current = result.children[selectedIndex];
54 | var selected = result.children[selectedIndex + 1];
55 | if (selected && selected.classList.contains('search-separator')) {
56 | var selected = result.children[selectedIndex + 2];
57 | selectedIndex++;
58 | }
59 |
60 | if (selected) {
61 | if (current) current.classList.remove('selected');
62 | selectedIndex++;
63 | selected.classList.add('selected');
64 | }
65 | } else if (ev.keyCode === 38) {
66 | // arrow up
67 | var current = result.children[selectedIndex];
68 | var selected = result.children[selectedIndex - 1];
69 | if (selected && selected.classList.contains('search-separator')) {
70 | var selected = result.children[selectedIndex - 2];
71 | selectedIndex--;
72 | }
73 |
74 | if (selected) {
75 | if (current) current.classList.remove('selected');
76 | selectedIndex--;
77 | selected.classList.add('selected');
78 | }
79 | } else if (ev.keyCode === 13) {
80 | // enter
81 | var current = result.children[selectedIndex];
82 | if (current) {
83 | var link = current.querySelector('a');
84 | if (link) location.href = link.href;
85 | }
86 | } else {
87 | return;
88 | }
89 |
90 | ev.preventDefault();
91 | });
92 |
93 | // select search result when search result is mouse over.
94 | result.addEventListener('mousemove', function(ev){
95 | var current = result.children[selectedIndex];
96 | if (current) current.classList.remove('selected');
97 |
98 | var li = ev.target;
99 | while (li) {
100 | if (li.nodeName === 'LI') break;
101 | li = li.parentElement;
102 | }
103 |
104 | if (li) {
105 | selectedIndex = Array.prototype.indexOf.call(result.children, li);
106 | li.classList.add('selected');
107 | }
108 | });
109 |
110 | // clear search result when body is clicked.
111 | document.body.addEventListener('click', function(ev){
112 | selectedIndex = -1;
113 | result.style.display = 'none';
114 | result.innerHTML = '';
115 | });
116 |
117 | })();
118 |
--------------------------------------------------------------------------------
/docs/api/script/test-summary.js:
--------------------------------------------------------------------------------
1 | (function(){
2 | function toggle(ev) {
3 | var button = ev.target;
4 | var parent = ev.target.parentElement;
5 | while(parent) {
6 | if (parent.tagName === 'TR' && parent.classList.contains('test-interface')) break;
7 | parent = parent.parentElement;
8 | }
9 |
10 | if (!parent) return;
11 |
12 | var direction;
13 | if (button.classList.contains('opened')) {
14 | button.classList.remove('opened');
15 | button.classList.add('closed');
16 | direction = 'closed';
17 | } else {
18 | button.classList.remove('closed');
19 | button.classList.add('opened');
20 | direction = 'opened';
21 | }
22 |
23 | var targetDepth = parseInt(parent.dataset.testDepth, 10) + 1;
24 | var nextElement = parent.nextElementSibling;
25 | while (nextElement) {
26 | var depth = parseInt(nextElement.dataset.testDepth, 10);
27 | if (depth >= targetDepth) {
28 | if (direction === 'opened') {
29 | if (depth === targetDepth) nextElement.style.display = '';
30 | } else if (direction === 'closed') {
31 | nextElement.style.display = 'none';
32 | var innerButton = nextElement.querySelector('.toggle');
33 | if (innerButton && innerButton.classList.contains('opened')) {
34 | innerButton.classList.remove('opened');
35 | innerButton.classList.add('closed');
36 | }
37 | }
38 | } else {
39 | break;
40 | }
41 | nextElement = nextElement.nextElementSibling;
42 | }
43 | }
44 |
45 | var buttons = document.querySelectorAll('.test-summary tr.test-interface .toggle');
46 | for (var i = 0; i < buttons.length; i++) {
47 | buttons[i].addEventListener('click', toggle);
48 | }
49 |
50 | var topDescribes = document.querySelectorAll('.test-summary tr[data-test-depth="0"]');
51 | for (var i = 0; i < topDescribes.length; i++) {
52 | topDescribes[i].style.display = '';
53 | }
54 | })();
55 |
--------------------------------------------------------------------------------
/docs/change-device.md:
--------------------------------------------------------------------------------
1 | # Change device
2 |
3 | ## Output
4 |
5 | ### Example
6 |
7 | - [online demo](https://collab-project.github.io/videojs-wavesurfer/demo/output.html)
8 | - [demo source](https://github.com/collab-project/videojs-wavesurfer/blob/master/examples/output.html)
9 |
10 |
11 | ### Usage
12 |
13 | If your device has multiple audio output devices, use `setAudioOutput(deviceId)` to change
14 | the active audio output device, and listen for the `audioOutputReady` event to be notified
15 | when the new output device is active.
16 |
17 | ```javascript
18 | // change audio output device
19 | player.wavesurfer().setAudioOutput(deviceId);
20 | ```
21 |
22 | ## Input
23 |
24 | If your device has multiple audio input devices and you want to display
25 | these devices and allow the user to choose one, check out the input example.
26 |
27 | #### Example
28 |
29 | - [online demo](https://collab-project.github.io/videojs-wavesurfer/demo/input.html)
30 | - [demo source](https://github.com/collab-project/videojs-wavesurfer/blob/master/examples/input.html)
31 |
--------------------------------------------------------------------------------
/docs/controls.md:
--------------------------------------------------------------------------------
1 | # Customizing controls
2 |
3 | To disable and hide specific controls, use the video.js `controlBar` option:
4 |
5 | ```javascript
6 | controlBar: {
7 | // hide fullscreen control
8 | fullscreenToggle: false
9 | }
10 | ```
11 |
12 | For more information, see the video.js [component options](https://github.com/videojs/video.js/blob/master/docs/guides/options.md#component-options).
13 |
--------------------------------------------------------------------------------
/docs/css/style.css:
--------------------------------------------------------------------------------
1 | .markdown-section code {
2 | color: #2e8a60;
3 | background-color: #e1f4f4;
4 | }
5 |
6 | .content {
7 | padding-top: 0px;
8 | }
9 |
10 | .markdown-section h2 {
11 | font-size: 1.75rem;
12 | margin: 25px 0 .8rem;
13 | }
14 |
15 | .sidebar-toggle {
16 | bottom: unset;
17 | top: 0;
18 | }
19 |
20 |
--------------------------------------------------------------------------------
/docs/demo/fluid.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Wavesurfer Plugin for Video.js Fluid Example
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
72 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/docs/demo/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Wavesurfer Plugin for Video.js Example
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
82 |
83 |
84 |
85 |
--------------------------------------------------------------------------------
/docs/demo/input.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Audio Input Example - Wavesurfer Plugin for Video.js
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
47 |
48 |
49 |
50 |
161 |
162 |
163 |
164 |
--------------------------------------------------------------------------------
/docs/demo/live.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Wavesurfer Microphone Plugin for Video.js Example
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
31 |
32 |
33 |
34 |
35 |
36 |
108 |
109 |
110 |
111 |
--------------------------------------------------------------------------------
/docs/demo/media/example.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/collab-project/videojs-wavesurfer/cd3284bf8e2da59039fe59af8ce33003d69bd6ae/docs/demo/media/example.mp4
--------------------------------------------------------------------------------
/docs/demo/media/hal.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/collab-project/videojs-wavesurfer/cd3284bf8e2da59039fe59af8ce33003d69bd6ae/docs/demo/media/hal.mp3
--------------------------------------------------------------------------------
/docs/demo/media/hal.vtt:
--------------------------------------------------------------------------------
1 | WEBVTT
2 |
3 | NOTE Paragraph
4 |
5 | 00:00:01.137 --> 00:00:05.250
6 | This mission is too important for me to allow you to jeopardize it
7 |
8 | 00:00:06.500 --> 00:00:08.002
9 | I don't know what you're talking about, Hal
10 |
--------------------------------------------------------------------------------
/docs/demo/media/hal.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/collab-project/videojs-wavesurfer/cd3284bf8e2da59039fe59af8ce33003d69bd6ae/docs/demo/media/hal.wav
--------------------------------------------------------------------------------
/docs/demo/multi.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | videojs-wavesurfer with multiple players on single page
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
37 |
38 |
88 |
89 |
90 |
--------------------------------------------------------------------------------
/docs/demo/output.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Audio Output Example - Wavesurfer Plugin for Video.js
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
45 |
46 |
47 |
48 |
154 |
155 |
156 |
157 |
--------------------------------------------------------------------------------
/docs/demo/peaks.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Peaks example - Wavesurfer Plugin for Video.js
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
82 |
83 |
84 |
85 |
--------------------------------------------------------------------------------
/docs/demo/plugin.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Plugin Example - Wavesurfer Plugin for Video.js
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
107 |
108 |
109 |
110 |
--------------------------------------------------------------------------------
/docs/demo/react/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Wavesurfer Plugin for Video.js React Example
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/docs/demo/react/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * React example.
3 | */
4 |
5 | class VideojsWavesurferPlayer extends React.Component {
6 | componentDidMount() {
7 | // instantiate Video.js
8 | this.player = videojs(this.audioNode, this.props, () => {
9 | // print version information at startup
10 | const version_info = 'Using video.js ' + videojs.VERSION +
11 | ' with videojs-wavesurfer ' + videojs.getPluginVersion('wavesurfer') +
12 | ' and wavesurfer.js ' + WaveSurfer.VERSION;
13 | videojs.log(version_info);
14 |
15 | // load file
16 | this.player.src({src: '../media/hal.wav', type: 'audio/wav'});
17 | });
18 |
19 | this.player.on('waveReady', (event) => {
20 | console.log('waveform: ready!');
21 | });
22 |
23 | this.player.on('playbackFinish', (event) => {
24 | console.log('playback finished.');
25 | });
26 |
27 | // error handling
28 | this.player.on('error', (element, error) => {
29 | console.warn(error);
30 | });
31 | }
32 |
33 | // destroy player on unmount
34 | componentWillUnmount() {
35 | if (this.player) {
36 | this.player.dispose();
37 | }
38 | }
39 |
40 | // wrap the player in a div with a `data-vjs-player` attribute
41 | // so videojs won't create additional wrapper in the DOM
42 | // see https://github.com/videojs/video.js/pull/3856
43 | render() {
44 | return (
45 |
46 |
47 |
48 | )
49 | }
50 | }
51 |
52 | const videoJsOptions = {
53 | controls: true,
54 | autoplay: false,
55 | fluid: false,
56 | width: 600,
57 | height: 300,
58 | bigPlayButton: false,
59 | plugins: {
60 | wavesurfer: {
61 | backend: 'MediaElement',
62 | displayMilliseconds: true,
63 | debug: true,
64 | waveColor: 'white',
65 | progressColor: 'black',
66 | cursorColor: 'black',
67 | hideScrollbar: true
68 | }
69 | }
70 | };
71 |
72 | ReactDOM.render(, document.getElementById('root'));
73 |
--------------------------------------------------------------------------------
/docs/demo/safari-workaround.js:
--------------------------------------------------------------------------------
1 | /* workaround safari issues when using the WebAudio backend in wavesurfer.js */
2 |
3 | var isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
4 |
5 | function addStartButton() {
6 | var btn = document.createElement('BUTTON');
7 | var t = document.createTextNode('Show player');
8 | btn.onclick = createPlayer;
9 | btn.appendChild(t);
10 | document.body.appendChild(btn);
11 | }
12 |
13 | function updateContext(opts) {
14 | // Safari 11 or newer automatically suspends new AudioContext's that aren't
15 | // created in response to a user-gesture, like a click or tap, so create one
16 | // here (inc. the script processor)
17 | var AudioContext = window.AudioContext || window.webkitAudioContext;
18 | var context = new AudioContext();
19 | var processor = context.createScriptProcessor(1024, 1, 1);
20 |
21 | opts.plugins.wavesurfer.audioContext = context;
22 | opts.plugins.wavesurfer.audioScriptProcessor = processor;
23 | }
24 |
25 | function enableTextTracks(opts) {
26 | // workaround for video.js issue with Safari text tracks
27 | // see https://github.com/videojs/video.js/issues/7015
28 | opts.html5 = {
29 | nativeTextTracks: false,
30 | vhs: {
31 | overrideNative: true
32 | }
33 | };
34 | }
35 |
--------------------------------------------------------------------------------
/docs/demo/texttrack.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Text tracks - Wavesurfer Plugin for Video.js Example
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
92 |
93 |
94 |
95 |
--------------------------------------------------------------------------------
/docs/demo/video.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Wavesurfer Plugin Video Example
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
86 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/docs/development.md:
--------------------------------------------------------------------------------
1 | # Development
2 |
3 | Install dependencies using npm:
4 |
5 | ```
6 | npm install
7 | ```
8 |
9 | Build development and minified versions of the library and stylesheets:
10 |
11 | ```
12 | npm run build
13 | ```
14 |
15 | Generated files are placed in the `dist` directory.
16 |
17 | During development:
18 |
19 | ```
20 | npm run start
21 | ```
22 |
23 | This will watch the source directory and rebuild when any changes
24 | are detected. It will also serve the files on http://127.0.0.1:8080.
25 |
26 | Generate the API documentation (placed in the `docs` directory):
27 |
28 | ```
29 | npm run docs
30 | ```
31 |
32 | All commands for development are listed in the `package.json` file and
33 | are run using:
34 |
35 | ```
36 | npm run
37 | ```
38 |
--------------------------------------------------------------------------------
/docs/donate.md:
--------------------------------------------------------------------------------
1 | # Donate
2 |
3 | Please consider donating if you like this project. Bitcoin is accepted
4 | and can be sent to `3PmXCqUggtq7KUWPbpN8WhMnb1Mfb1jbq8`.
5 |
--------------------------------------------------------------------------------
/docs/events.md:
--------------------------------------------------------------------------------
1 | # Events
2 |
3 | The events for this plugin are available on the video.js player instance.
4 |
5 | For example:
6 |
7 | ```javascript
8 | player.on('waveReady', function(event) {
9 | console.log('waveform is ready!');
10 | });
11 | ```
12 |
13 | | Event | Description |
14 | | ----- | ----------- |
15 | | `waveReady` | Audio is loaded, decoded and the waveform is drawn. |
16 | | `playbackFinish` | Audio playback finished. |
17 | | `audioOutputReady` | Audio output was changed and is now active. |
18 | | `abort` | Audio loading process was interrupted and cancelled. |
19 | | `error` | Error occurred. |
20 |
--------------------------------------------------------------------------------
/docs/examples.md:
--------------------------------------------------------------------------------
1 | # Examples
2 |
3 | ## Online demos
4 |
5 | View the examples online:
6 |
7 | | Example | Description | Source |
8 | | --- | --- | --- |
9 | | [Audio](https://collab-project.github.io/videojs-wavesurfer/demo/index.html) | Basic audio example | [example source](https://github.com/collab-project/videojs-wavesurfer/blob/master/examples/index.html) |
10 | | [Video](https://collab-project.github.io/videojs-wavesurfer/demo/video.html) | Basic video example | [example source](https://github.com/collab-project/videojs-wavesurfer/blob/master/examples/video.html) |
11 | | [Responsive](https://collab-project.github.io/videojs-wavesurfer/demo/fluid.html) | Enable [responsive layout](responsive.md) | [example source](https://github.com/collab-project/videojs-wavesurfer/blob/master/examples/fluid.html) |
12 | | [Text tracks](https://collab-project.github.io/videojs-wavesurfer/demo/texttrack.html) | Display [text tracks](text-tracks.md) | [example source](https://github.com/collab-project/videojs-wavesurfer/blob/master/examples/texttrack.html) |
13 | | [Microphone](https://collab-project.github.io/videojs-wavesurfer/demo/live.html) | Real-time waveform rendering of [microphone](microphone.md) | [example source](https://github.com/collab-project/videojs-wavesurfer/blob/master/examples/live.html) |
14 | | [Peaks](https://collab-project.github.io/videojs-wavesurfer/demo/peaks.html) | Use JSON [peaks data](peaks.md) to render waveform | [example source](https://github.com/collab-project/videojs-wavesurfer/blob/master/examples/peaks.html) |
15 | | [Output device](https://collab-project.github.io/videojs-wavesurfer/demo/output.html) | Change audio [output device](change-device.md) | [example source](https://github.com/collab-project/videojs-wavesurfer/blob/master/examples/output.html) |
16 | | [Input device](https://collab-project.github.io/videojs-wavesurfer/demo/input.html) | Change audio [input device](change-device.md) | [example source](https://github.com/collab-project/videojs-wavesurfer/blob/master/examples/input.html) |
17 | | [Plugin](https://collab-project.github.io/videojs-wavesurfer/demo/plugin.html) | Enable additional [wavesurfer.js plugins](plugins.md) | [example source](https://github.com/collab-project/videojs-wavesurfer/blob/master/examples/plugin.html) |
18 | | [React](https://collab-project.github.io/videojs-wavesurfer/demo/react/index.html) | Basic [React](react.md) example | [example source](https://github.com/collab-project/videojs-wavesurfer/blob/master/examples/react/index.html) |
19 | | [Multi](https://collab-project.github.io/videojs-wavesurfer/demo/multi.html) | Using multiple players on single page | [example source](https://github.com/collab-project/videojs-wavesurfer/blob/master/examples/multi.html) |
20 |
21 | ## Local setup
22 |
23 | To try out the examples locally either:
24 |
25 | - download the [zipfile](https://github.com/collab-project/videojs-wavesurfer/archive/master.zip) and unpack it
26 | - or checkout the repository with Git:
27 | ```console
28 | git clone https://github.com/collab-project/videojs-wavesurfer.git
29 | ```
30 |
31 | 1. Install the dependencies:
32 |
33 | ```console
34 | cd /path/to/videojs-wavesurfer
35 | npm install
36 | ```
37 |
38 | 2. Build the library and assets once:
39 |
40 | ```console
41 | npm run build
42 | ```
43 |
44 | 3. And start the local examples webserver:
45 |
46 | ```console
47 | npm run start
48 | ```
49 |
50 | Open http://localhost:8080/examples/index.html in a browser.
51 |
--------------------------------------------------------------------------------
/docs/img/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/collab-project/videojs-wavesurfer/cd3284bf8e2da59039fe59af8ce33003d69bd6ae/docs/img/screenshot.png
--------------------------------------------------------------------------------
/docs/img/text-tracks.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/collab-project/videojs-wavesurfer/cd3284bf8e2da59039fe59af8ce33003d69bd6ae/docs/img/text-tracks.png
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | videojs-wavesurfer
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/docs/install.md:
--------------------------------------------------------------------------------
1 | # Installation
2 |
3 | ## NPM
4 |
5 | You can use [npm](https://www.npmjs.org) to install the plugin:
6 |
7 | ```
8 | npm install videojs-wavesurfer
9 | ```
10 |
11 | Or [download it from Github](https://github.com/collab-project/videojs-wavesurfer/releases).
12 |
13 | ## CDN
14 |
15 | Using the [unpkg.com](https://unpkg.com) CDN:
16 |
17 | ```html
18 |
19 |
20 |
21 |
22 |
23 | ```
24 |
25 | Alternative CDN locations:
26 |
27 | - https://cdn.jsdelivr.net/npm/videojs-wavesurfer/
28 | - https://cdnjs.com/libraries/videojs-wavesurfer
29 | - https://www.bootcdn.cn/videojs-wavesurfer/
30 |
31 | ## Upgrade
32 |
33 | Since v3.0 this plugin is compatible with:
34 |
35 | - video.js 7.0.5 or newer
36 | - wavesurfer.js 6.3.0 or newer. wavesurfer.js 7.x is not supported.
37 |
38 | If you want to use this plugin with an older video.js or wavesurfer.js version,
39 | check the [archived releases](https://github.com/collab-project/videojs-wavesurfer/releases)
40 | for an older release of this plugin.
41 |
42 | Also take a look at the [changelog](changelog.md) when upgrading from a previous
43 | version of videojs-wavesurfer.
44 |
--------------------------------------------------------------------------------
/docs/methods.md:
--------------------------------------------------------------------------------
1 | # Methods
2 |
3 | Methods for this plugin are documented below. These are available on the
4 | `wavesurfer` plugin instance of the video.js player.
5 |
6 | For example:
7 |
8 | ```javascript
9 | player.on('ready', function() {
10 | player.wavesurfer().destroy();
11 | });
12 | ```
13 |
14 | # Plugin
15 |
16 | | Method | Description |
17 | | ------ | ----------- |
18 | | `destroy` | Destroys the wavesurfer instance and children (including the video.js player). |
19 | | `load(url)` | Load the clip at `url`. Also supports loading [File](https://developer.mozilla.org/nl/docs/Web/API/File) or [Blob](https://developer.mozilla.org/nl/docs/Web/API/Blob) objects. |
20 | | `setVolume(level)` | Set the volume level (value between 0.0 and 1.0). |
21 | | `play` | Start playback. |
22 | | `pause` | Pause playback. |
23 | | `getDuration` | Get the length of the stream in seconds. Returns 0 if no stream is available (yet). |
24 | | `getCurrentTime` | Get the current time (in seconds) of the stream during playback. Returns 0 if no stream is available (yet). |
25 | | `setFormatTime(impl)` | Change the current `formatTime` implementation with a custom implementation. |
26 | | `exportImage(format, quality, type)` | Save waveform image as Blob or data URI. Default `format` is `'image/png'`, `quality` is 1 and `type` is `blob`. |
27 | | `setAudioOutput(deviceId)` | Change the audio output device using its [deviceId](https://developer.mozilla.org/en-US/docs/Web/API/MediaDeviceInfo/deviceId). |
28 |
29 | ## wavesurfer.js
30 |
31 | You can access the wavesurfer instance, for example to call the
32 | wavesurfer.js `seekTo` method, by using the `surfer` property of the
33 | `wavesurfer` plugin instance:
34 |
35 | ```javascript
36 | player.wavesurfer().surfer.seekTo(1);
37 | ```
38 |
--------------------------------------------------------------------------------
/docs/microphone.md:
--------------------------------------------------------------------------------
1 | # Microphone plugin
2 |
3 | It's also possible to use a microphone for real-time rendering of the audio waveform. This
4 | uses the [microphone plugin](https://wavesurfer-js.org/plugins/microphone.html) that comes
5 | with wavesurfer.js.
6 |
7 | ## Example
8 |
9 | - [online demo](https://collab-project.github.io/videojs-wavesurfer/demo/live.html)
10 | - [demo source](https://github.com/collab-project/videojs-wavesurfer/blob/master/examples/live.html)
11 |
12 | ## Usage
13 |
14 | Include the additional `wavesurfer.microphone.js` plugin on your page.
15 |
16 | ```html
17 |
18 | ```
19 |
20 | Add an `audio` element:
21 |
22 | ```html
23 |
24 | ```
25 |
26 | Hide irrelevant controls, specify the `WebAudio` backend and enable the microphone plugin:
27 |
28 | ```javascript
29 | let options = {
30 | controls: true,
31 | width: 600,
32 | height: 300,
33 | // hide irrelevant controls
34 | bigPlayButton: false,
35 | controlBar: {
36 | currentTimeDisplay: false,
37 | timeDivider: false,
38 | durationDisplay: false,
39 | remainingTimeDisplay: false,
40 | volumePanel: false,
41 | progressControl: false
42 | },
43 | plugins: {
44 | // enable videojs-wavesurfer plugin
45 | wavesurfer: {
46 | debug: true,
47 | backend: 'WebAudio',
48 | waveColor: 'black',
49 | cursorWidth: 0,
50 | interact: false,
51 | hideScrollbar: true,
52 | plugins: [
53 | // enable microphone plugin
54 | WaveSurfer.microphone.create({
55 | bufferSize: 4096,
56 | numberOfInputChannels: 1,
57 | numberOfOutputChannels: 1,
58 | constraints: {
59 | video: false,
60 | audio: true
61 | }
62 | })
63 | ]
64 | }
65 | }
66 | };
67 |
68 | let player = videojs('myLiveAudio', options);
69 | ```
70 |
71 | The wavesurfer.js microphone plugin has additional configuration
72 | [options](https://wavesurfer-js.org/plugins/microphone.html).
73 |
--------------------------------------------------------------------------------
/docs/options.md:
--------------------------------------------------------------------------------
1 | # Options
2 |
3 | Configure the player with:
4 |
5 | - [video.js options](https://github.com/videojs/video.js/blob/master/docs/guides/options.md)
6 | - [wavesurfer.js options](https://wavesurfer-js.org/docs/options.html)
7 |
8 | Additional options for this plugin are:
9 |
10 | | option | type | default | description |
11 | | ------ | ---- | ------- | ----------- |
12 | | `debug` | boolean | `false` | Display internal log messages using the `videojs.log` method. |
13 | | `displayMilliseconds` | boolean | `true` | Indicates if milliseconds should be included in time displays, e.g. `00:00:000` vs `00:00`. |
14 | | `formatTime` | function | builtin `formatTime` | Use a custom time format function. For example: ```(seconds, guide) => `test:${seconds}:${guide} ``` |
15 |
--------------------------------------------------------------------------------
/docs/peaks.md:
--------------------------------------------------------------------------------
1 | # Using peaks for large audio files
2 |
3 | When you're dealing with long audio files, it's sometimes useful to generate the waveform data,
4 | called peaks, on the server. This allows wavesurfer.js to load the peaks JSON data and create the
5 | waveform from that pre-rendered peak data. This JSON file can be generated using the
6 | [bbc/audiowaveform](https://github.com/bbc/audiowaveform) utility.
7 |
8 | For more information, see the wavesurfer.js [FAQ](https://wavesurfer-js.org/faq/).
9 |
10 | ## Example
11 |
12 | - [online demo](https://collab-project.github.io/videojs-wavesurfer/demo/peaks.html)
13 | - [demo source](https://github.com/collab-project/videojs-wavesurfer/blob/master/examples/peaks.html)
14 |
15 | ## Usage
16 |
17 | Load peaks data:
18 |
19 | ```javascript
20 | // load file with peaks
21 | player.src({
22 | src: 'media/hal.wav',
23 | type: 'audio/wav',
24 | // Use peaks from JSON file. See https://wavesurfer-js.org/faq/
25 | // for instructions on how to generate peaks
26 | peaks: 'media/hal-peaks.json'
27 | });
28 | ```
29 |
--------------------------------------------------------------------------------
/docs/plugins.md:
--------------------------------------------------------------------------------
1 | # More features using other plugins
2 |
3 | ## video.js
4 |
5 | The Video.js community created
6 | [lots of plugins](https://github.com/videojs/video.js/wiki/Plugins)
7 | that can be used to enhance the player's functionality.
8 |
9 | Plugins actually tested with videojs-wavesurfer include:
10 |
11 | - [videojs-record](https://github.com/collab-project/videojs-record) - Adds
12 | support for recording audio/video/image files.
13 |
14 | ## wavesurfer.js
15 |
16 | The plugin example extends videojs-wavesurfer with the wavesurfer.js
17 | [timeline](https://wavesurfer-js.org/example/timeline/index.html) and
18 | [regions](https://wavesurfer-js.org/example/regions/index.html) plugins:
19 |
20 | - [online demo](https://collab-project.github.io/videojs-wavesurfer/demo/plugin.html)
21 | - [demo source](https://github.com/collab-project/videojs-wavesurfer/blob/master/examples/plugin.html)
22 |
--------------------------------------------------------------------------------
/docs/react.md:
--------------------------------------------------------------------------------
1 | # React
2 |
3 | This guide shows you how to get started with [React](https://reactjs.org) and
4 | videojs-wavesurfer using [create-react-app](https://github.com/facebook/create-react-app).
5 |
6 | For more information, check the video.js [documentation](https://videojs.com/guides/react/)
7 | for React.
8 |
9 | ## Installation
10 |
11 | Create an example React application called `videojs-wavesurfer-react`:
12 |
13 | ```console
14 | npx create-react-app videojs-wavesurfer-react
15 | ```
16 |
17 | Install videojs-wavesurfer:
18 |
19 | ```console
20 | cd videojs-wavesurfer-react
21 | npm install --save videojs-wavesurfer
22 | ```
23 |
24 | ## Application
25 |
26 | Replace content of `src/App.js` with:
27 |
28 | ```javascript
29 | import './App.css';
30 | import React from 'react';
31 |
32 | import VideoJSComponent from './VideoJSComponent';
33 |
34 | function App() {
35 | const playerRef = React.useRef(null);
36 | const videoJsOptions = {
37 | controls: true,
38 | bigPlayButton: false,
39 | inactivityTimeout: 0,
40 | width: 600,
41 | height: 300,
42 | fluid: false,
43 | plugins: {
44 | wavesurfer: {
45 | backend: 'MediaElement',
46 | displayMilliseconds: true,
47 | debug: true,
48 | waveColor: '#163b5b',
49 | progressColor: 'black',
50 | cursorColor: 'black',
51 | hideScrollbar: true
52 | }
53 | }
54 | };
55 |
56 | const handlePlayerReady = (player) => {
57 | playerRef.current = player;
58 |
59 | // handle player events
60 | player.on('waveReady', (event) => {
61 | console.log('waveform: ready!');
62 | });
63 |
64 | player.on('playbackFinish', (event) => {
65 | console.log('playback finished.');
66 | });
67 |
68 | // error handling
69 | player.on('error', (element, error) => {
70 | console.error(error);
71 | });
72 | };
73 |
74 | return (
75 |
76 |
77 |
78 | );
79 | }
80 |
81 | export default App;
82 | ```
83 |
84 | Add the following to `src/App.css`:
85 |
86 | ```css
87 | /* change player background color */
88 | .App video-js {
89 | background-color: #ACB2F2;
90 | }
91 | ```
92 |
93 | Create `src/VideoJSComponent.js`:
94 |
95 | ```javascript
96 | import React from 'react';
97 | import videojs from 'video.js';
98 | import 'video.js/dist/video-js.css';
99 | import WaveSurfer from 'wavesurfer.js';
100 |
101 | /*
102 | // the following imports are only needed when you're using
103 | // the microphone plugin
104 | import 'webrtc-adapter';
105 |
106 | import MicrophonePlugin from 'wavesurfer.js/dist/plugin/wavesurfer.microphone.js';
107 | WaveSurfer.microphone = MicrophonePlugin;
108 | */
109 |
110 | // register videojs-wavesurfer plugin with this import
111 | import 'videojs-wavesurfer/dist/css/videojs.wavesurfer.css';
112 | import Wavesurfer from 'videojs-wavesurfer/dist/videojs.wavesurfer.js';
113 |
114 | export const VideoJSComponent = (props) => {
115 | const videoRef = React.useRef(null);
116 | const playerRef = React.useRef(null);
117 | const {options, onReady} = props;
118 |
119 | React.useEffect(() => {
120 |
121 | // Make sure Video.js player is only initialized once
122 | if (!playerRef.current) {
123 | // The Video.js player needs to be _inside_ the component el for React 18 Strict Mode.
124 | const videoElement = document.createElement("video-js");
125 |
126 | videoElement.className = 'video-js vjs-default-skin';
127 | videoRef.current.appendChild(videoElement);
128 |
129 | const player = playerRef.current = videojs(videoElement, options, () => {
130 | // print version information at startup
131 | const version_info = 'Using video.js ' + videojs.VERSION +
132 | ' with videojs-wavesurfer ' + videojs.getPluginVersion('wavesurfer') +
133 | ', wavesurfer.js ' + WaveSurfer.VERSION + ' and React ' + React.version;
134 | videojs.log(version_info);
135 |
136 | onReady && onReady(player);
137 |
138 | // load track
139 | player.src({src: 'hal.wav', type: 'audio/wav'});
140 | });
141 |
142 | // You could update an existing player in the `else` block here
143 | // on prop change, for example:
144 | } else {
145 | //const player = playerRef.current;
146 | //player.src({src: 'hal.wav', type: 'audio/wav'});
147 | }
148 | }, [options, videoRef]);
149 |
150 | // Dispose the Video.js player when the functional component unmounts
151 | React.useEffect(() => {
152 | const player = playerRef.current;
153 |
154 | return () => {
155 | if (player && !player.isDisposed()) {
156 | player.dispose();
157 | playerRef.current = null;
158 | }
159 | };
160 | }, [playerRef]);
161 |
162 | return (
163 |
166 | );
167 | }
168 |
169 | export default VideoJSComponent;
170 | ```
171 |
172 | ## Media
173 |
174 | Download the [example audio file](https://github.com/collab-project/videojs-wavesurfer/raw/master/examples/media/hal.wav)
175 | and place it in the `public` directory.
176 |
177 | ## Run
178 |
179 | Start the development server:
180 |
181 | ```console
182 | npm start
183 | ```
184 |
185 | And open http://localhost:3000 in a browser.
186 |
--------------------------------------------------------------------------------
/docs/responsive.md:
--------------------------------------------------------------------------------
1 | # Responsive layout
2 |
3 | The `fluid` option for video.js will resize the player according to the size
4 | of the window.
5 |
6 | ## Example
7 |
8 | - [online demo](https://collab-project.github.io/videojs-wavesurfer/demo/fluid.html)
9 | - [demo source](https://github.com/collab-project/videojs-wavesurfer/blob/master/examples/fluid.html)
10 |
11 | ## Usage
12 |
13 | Configure the player and enable the video.js `fluid` option:
14 |
15 | ```javascript
16 | fluid: true
17 | ```
18 |
19 | For more information, see the video.js [layout documentation](https://github.com/videojs/video.js/blob/master/docs/guides/layout.md).
20 |
--------------------------------------------------------------------------------
/docs/text-tracks.md:
--------------------------------------------------------------------------------
1 | # Text Tracks
2 |
3 | Text tracks (or captions/subtitles) are a feature of HTML5 for displaying
4 | time-triggered text to the user. Video.js offers a cross-browser implementation
5 | of text tracks.
6 |
7 | For more information, check the video.js
8 | [text tracks documentation](https://github.com/videojs/video.js/blob/master/docs/guides/text-tracks.md).
9 |
10 | ## Example
11 |
12 | - [online demo](https://collab-project.github.io/videojs-wavesurfer/demo/texttrack.html)
13 | - [demo source](https://github.com/collab-project/videojs-wavesurfer/blob/master/examples/texttrack.html)
14 |
15 | ## Usage
16 |
17 | Create an array for the text track(s) you want to use:
18 |
19 | ```javascript
20 | let textTracks = [
21 | {
22 | kind: 'captions',
23 | srclang: 'en',
24 | label: 'English',
25 | src: 'media/hal.vtt',
26 | mode: 'showing',
27 | default: true
28 | }
29 | ];
30 | ```
31 |
32 | And pass it to the video.js `tracks` option:
33 |
34 | ```javascript
35 | const options = {
36 | tracks: textTracks,
37 | plugins: {
38 | // etc...
39 | }
40 | };
41 | ```
42 |
43 | 
44 |
--------------------------------------------------------------------------------
/docs/tools/update-videojs.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Update video.js version in demo files.
3 | *
4 | * @since 3.0.0
5 | */
6 |
7 | const replace = require('replace-in-file');
8 | const path = require('path');
9 |
10 | const OLD_VERSION = "video.js@7.21.1";
11 | const NEW_VERSION = "video.js@8.10.0";
12 |
13 | const options = {
14 | files: path.resolve(__dirname, '..', 'demo') + '/**/*.html',
15 | from: new RegExp(OLD_VERSION, 'g'),
16 | to: NEW_VERSION,
17 | dry: false
18 | };
19 |
20 | console.log();
21 | console.log(`Updating from ${OLD_VERSION} to ${NEW_VERSION} in ${path.relative('.', options.files)}`);
22 | console.log();
23 |
24 | replace(options)
25 | .then(results => {
26 | let changes = false;
27 | results.forEach(item => {
28 | if (item.hasChanged) {
29 | console.log("Updated " + path.relative('.', item.file));
30 | changes = true;
31 | }
32 | });
33 | if (!changes) {
34 | console.log("No files updated.");
35 | }
36 | })
37 | .catch(error => {
38 | console.error('Error occurred:', error);
39 | });
40 |
--------------------------------------------------------------------------------
/docs/usage.md:
--------------------------------------------------------------------------------
1 | # Usage
2 |
3 | The plugin depends on the video.js and wavesurfer.js 6.x libraries:
4 |
5 | ```javascript
6 | // style
7 | import 'video.js/dist/video-js.min.css';
8 | import 'videojs-wavesurfer/dist/css/videojs.wavesurfer.css';
9 |
10 | // libraries
11 | import videojs from 'video.js';
12 | import WaveSurfer from 'wavesurfer.js';
13 | ```
14 |
15 | The videojs-wavesurfer plugin automatically registers itself after importing it:
16 |
17 | ```javascript
18 | import Wavesurfer from 'videojs-wavesurfer/dist/videojs.wavesurfer.js';
19 | ```
20 |
21 | Add an `audio` element:
22 |
23 | ```html
24 |
25 | ```
26 |
27 | Or `video` element:
28 |
29 | ```html
30 |
31 | ```
32 |
33 | Define the player configuration and enable the videojs-wavesurfer plugin by
34 | adding a `wavesurfer` entry:
35 |
36 | ```javascript
37 | // configuration for video.js
38 | let options = {
39 | controls: true,
40 | bigPlayButton: false,
41 | autoplay: false,
42 | loop: false,
43 | fluid: false,
44 | width: 600,
45 | height: 300,
46 | plugins: {
47 | // enable videojs-wavesurfer plugin
48 | wavesurfer: {
49 | // configure videojs-wavesurfer
50 | backend: 'MediaElement',
51 | displayMilliseconds: true,
52 | debug: true,
53 | waveColor: 'grey',
54 | progressColor: 'black',
55 | cursorColor: 'black',
56 | hideScrollbar: true
57 | }
58 | }
59 | };
60 | ```
61 |
62 | Finally, create the player and load a file:
63 |
64 | ```javascript
65 | let player = videojs('myClip', options, function() {
66 | // print version information at startup
67 | let msg = 'Using video.js ' + videojs.VERSION +
68 | ' with videojs-wavesurfer ' +
69 | videojs.getPluginVersion('wavesurfer') +
70 | ' and wavesurfer.js ' + WaveSurfer.VERSION;
71 | videojs.log(msg);
72 |
73 | // load wav file from url
74 | player.src({src: 'media/hal.wav', type: 'audio/wav'});
75 | });
76 | ```
77 |
78 | Check the [options](options.md), [methods](methods.md) and [events](events.md) documentation
79 | for more information.
80 |
--------------------------------------------------------------------------------
/docs/vue.md:
--------------------------------------------------------------------------------
1 | # Vue
2 |
3 | This page shows how to get started with [Vue.js](https://vuejs.org/) and videojs-wavesurfer.
4 |
5 | For more information, check the video.js [documentation](https://videojs.com/guides/vue/)
6 | for Vue.js.
7 |
8 | ## Installation
9 |
10 | Install the [Vue.js CLI](https://cli.vuejs.org/guide/) globally:
11 |
12 | ```console
13 | npm install -g @vue/cli
14 | ```
15 |
16 | Create a new application, e.g. `videojs-wavesurfer-app`:
17 |
18 | ```console
19 | vue create --default --packageManager npm videojs-wavesurfer-app
20 | ```
21 |
22 | Install videojs-wavesurfer:
23 |
24 | ```console
25 | cd videojs-wavesurfer-app
26 | npm install --save videojs-wavesurfer
27 | ```
28 |
29 | ## Application
30 |
31 | Create `src/components/VideoJSWavesurfer.vue`:
32 |
33 | ```html
34 |
35 |
36 |
37 |
38 |
108 | ```
109 |
110 | Change `src/App.vue` to:
111 |
112 | ```html
113 |
114 |
115 |
116 |
117 |
118 |
119 |
129 |
130 |
136 | ```
137 |
138 | ## Media
139 |
140 | Download the [example audio file](https://github.com/collab-project/videojs-wavesurfer/raw/master/examples/media/hal.wav)
141 | and place it in the `public` directory.
142 |
143 | ## Run
144 |
145 | Start the Vue.js development server:
146 |
147 | ```console
148 | npm run serve
149 | ```
150 |
151 | And open http://localhost:8080 in a browser.
152 |
--------------------------------------------------------------------------------
/docs/webpack.md:
--------------------------------------------------------------------------------
1 | # Webpack
2 |
3 | This document describes how to setup [Webpack](https://webpack.js.org/) with videojs-wavesurfer.
4 |
5 | ## Installation
6 |
7 | Create a project directory:
8 |
9 | ```console
10 | mkdir videojs-wavesurfer-webpack
11 | cd videojs-wavesurfer-webpack
12 | ```
13 |
14 | Install Webpack:
15 |
16 | ```console
17 | npm install -D webpack webpack-dev-server webpack-cli css-loader style-loader
18 | ```
19 |
20 | Install videojs-wavesurfer:
21 |
22 | ```console
23 | npm install --save videojs-wavesurfer
24 | ```
25 |
26 | ## Configuration
27 |
28 | Create the Webpack config file called `webpack.config.js`:
29 |
30 | ```javascript
31 | const path = require('path');
32 | const basePath = path.resolve(__dirname);
33 |
34 | module.exports = {
35 | mode: 'development',
36 | context: path.join(basePath, 'src'),
37 | entry: {
38 | app: './app.js'
39 | },
40 | output: {
41 | path: path.join(basePath, 'dist'),
42 | filename: '[name].bundle.js',
43 | publicPath: '/dist'
44 | },
45 | devServer: {
46 | static: {
47 | directory: basePath,
48 | serveIndex: true,
49 | watch: true,
50 | }
51 | },
52 | module: {
53 | rules: [{
54 | test: /\.css$/,
55 | use: ['style-loader', 'css-loader'],
56 | }]
57 | }
58 | };
59 | ```
60 |
61 | ## Application
62 |
63 | Create `src/index.html` containing:
64 |
65 | ```html
66 |
67 |
68 |
69 |
70 | Webpack videojs-wavesurfer example
71 |
72 |
73 |
74 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 | ```
88 |
89 | And create `src/app.js`:
90 |
91 | ```javascript
92 | import video_css from 'video.js/dist/video-js.min.css';
93 | import videojs from 'video.js';
94 | import WaveSurfer from 'wavesurfer.js';
95 |
96 | /*
97 | // the following imports are only required when using the
98 | // videojs-wavesurfer 'live' mode with the microphone plugin
99 | // make sure to import them before importing videojs-wavesurfer
100 | import 'webrtc-adapter';
101 | import MicrophonePlugin from 'wavesurfer.js/dist/plugin/wavesurfer.microphone.js';
102 | WaveSurfer.microphone = MicrophonePlugin;
103 | */
104 |
105 | // register videojs-wavesurfer plugin
106 | import wavesurfer_css from 'videojs-wavesurfer/dist/css/videojs.wavesurfer.css';
107 | import Wavesurfer from 'videojs-wavesurfer/dist/videojs.wavesurfer.js';
108 |
109 | let player;
110 | const elementId = 'myAudio';
111 | let src = {src: '/hal.wav', type: 'audio/wav'};
112 |
113 | const playerOptions = {
114 | controls: true,
115 | bigPlayButton: false,
116 | autoplay: false,
117 | fluid: false,
118 | loop: false,
119 | width: 600,
120 | height: 300,
121 | plugins: {
122 | // configure videojs-wavesurfer plugin
123 | wavesurfer: {
124 | backend: 'MediaElement',
125 | displayMilliseconds: true,
126 | debug: true,
127 | waveColor: '#4A4A22',
128 | progressColor: 'black',
129 | cursorColor: 'black',
130 | hideScrollbar: true
131 | }
132 | }
133 | };
134 |
135 | // wait till DOM is ready
136 | document.addEventListener('DOMContentLoaded', function() {
137 | // create player
138 | player = videojs(elementId, playerOptions, function() {
139 | console.log('player ready! id:', elementId);
140 |
141 | // print version information at startup
142 | const msg = 'Using video.js ' + videojs.VERSION +
143 | ' with videojs-wavesurfer ' + videojs.getPluginVersion('wavesurfer') +
144 | ' and wavesurfer.js ' + WaveSurfer.VERSION;
145 | videojs.log(msg);
146 |
147 | // load file
148 | player.src(src);
149 | });
150 |
151 | player.on('waveReady', function(event) {
152 | console.log('waveform is ready!');
153 | });
154 |
155 | player.on('playbackFinish', function(event) {
156 | console.log('playback finished.');
157 | });
158 |
159 | // error handling
160 | player.on('error', function(element, error) {
161 | console.error('ERROR:', error);
162 | });
163 | });
164 | ```
165 |
166 | ## Media
167 |
168 | Download the [example audio file](https://github.com/collab-project/videojs-wavesurfer/raw/master/examples/media/hal.wav)
169 | and place it in the root directory.
170 |
171 | ## Run
172 |
173 | Start the Webpack development server:
174 |
175 | ```
176 | ./node_modules/.bin/webpack serve --config=webpack.config.js
177 | ```
178 |
179 | And open http://localhost:8080/src/index.html in a browser.
180 |
--------------------------------------------------------------------------------
/eslint.config.js:
--------------------------------------------------------------------------------
1 | const jsdoc = require('eslint-plugin-jsdoc');
2 |
3 | module.exports = [
4 | {
5 | // configuration included in plugin
6 | //jsdoc.configs['flat/recommended'],
7 | "languageOptions": {
8 | "ecmaVersion": 6,
9 | "sourceType": "module"
10 | },
11 | "plugins": {jsdoc: jsdoc},
12 | "rules": {
13 | "no-trailing-spaces": 2,
14 | "no-mixed-spaces-and-tabs": 2,
15 | "no-multi-spaces": 2,
16 | "no-tabs": 2,
17 | "no-extra-bind": 2,
18 | "eqeqeq": 2,
19 | "indent": [
20 | 2,
21 | 4,
22 | {
23 | "SwitchCase": 1
24 | }
25 | ],
26 | "semi-spacing": [
27 | 2,
28 | {
29 | "before": false,
30 | "after": true
31 | }
32 | ],
33 | "semi": [
34 | 2,
35 | "always"
36 | ],
37 | "comma-dangle": ["error", "never"],
38 | "keyword-spacing": ["error", { "before": true }],
39 | "space-infix-ops": 2,
40 | "prefer-arrow-callback": 2,
41 | "jsdoc/require-return": "off",
42 | "jsdoc/require-returntype": "off",
43 | "no-console": 2,
44 | "no-dupe-args": 2,
45 | "no-dupe-keys": 2,
46 | "no-extra-semi": 2,
47 | "no-fallthrough": 2,
48 | "use-isnan": 2,
49 | "valid-typeof": 2,
50 | "no-var": 2
51 | },
52 | },
53 | {
54 | files: ["test-*.js", "*.spec.js", "test-helpers.js"],
55 | rules: {
56 | "valid-jsdoc": "off",
57 | "require-jsdoc": "off"
58 | }
59 | },
60 | {
61 | files: ["build-config/**/*.js"],
62 | rules: {
63 | "no-console": "off"
64 | }
65 | }
66 | ];
67 |
--------------------------------------------------------------------------------
/examples/fluid.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Fluid Example - Wavesurfer Plugin for Video.js
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
75 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/examples/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Wavesurfer Plugin for Video.js Example
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
77 |
78 |
79 |
80 |
--------------------------------------------------------------------------------
/examples/live.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Microphone Example - Wavesurfer Plugin for Video.js
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
31 |
32 |
33 |
34 |
35 |
36 |
114 |
115 |
116 |
117 |
--------------------------------------------------------------------------------
/examples/media/example.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/collab-project/videojs-wavesurfer/cd3284bf8e2da59039fe59af8ce33003d69bd6ae/examples/media/example.mp4
--------------------------------------------------------------------------------
/examples/media/hal.vtt:
--------------------------------------------------------------------------------
1 | WEBVTT
2 |
3 | NOTE Paragraph
4 |
5 | 00:00:01.123 --> 00:00:05.250
6 | This mission is too important for me to allow you to jeopardize it
7 |
8 | 00:00:06.485 --> 00:00:08.002
9 | I don't know what you're talking about, Hal
10 |
--------------------------------------------------------------------------------
/examples/media/hal.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/collab-project/videojs-wavesurfer/cd3284bf8e2da59039fe59af8ce33003d69bd6ae/examples/media/hal.wav
--------------------------------------------------------------------------------
/examples/multi.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | videojs-wavesurfer with multiple players on single page
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
37 |
38 |
88 |
89 |
90 |
--------------------------------------------------------------------------------
/examples/output.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Audio Output Example - Wavesurfer Plugin for Video.js
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
45 |
46 |
47 |
48 |
159 |
160 |
161 |
162 |
--------------------------------------------------------------------------------
/examples/peaks.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Peaks Example - Wavesurfer Plugin for Video.js
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
76 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/examples/plugin.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Plugin Example - Wavesurfer Plugin for Video.js
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
111 |
112 |
113 |
114 |
--------------------------------------------------------------------------------
/examples/react/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Wavesurfer Plugin for Video.js React Example
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/examples/react/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * React example.
3 | */
4 |
5 | class VideojsWavesurferPlayer extends React.Component {
6 | componentDidMount() {
7 | // instantiate Video.js
8 | this.player = videojs(this.audioNode, this.props, () => {
9 | // print version information at startup
10 | var version_info = 'Using video.js ' + videojs.VERSION +
11 | ' with videojs-wavesurfer ' + videojs.getPluginVersion('wavesurfer') +
12 | ' and wavesurfer.js ' + WaveSurfer.VERSION;
13 | videojs.log(version_info);
14 |
15 | // load file
16 | this.player.src({src: '../media/hal.wav', type: 'audio/wav'});
17 | });
18 |
19 | this.player.on('waveReady', (event) => {
20 | console.log('waveform: ready!');
21 | });
22 |
23 | this.player.on('playbackFinish', (event) => {
24 | console.log('playback finished.');
25 | });
26 |
27 | // error handling
28 | this.player.on('error', (element, error) => {
29 | console.warn(error);
30 | });
31 | }
32 |
33 | // destroy player on unmount
34 | componentWillUnmount() {
35 | if (this.player) {
36 | this.player.dispose();
37 | }
38 | }
39 |
40 | // wrap the player in a div with a `data-vjs-player` attribute
41 | // so videojs won't create additional wrapper in the DOM
42 | // see https://github.com/videojs/video.js/pull/3856
43 | render() {
44 | return (
45 |
46 |
47 |
48 | )
49 | }
50 | }
51 |
52 | const videoJsOptions = {
53 | controls: true,
54 | autoplay: false,
55 | loop: false,
56 | muted: false,
57 | fluid: false,
58 | width: 600,
59 | height: 300,
60 | bigPlayButton: false,
61 | plugins: {
62 | wavesurfer: {
63 | backend: 'MediaElement',
64 | displayMilliseconds: true,
65 | debug: true,
66 | waveColor: 'white',
67 | progressColor: 'black',
68 | cursorColor: 'black',
69 | hideScrollbar: true
70 | }
71 | }
72 | };
73 |
74 | ReactDOM.render(, document.getElementById('root'));
75 |
--------------------------------------------------------------------------------
/examples/safari-workaround.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 |
3 | /* workaround safari issues */
4 |
5 | var isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
6 |
7 | function addStartButton() {
8 | var btn = document.createElement('BUTTON');
9 | var t = document.createTextNode('Show player');
10 | btn.onclick = createPlayer;
11 | btn.appendChild(t);
12 | document.body.appendChild(btn);
13 | }
14 |
15 | function updateContext(opts) {
16 | // Safari 11 or newer automatically suspends new AudioContext's that aren't
17 | // created in response to a user-gesture, like a click or tap, so create one
18 | // here (inc. the script processor)
19 | var AudioContext = window.AudioContext || window.webkitAudioContext;
20 | var context = new AudioContext();
21 | var processor = context.createScriptProcessor(1024, 1, 1);
22 |
23 | opts.plugins.wavesurfer.audioContext = context;
24 | opts.plugins.wavesurfer.audioScriptProcessor = processor;
25 | }
26 |
27 | function enableTextTracks(opts) {
28 | // workaround for video.js issue with Safari text tracks
29 | // see https://github.com/videojs/video.js/issues/7015
30 | opts.html5 = {
31 | nativeTextTracks: false,
32 | vhs: {
33 | overrideNative: true
34 | }
35 | };
36 | }
37 |
--------------------------------------------------------------------------------
/examples/texttrack.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Text-track Example - Wavesurfer Plugin for Video.js
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
95 |
96 |
97 |
98 |
--------------------------------------------------------------------------------
/examples/video.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Video Example - Wavesurfer Plugin Video
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
89 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "videojs-wavesurfer",
3 | "version": "3.10.0",
4 | "description": "video.js plugin that adds a navigable waveform for audio and video files.",
5 | "author": "Collab",
6 | "license": "MIT",
7 | "readmeFilename": "README.md",
8 | "bugs": {
9 | "url": "https://github.com/collab-project/videojs-wavesurfer/issues"
10 | },
11 | "homepage": "https://github.com/collab-project/videojs-wavesurfer",
12 | "main": "dist/videojs.wavesurfer.js",
13 | "style": "dist/css/videojs.wavesurfer.css",
14 | "sass": "src/css/videojs.wavesurfer.scss",
15 | "directories": {
16 | "docs": "./docs",
17 | "lib": "./src",
18 | "example": "./examples",
19 | "test": "./test"
20 | },
21 | "repository": {
22 | "type": "git",
23 | "url": "https://github.com/collab-project/videojs-wavesurfer.git"
24 | },
25 | "scripts": {
26 | "clean": "rimraf dist",
27 | "build": "npm run clean && npm run lint && npm run build:dev && npm run build:min",
28 | "build:dev": "webpack --config ./build-config/webpack.dev.main.js",
29 | "build:min": "webpack --config ./build-config/webpack.prod.main.js",
30 | "docs": "npm-run-all docs:*",
31 | "docs:api": "rimraf docs/api && esdoc",
32 | "lint": "npm-run-all lint:*",
33 | "lint:js": "eslint src/js test build-config",
34 | "lint:html": "htmlhint examples docs/demo",
35 | "start": "npm run build && webpack serve --config ./build-config/webpack.dev.main.js",
36 | "start-doc": "docsify serve docs",
37 | "test": "karma start karma.conf.js",
38 | "prepublishOnly": "not-in-install && npm run build || in-install"
39 | },
40 | "files": [
41 | "dist/",
42 | "src/"
43 | ],
44 | "keywords": [
45 | "waveform",
46 | "audio",
47 | "video",
48 | "wavesurfer",
49 | "videojs",
50 | "videojs-plugin",
51 | "player"
52 | ],
53 | "dependencies": {
54 | "video.js": ">=7.0.5",
55 | "wavesurfer.js": ">=6.3.0 <7.0.0"
56 | },
57 | "devDependencies": {
58 | "@babel/core": "^7.26.0",
59 | "@babel/preset-env": "^7.26.0",
60 | "@babel/register": "^7.25.9",
61 | "@chiragrupani/karma-chromium-edge-launcher": "^2.4.1",
62 | "@jsdevtools/host-environment": "^2.1.2",
63 | "@jsdevtools/karma-host-environment": "^3.0.3",
64 | "add-zero": "^1.0.0",
65 | "babel-loader": "^9.2.1",
66 | "babel-plugin-add-module-exports": "^1.0.4",
67 | "babel-plugin-istanbul": "^7.0.0",
68 | "browserslist": "^4.24.3",
69 | "css-loader": "^7.1.2",
70 | "css-minimizer-webpack-plugin": "^7.0.0",
71 | "date-fns": "^4.1.0",
72 | "docsify-cli": "^4.4.4",
73 | "esdoc": "^1.1.0",
74 | "esdoc-standard-plugin": "^1.0.0",
75 | "eslint": "^9.17.0",
76 | "eslint-plugin-jsdoc": "^50.6.1",
77 | "htmlhint": "^1.1.4",
78 | "in-publish": "^2.0.1",
79 | "jasmine-core": "^5.5.0",
80 | "karma": "^6.4.4",
81 | "karma-chrome-launcher": "^3.2.0",
82 | "karma-coverage": "^2.2.1",
83 | "karma-detect-browsers": "^2.3.3",
84 | "karma-firefox-launcher": "^2.1.3",
85 | "karma-jasmine": "^5.1.0",
86 | "karma-jasmine-matchers": "^5.0.0",
87 | "karma-verbose-reporter": "0.0.8",
88 | "karma-webpack": "^5.0.1",
89 | "mini-css-extract-plugin": "^2.9.2",
90 | "npm-run-all": "^4.1.5",
91 | "parse-ms": "^3.0.0",
92 | "replace-in-file": "^8.2.0",
93 | "rimraf": "^6.0.1",
94 | "sass": "^1.83.0",
95 | "sass-loader": "^16.0.4",
96 | "style-loader": "^4.0.0",
97 | "webpack": "^5.97.1",
98 | "webpack-cli": "^5.1.4",
99 | "webpack-dev-server": "^5.2.0",
100 | "webpack-merge": "^6.0.1",
101 | "webpack-remove-empty-scripts": "^1.0.4",
102 | "webrtc-adapter": "^9.0.1"
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/src/css/fluid.scss:
--------------------------------------------------------------------------------
1 | /* Handle responsive / fluid view.
2 | --------------------------------------------------------------------------------
3 | */
4 | .vjs-wavesurfer.vjs-fluid wave.vjs-wavedisplay {
5 | top: 0;
6 | position: absolute!important;
7 | width: 100%;
8 | min-width: 100%;
9 | max-width: 100%;
10 | height: 100%;
11 | }
12 |
--------------------------------------------------------------------------------
/src/css/main.scss:
--------------------------------------------------------------------------------
1 | /* Ensure custom controls are always visible because
2 | the plugin hides and replace the video.js native mobile
3 | controls.
4 | --------------------------------------------------------------------------------
5 | */
6 | .vjs-wavesurfer .vjs-using-native-controls .vjs-control-bar {
7 | display: flex !important;
8 | }
9 |
10 | /* Ensure that vjs menus and interfaces can be interacted with (such as the
11 | progress control).
12 | --------------------------------------------------------------------------------
13 | */
14 | .vjs-wavesurfer .vjs-menu-content, .vjs-progress-control {
15 | z-index: 4;
16 | }
17 |
18 | .vjs-wavesurfer .vjs-modal-dialog, .vjs-text-track-display {
19 | z-index: 4;
20 | }
--------------------------------------------------------------------------------
/src/css/videojs.wavesurfer.scss:
--------------------------------------------------------------------------------
1 | @use "main";
2 | @use "fluid";
3 |
--------------------------------------------------------------------------------
/src/js/defaults.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file defaults.js
3 | * @since 2.0.0
4 | */
5 |
6 | // plugin defaults
7 | const pluginDefaultOptions = {
8 | // Display console log messages.
9 | debug: false,
10 | // Boolean indicating if milliseconds should be included,
11 | // e.g. "00:00:000" vs "00:00".
12 | displayMilliseconds: true
13 | };
14 |
15 | export default pluginDefaultOptions;
16 |
--------------------------------------------------------------------------------
/src/js/event.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file event.js
3 | * @since 2.8.0
4 | */
5 |
6 | class Event {}
7 |
8 | // video.js
9 | Event.READY = 'ready';
10 | Event.ERROR = 'error';
11 | Event.VOLUMECHANGE = 'volumechange';
12 | Event.FULLSCREENCHANGE = 'fullscreenchange';
13 | Event.TIMEUPDATE = 'timeupdate';
14 | Event.ENDED = 'ended';
15 | Event.PAUSE = 'pause';
16 |
17 | // wavesurfer.js
18 | Event.FINISH = 'finish';
19 | Event.SEEK = 'seek';
20 | Event.REDRAW = 'redraw';
21 | Event.AUDIOPROCESS = 'audioprocess';
22 | Event.DEVICE_READY = 'deviceReady';
23 | Event.DEVICE_ERROR = 'deviceError';
24 |
25 | // videojs-wavesurfer
26 | Event.AUDIO_OUTPUT_READY = 'audioOutputReady';
27 | Event.WAVE_READY = 'waveReady';
28 | Event.PLAYBACK_FINISH = 'playbackFinish';
29 | Event.ABORT = 'abort';
30 |
31 | // dom
32 | Event.RESIZE = 'resize';
33 |
34 | // after the freeze, any attempts of altering the class will have no result
35 | Object.freeze(Event);
36 |
37 | export default Event;
38 |
--------------------------------------------------------------------------------
/src/js/middleware.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file middleware.js
3 | * @since 3.0.0
4 | */
5 |
6 | const WavesurferMiddleware = {
7 | /**
8 | * Setup the routing between a specific source and middleware
9 | * and eventually set the source on the Tech.
10 | *
11 | * @param {Tech~SourceObject} [srcObj] - Source object to manipulate.
12 | * @param {Function} [next] - The next middleware to run.
13 | */
14 | setSource(srcObj, next) {
15 | // check if this player is using the videojs-wavesurfer plugin
16 | if (this.player.usingPlugin('wavesurfer')) {
17 | let backend = this.player.wavesurfer().surfer.params.backend;
18 | let src = srcObj.src;
19 | let peaks = srcObj.peaks;
20 |
21 | switch (backend) {
22 | case 'WebAudio':
23 | // load url into wavesurfer
24 | this.player.wavesurfer().load(src);
25 | break;
26 |
27 | default:
28 | // load source into video.js
29 | next(null, srcObj);
30 |
31 | // load media element into wavesurfer
32 | let element = this.player.tech_.el();
33 | if (peaks === undefined) {
34 | // element without peaks
35 | this.player.wavesurfer().load(element);
36 | } else {
37 | // element with peaks
38 | this.player.wavesurfer().load(element, peaks);
39 | }
40 | break;
41 | }
42 | } else {
43 | // ignore middleware (this player isn't using the videojs-wavesurfer
44 | // plugin) and load source into video.js
45 | next(null, srcObj);
46 | }
47 | }
48 | };
49 |
50 | export default WavesurferMiddleware;
51 |
--------------------------------------------------------------------------------
/src/js/utils/format-time.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file format-time.js
3 | * @since 2.0.0
4 | */
5 |
6 | import addZero from 'add-zero';
7 | import parseMilliseconds from 'parse-ms';
8 |
9 | /**
10 | * Format seconds as a duration string.
11 | *
12 | * Either formatted as:
13 | *
14 | * - DD:HH:MM:SS (> 24 hours)
15 | * - HH:MM:SS (> 1 hour)
16 | * - MM:SS:MSS (`displayMilliseconds = true`)
17 | * - MM:SS (`displayMilliseconds = false`)
18 | *
19 | * Supplying a guide (in seconds) will force a number of leading zeros
20 | * to cover the length of the guide.
21 | *
22 | * @param {number} seconds - Number of seconds to be turned into a
23 | * string.
24 | * @param {number} guide - Number (in seconds) to model the string after.
25 | * @param {boolean} displayMilliseconds - Display milliseconds or not.
26 | * @return {string} Formatted duration time, e.g '00:12:653'.
27 | * @private
28 | */
29 | const formatTime = function(seconds, guide, displayMilliseconds = true) {
30 | seconds = seconds < 0 ? 0 : seconds;
31 | if (isNaN(seconds) || seconds === Infinity) {
32 | seconds = 0;
33 | }
34 | const inputTime = parseMilliseconds(seconds * 1000);
35 | let guideTime = inputTime;
36 | if (guide !== undefined) {
37 | guideTime = parseMilliseconds(guide * 1000);
38 | }
39 | const hr = addZero(inputTime.hours);
40 | const min = addZero(inputTime.minutes);
41 | const sec = addZero(inputTime.seconds);
42 | const ms = addZero(inputTime.milliseconds, 3);
43 |
44 | if (inputTime.days > 0 || guideTime.days > 0) {
45 | const day = addZero(inputTime.days);
46 | return `${day}:${hr}:${min}:${sec}`;
47 | }
48 | if (inputTime.hours > 0 || guideTime.hours > 0) {
49 | return `${hr}:${min}:${sec}`;
50 | }
51 | if (displayMilliseconds) {
52 | return `${min}:${sec}:${ms}`;
53 | }
54 |
55 | return `${min}:${sec}`;
56 | };
57 |
58 | export default formatTime;
59 |
--------------------------------------------------------------------------------
/src/js/utils/log.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @file log.js
3 | * @since 2.0.0
4 | */
5 |
6 | import videojs from 'video.js';
7 |
8 | const ERROR = 'error';
9 | const WARN = 'warn';
10 |
11 | /**
12 | * Log message (if the debug option is enabled).
13 | *
14 | * @private
15 | * @param {Array} args - The arguments to be passed to the matching console
16 | * method.
17 | * @param {string} logType - The name of the console method to use.
18 | * @param {boolean} debug - Whether or not the debug option is enabled or not.
19 | */
20 | const log = function(args, logType, debug)
21 | {
22 | if (debug === true) {
23 | if (logType === ERROR) {
24 | videojs.log.error(args);
25 | } else if (logType === WARN) {
26 | videojs.log.warn(args);
27 | } else {
28 | videojs.log(args);
29 | }
30 | }
31 | };
32 |
33 | export default log;
34 |
--------------------------------------------------------------------------------
/test/defaults.spec.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @since 2.3.0
3 | */
4 |
5 | import pluginDefaultOptions from '../src/js/defaults.js';
6 |
7 | /** @test {defaults} */
8 | describe('pluginDefaultOptions', () => {
9 |
10 | /** @test {pluginDefaultOptions} */
11 | it('returns a non-empty object', () => {
12 | expect(pluginDefaultOptions).toBeNonEmptyObject();
13 | });
14 |
15 | /** @test {pluginDefaultOptions} */
16 | it('contains correct default values', () => {
17 | expect(pluginDefaultOptions).toEqual({
18 | debug: false,
19 | displayMilliseconds: true
20 | });
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/test/fluid.spec.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @since 2.3.0
3 | */
4 |
5 | import window from 'global/window';
6 |
7 | import Event from '../src/js/event.js';
8 |
9 | import TestHelpers from './test-helpers.js';
10 |
11 |
12 | let player;
13 |
14 | function fluid_test(backend) {
15 | /** @test {Wavesurfer#redrawWaveform} */
16 | it('redraws the waveform', (done) => {
17 | let options = {
18 | fluid: true,
19 | plugins: {
20 | wavesurfer: {
21 | backend: backend
22 | }
23 | }
24 | };
25 | player = TestHelpers.makePlayer(options);
26 | player.one(Event.WAVE_READY, () => {
27 | // class is present
28 | expect(player.hasClass('vjs-fluid')).toBeTrue();
29 |
30 | done();
31 | });
32 |
33 | // load file
34 | player.src(TestHelpers.EXAMPLE_AUDIO_SRC);
35 | });
36 | }
37 |
38 | /** @test {Wavesurfer} */
39 | describe('Wavesurfer Fluid', () => {
40 | afterEach(() => {
41 | // destroy player
42 | player.dispose();
43 | });
44 |
45 | fluid_test(TestHelpers.MEDIA_ELEMENT_BACKEND);
46 | fluid_test(TestHelpers.WEB_AUDIO_BACKEND);
47 | fluid_test(TestHelpers.MEDIA_ELEMENT_WEB_AUDIO_BACKEND);
48 | });
--------------------------------------------------------------------------------
/test/live.spec.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @since 2.3.0
3 | */
4 |
5 | import Event from '../src/js/event.js';
6 |
7 | import TestHelpers from './test-helpers.js';
8 |
9 | import MicrophonePlugin from 'wavesurfer.js/dist/plugin/wavesurfer.microphone';
10 |
11 | /** @test {Wavesurfer} */
12 | describe('Wavesurfer Live', () => {
13 | let player;
14 |
15 | afterEach(() => {
16 | // delete player
17 | player.dispose();
18 | });
19 |
20 | /** @test {Wavesurfer} */
21 | it('microphone plugin is enabled', (done) => {
22 | player = TestHelpers.makePlayer({
23 | plugins: {
24 | wavesurfer: {
25 | backend: 'WebAudio',
26 | waveColor: 'black',
27 | cursorWidth: 0,
28 | interact: false,
29 | plugins: [
30 | // enable microphone plugin (for wavesurfer.js)
31 | MicrophonePlugin.create({
32 | bufferSize: 4096,
33 | numberOfInputChannels: 1,
34 | numberOfOutputChannels: 1,
35 | constraints: {
36 | video: false,
37 | audio: true
38 | }
39 | })
40 | ]
41 | }
42 | }
43 | });
44 |
45 | player.one(Event.READY, () => {
46 | expect(player.wavesurfer().liveMode).toBeTrue();
47 | expect(player.wavesurfer().waveReady).toBeTrue();
48 | expect(player.wavesurfer().surfer.microphone).toBeDefined();
49 |
50 | player.wavesurfer().surfer.microphone.once('deviceReady', () => {
51 | // device is ready
52 | expect(player.wavesurfer().surfer.microphone.active).toBeTrue();
53 |
54 | // increase test coverage
55 | player.wavesurfer().pause();
56 | player.wavesurfer().play();
57 |
58 | done();
59 | });
60 |
61 | // start microphone
62 | player.wavesurfer().play();
63 | });
64 | });
65 |
66 | });
--------------------------------------------------------------------------------
/test/options.spec.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @since 2.0.0
3 | */
4 |
5 | import host from "@jsdevtools/host-environment";
6 |
7 | import Event from '../src/js/event.js';
8 |
9 | import TestHelpers from './test-helpers.js';
10 |
11 |
12 | let player;
13 |
14 | function ws_options_test(backend) {
15 | /** @test {Wavesurfer} */
16 | it('accepts waveformHeight option', (done) => {
17 | let height = 139;
18 | // create player
19 | player = TestHelpers.makePlayer({
20 | plugins: {
21 | wavesurfer: {
22 | backend: backend,
23 | waveformHeight: height
24 | }
25 | }
26 | });
27 |
28 | player.one(Event.WAVE_READY, () => {
29 | expect(player.wavesurfer().surfer.getHeight()).toEqual(height);
30 |
31 | done();
32 | });
33 |
34 | // load file
35 | player.src(TestHelpers.EXAMPLE_AUDIO_SRC);
36 | });
37 |
38 | /** @test {Wavesurfer} */
39 | it('accepts splitChannels option', (done) => {
40 | player = TestHelpers.makePlayer({
41 | height: 100,
42 | plugins: {
43 | wavesurfer: {
44 | backend: backend,
45 | splitChannels: true
46 | }
47 | }
48 | });
49 |
50 | player.one(Event.WAVE_READY, () => {
51 | expect(player.wavesurfer().surfer.getHeight()).toEqual(35);
52 |
53 | done();
54 | });
55 |
56 | // load file
57 | player.src(TestHelpers.EXAMPLE_AUDIO_SRC);
58 | });
59 |
60 | /** @test {Wavesurfer} */
61 | it('accepts autoplay option', (done) => {
62 | player = TestHelpers.makePlayer({
63 | autoplay: true,
64 | plugins: {
65 | wavesurfer: {
66 | backend: backend
67 | }
68 | }
69 | });
70 |
71 | // skip test in firefox until autoplay in headless browser is figured out
72 | if (host.browser.firefox) {
73 | done();
74 | }
75 |
76 | player.one(Event.ERROR, (element, error) => {
77 | fail(error);
78 | });
79 | player.one(Event.ENDED, done);
80 |
81 | // load file
82 | player.src(TestHelpers.EXAMPLE_AUDIO_SRC);
83 | });
84 |
85 | /** @test {Wavesurfer} */
86 | it('accepts formatTime option', (done) => {
87 | player = TestHelpers.makePlayer({
88 | height: 100,
89 | plugins: {
90 | wavesurfer: {
91 | backend: backend,
92 | formatTime: (seconds, guide) => `foo:${seconds}:${guide}`
93 | }
94 | }
95 | });
96 |
97 | player.one(Event.WAVE_READY, () => {
98 | expect(player.controlBar.currentTimeDisplay.formattedTime_).toEqual('foo:0:0');
99 | expect(player.controlBar.durationDisplay.formattedTime_.substring(0, 5)).toEqual('foo:0');
100 | done();
101 | });
102 |
103 | // load file
104 | player.src(TestHelpers.EXAMPLE_AUDIO_SRC);
105 | });
106 | }
107 |
108 | /** @test {Wavesurfer} */
109 | describe('Wavesurfer options', () => {
110 | afterEach(() => {
111 | // delete player
112 | player.dispose();
113 | });
114 |
115 | // run tests for each wavesurfer.js backend
116 | ws_options_test(TestHelpers.MEDIA_ELEMENT_BACKEND);
117 | ws_options_test(TestHelpers.MEDIA_ELEMENT_WEB_AUDIO_BACKEND);
118 | ws_options_test(TestHelpers.WEB_AUDIO_BACKEND);
119 | });
--------------------------------------------------------------------------------
/test/support/demo-peaks-invalid.json:
--------------------------------------------------------------------------------
1 | {"invalid": [-0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.031496062992125984, 0.03937007874015748, -1.0078740157480315, 1.0, -0.015748031496062992, 0.015748031496062992, -0.007874015748031496, 0.0, -0.06299212598425197, 0.09448818897637795, -0.7952755905511811, 0.7480314960629921, -1.0078740157480315, 1.0, -0.889763779527559, 0.7086614173228346, -1.0078740157480315, 1.0, -1.0078740157480315, 1.0, -0.12598425196850394, 0.23622047244094488, -0.10236220472440945, 0.11023622047244094, -0.05511811023622047, 0.0, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.015748031496062992, 0.0, -0.25196850393700787, 0.2125984251968504, -0.5354330708661418, 0.3228346456692913, -0.06299212598425197, 0.09448818897637795, -0.10236220472440945, 0.09448818897637795, -0.031496062992125984, 0.08661417322834646, -0.6692913385826772, 0.9212598425196851, -1.0078740157480315, 1.0, -1.0078740157480315, 1.0, -0.2283464566929134, 0.16535433070866143, -0.11023622047244094, 0.11023622047244094, -0.06299212598425197, 0.08661417322834646, -0.05511811023622047, 0.047244094488188976, -0.015748031496062992, 0.015748031496062992, -0.07874015748031496, 0.08661417322834646, -0.007874015748031496, 0.015748031496062992, -0.015748031496062992, 0.015748031496062992, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.14960629921259844, 0.13385826771653545, -0.25196850393700787, 0.1889763779527559, -0.007874015748031496, 0.015748031496062992, -0.5118110236220472, 0.3937007874015748, -1.0078740157480315, 1.0, -0.05511811023622047, 0.06299212598425197, -0.015748031496062992, 0.015748031496062992, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0]}
--------------------------------------------------------------------------------
/test/support/demo-peaks.json:
--------------------------------------------------------------------------------
1 | {"bits": 8, "length": 68, "sample_rate": 22050, "samples_per_pixel": 256, "data": [-0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.031496062992125984, 0.03937007874015748, -1.0078740157480315, 1.0, -0.015748031496062992, 0.015748031496062992, -0.007874015748031496, 0.0, -0.06299212598425197, 0.09448818897637795, -0.7952755905511811, 0.7480314960629921, -1.0078740157480315, 1.0, -0.889763779527559, 0.7086614173228346, -1.0078740157480315, 1.0, -1.0078740157480315, 1.0, -0.12598425196850394, 0.23622047244094488, -0.10236220472440945, 0.11023622047244094, -0.05511811023622047, 0.0, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.015748031496062992, 0.0, -0.25196850393700787, 0.2125984251968504, -0.5354330708661418, 0.3228346456692913, -0.06299212598425197, 0.09448818897637795, -0.10236220472440945, 0.09448818897637795, -0.031496062992125984, 0.08661417322834646, -0.6692913385826772, 0.9212598425196851, -1.0078740157480315, 1.0, -1.0078740157480315, 1.0, -0.2283464566929134, 0.16535433070866143, -0.11023622047244094, 0.11023622047244094, -0.06299212598425197, 0.08661417322834646, -0.05511811023622047, 0.047244094488188976, -0.015748031496062992, 0.015748031496062992, -0.07874015748031496, 0.08661417322834646, -0.007874015748031496, 0.015748031496062992, -0.015748031496062992, 0.015748031496062992, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.14960629921259844, 0.13385826771653545, -0.25196850393700787, 0.1889763779527559, -0.007874015748031496, 0.015748031496062992, -0.5118110236220472, 0.3937007874015748, -1.0078740157480315, 1.0, -0.05511811023622047, 0.06299212598425197, -0.015748031496062992, 0.015748031496062992, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0, -0.007874015748031496, 0.0]}
--------------------------------------------------------------------------------
/test/support/demo.vtt:
--------------------------------------------------------------------------------
1 | WEBVTT
2 |
3 | NOTE Paragraph
4 |
5 | 00:00:00.137 --> 00:00:00.780
6 | Blip
7 |
--------------------------------------------------------------------------------
/test/support/demo.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/collab-project/videojs-wavesurfer/cd3284bf8e2da59039fe59af8ce33003d69bd6ae/test/support/demo.wav
--------------------------------------------------------------------------------
/test/support/stars.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/collab-project/videojs-wavesurfer/cd3284bf8e2da59039fe59af8ce33003d69bd6ae/test/support/stars.mp4
--------------------------------------------------------------------------------
/test/test-helpers.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @since 2.3.0
3 | */
4 |
5 | import document from 'global/document';
6 |
7 | import {Player, obj} from 'video.js';
8 |
9 | const TestHelpers = {
10 |
11 | /** wavesurer.js backends to test against */
12 | MEDIA_ELEMENT_BACKEND: 'MediaElement',
13 | MEDIA_ELEMENT_WEB_AUDIO_BACKEND: 'MediaElementWebAudio',
14 | WEB_AUDIO_BACKEND: 'WebAudio',
15 |
16 | /** Example audio clip */
17 | EXAMPLE_AUDIO_FILE: '/base/test/support/demo.wav',
18 |
19 | /** Example audio clip mime-type */
20 | EXAMPLE_AUDIO_TYPE: 'audio/wav',
21 |
22 | /** Example audio src object */
23 | EXAMPLE_AUDIO_SRC: {
24 | src: '/base/test/support/demo.wav',
25 | type: 'audio/wav'
26 | },
27 |
28 | /** Length of example audio clip */
29 | EXAMPLE_AUDIO_DURATION: 0.782312925170068,
30 |
31 | /** Peaks data for example audio clip */
32 | EXAMPLE_AUDIO_PEAKS_FILE: '/base/test/support/demo-peaks.json',
33 |
34 | /** File with invalid peaks data */
35 | EXAMPLE_AUDIO_PEAKS_INVALID_FILE: '/base/test/support/demo-peaks-invalid.json',
36 |
37 | /** Example VTT clip */
38 | EXAMPLE_VTT_FILE: '/base/test/support/demo.vtt',
39 |
40 | /** Example video clip */
41 | EXAMPLE_VIDEO_FILE: '/base/test/support/stars.mp4',
42 |
43 | /** Example video clip mime-type */
44 | EXAMPLE_VIDEO_TYPE: 'video/mp4',
45 |
46 | /**
47 | * Create DOM element.
48 | */
49 | makeElement(element_type, id_name) {
50 | if (element_type === undefined) {
51 | element_type = 'audio';
52 | }
53 | if (id_name === undefined) {
54 | id_name = 'myAudio';
55 | }
56 | const element = document.createElement(element_type);
57 | element.id = id_name;
58 | element.muted = true;
59 | element.className = 'video-js vjs-default-skin';
60 | element.style = 'background-color: #F2E68A;';
61 |
62 | return element;
63 | },
64 |
65 | /**
66 | * Create a test player containing the videojs-wavesurfer plugin.
67 | *
68 | * @param {Object} playerOptions
69 | * @param {Element|String} elementTag
70 | */
71 | makePlayer(playerOptions, elementTag) {
72 | elementTag = elementTag || TestHelpers.makeElement();
73 |
74 | // add to dom
75 | document.getElementsByTagName('body')[0].appendChild(elementTag);
76 |
77 | // default options
78 | let opts = obj.merge({
79 | controls: true,
80 | autoplay: false,
81 | fluid: false,
82 | loop: false,
83 | width: 600,
84 | height: 300,
85 | plugins: {
86 | wavesurfer: {
87 | backend: 'MediaElement',
88 | msDisplayMax: 10,
89 | debug: true,
90 | waveColor: 'blue',
91 | progressColor: 'black',
92 | cursorColor: 'black',
93 | hideScrollbar: true,
94 | xhr: {}
95 | }
96 | }
97 | }, playerOptions || {});
98 |
99 | return videojs(elementTag.id, opts);
100 | },
101 |
102 | /**
103 | * Dispose all players.
104 | */
105 | cleanup() {
106 | for (const playerId in Player.players) {
107 | if (Player.players[playerId] !== null) {
108 | Player.players[playerId].dispose();
109 | }
110 | delete Player.players[playerId];
111 | }
112 | },
113 |
114 | /**
115 | * Triggers an event on a DOM node natively.
116 | *
117 | * @param {Element} element
118 | * @param {string} eventType
119 | */
120 | triggerDomEvent(element, eventType) {
121 | let event;
122 |
123 | if (document.createEvent) {
124 | event = document.createEvent('HTMLEvents');
125 | event.initEvent(eventType, true, true);
126 | } else {
127 | event = document.createEventObject();
128 | event.eventType = eventType;
129 | }
130 |
131 | event.eventName = eventType;
132 |
133 | if (document.createEvent) {
134 | element.dispatchEvent(event);
135 | } else {
136 | element.fireEvent('on' + event.eventType, event);
137 | }
138 | }
139 | };
140 |
141 | export default TestHelpers;
--------------------------------------------------------------------------------
/test/tracks.spec.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @since 2.5.0
3 | */
4 |
5 | import Event from '../src/js/event.js';
6 |
7 | import TestHelpers from './test-helpers.js';
8 |
9 | /** @test {Wavesurfer} */
10 | describe('Wavesurfer TextTracks', () => {
11 | let player;
12 |
13 | beforeEach(() => {
14 | // create audio element with nested text track element
15 | const element = TestHelpers.makeElement('audio', 'myAudioTextTracks');
16 | const track = document.createElement('track');
17 | track.kind = 'captions';
18 | track.src = TestHelpers.EXAMPLE_VTT_FILE;
19 | track.srclang = 'en-US';
20 | track.default = true;
21 | element.appendChild(track);
22 |
23 | // create new player
24 | player = TestHelpers.makePlayer({}, element);
25 | });
26 |
27 | afterEach(() => {
28 | // delete player
29 | player.dispose();
30 | });
31 |
32 | /** @test {Wavesurfer} */
33 | it('displays interface elements', (done) => {
34 |
35 | player.one(Event.WAVE_READY, () => {
36 | // text tracks UI is visible
37 | expect(player.controlBar.subsCapsButton.hasClass('vjs-hidden')).toBeFalse();
38 |
39 | // text track is present
40 | expect(player.textTracks().length).toEqual(1);
41 |
42 | done();
43 | });
44 |
45 | player.src(TestHelpers.EXAMPLE_AUDIO_SRC);
46 | });
47 | });
--------------------------------------------------------------------------------
/test/utils.spec.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @since 2.3.0
3 | */
4 |
5 | import formatTime from '../src/js/utils/format-time.js';
6 | import log from '../src/js/utils/log.js';
7 |
8 | /** @test {format-time} */
9 | describe('formatTime', () => {
10 |
11 | /** @test {formatTime} */
12 | it('returns a formatted string for seconds', () => {
13 | let time = formatTime(10);
14 | expect(time).toEqual('00:10:000');
15 |
16 | time = formatTime(11);
17 | expect(time).toEqual('00:11:000');
18 |
19 | time = formatTime(121);
20 | expect(time).toEqual('02:01:000');
21 |
22 | time = formatTime(3661);
23 | expect(time).toEqual('01:01:01');
24 |
25 | // 300 days, 1 hour and 1 second
26 | time = formatTime(25923601);
27 | expect(time).toEqual('300:01:00:01');
28 | });
29 |
30 | /** @test {formatTime} */
31 | it('returns a formatted string using a guide', () => {
32 | let time = formatTime(4.121, 10);
33 | expect(time).toEqual('00:04:121');
34 |
35 | // using one hour as guide
36 | time = formatTime(4.121, 3600);
37 | expect(time).toEqual('00:00:04');
38 |
39 | // using one day as guide
40 | time = formatTime(4.121, 86400);
41 | expect(time).toEqual('00:00:00:04');
42 | });
43 |
44 | /** @test {formatTime} */
45 | it('returns a formatted string using displayMilliseconds option', () => {
46 | let time = formatTime(123.652, 10, false);
47 | expect(time).toEqual('02:03');
48 |
49 | time = formatTime(7.652, 4.652, false);
50 | expect(time).toEqual('00:07');
51 |
52 | // using one day as guide (will ignore option)
53 | time = formatTime(12.034, 86400, true);
54 | expect(time).toEqual('00:00:00:12');
55 | });
56 |
57 | /** @test {formatTime} */
58 | it('returns a string when no arguments are received', () => {
59 | let time = formatTime();
60 |
61 | expect(time).toEqual('00:00:000');
62 | });
63 |
64 | /** @test {formatTime} */
65 | it('defaults to 0 when a negative value is received', () => {
66 | let time = formatTime(-2);
67 |
68 | expect(time).toEqual('00:00:000');
69 | });
70 | });
71 |
72 | /** @test {log} */
73 | describe('log', () => {
74 |
75 | /** @test {log} */
76 | it('does not work when debug is false', () => {
77 | let test = log('foo', 'error', false);
78 | expect(test).toBeUndefined();
79 | });
80 |
81 | /** @test {log} */
82 | it('only works when debug is true', () => {
83 | let test = log('foo', 'error', true);
84 | expect(test).toBeUndefined();
85 |
86 | test = log('foo', 'warn', true);
87 | expect(test).toBeUndefined();
88 |
89 | test = log('foo', 'bar', true);
90 | expect(test).toBeUndefined();
91 | });
92 | });
--------------------------------------------------------------------------------
/test/video.spec.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @since 3.0.0
3 | */
4 |
5 | import window from 'global/window';
6 |
7 | import Event from '../src/js/event.js';
8 |
9 | import TestHelpers from './test-helpers.js';
10 |
11 | let player, element;
12 |
13 | function video_test(test_backend) {
14 | /** @test {Wavesurfer#redrawWaveform} */
15 | it('draw waveform for video', (done) => {
16 | let opts = {
17 | plugins: {
18 | wavesurfer: {
19 | backend: test_backend
20 | }
21 | }
22 | };
23 | element = TestHelpers.makeElement('video', 'testVideo');
24 | player = TestHelpers.makePlayer(opts, element);
25 | player.one(Event.ERROR, (element, error) => {
26 | fail(error);
27 | });
28 | player.one(Event.WAVE_READY, done);
29 |
30 | // load file
31 | player.src({
32 | src: TestHelpers.EXAMPLE_VIDEO_FILE,
33 | type: TestHelpers.EXAMPLE_VIDEO_TYPE
34 | });
35 | });
36 | }
37 |
38 | /** @test {Wavesurfer} */
39 | describe('Wavesurfer Video', () => {
40 |
41 | afterEach(() => {
42 | // destroy player
43 | player.dispose();
44 | });
45 |
46 | video_test(TestHelpers.MEDIA_ELEMENT_BACKEND);
47 | video_test(TestHelpers.MEDIA_ELEMENT_WEB_AUDIO_BACKEND);
48 | video_test(TestHelpers.WEB_AUDIO_BACKEND);
49 | });
--------------------------------------------------------------------------------