├── .editorconfig ├── .github ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .npmignore ├── .nvmrc ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── index.html ├── lang ├── ar.json ├── cs.json ├── de.json ├── en.json ├── es.json ├── fr.json ├── gl.json ├── it.json ├── ja.json ├── ko.json ├── pt.json ├── ru.json ├── tr.json ├── zh-CN.json └── zh-TW.json ├── package-lock.json ├── package.json ├── scripts ├── karma.conf.js ├── postcss.config.js └── rollup.config.js ├── src ├── plugin.css └── plugin.js └── test └── plugin.test.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_style = space 8 | indent_size = 2 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | Briefly describe the issue. 3 | Include a [reduced test case](https://css-tricks.com/reduced-test-cases/). 4 | 5 | ## Steps to reproduce 6 | Explain in detail the exact steps necessary to reproduce the issue. 7 | 8 | 1. 9 | 2. 10 | 3. 11 | 12 | ## Results 13 | ### Expected 14 | Please describe what you expected to see. 15 | 16 | ### Actual 17 | Please describe what actually happened. 18 | 19 | ### Error output 20 | If there are any errors at all, please include them here. 21 | 22 | ## Additional Information 23 | Please include any additional information necessary here. Including the following: 24 | 25 | ### versions 26 | #### videojs 27 | what version of videojs does this occur with? 28 | 29 | #### browsers 30 | what browser are affected? 31 | 32 | #### OSes 33 | what platforms (operating systems and devices) are affected? 34 | 35 | ### plugins 36 | are any videojs plugins being used on the page? If so, please list them below. 37 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | Please describe the change as necessary. 3 | If it's a feature or enhancement please be as detailed as possible. 4 | If it's a bug fix, please link the issue that it fixes or describe the bug in as much detail. 5 | 6 | ## Specific Changes proposed 7 | Please list the specific changes involved in this pull request. 8 | 9 | ## Requirements Checklist 10 | - [ ] Feature implemented / Bug fixed 11 | - [ ] If necessary, more likely in a feature request than a bug fix 12 | - [ ] Unit Tests updated or fixed 13 | - [ ] Docs/guides updated 14 | - [ ] Reviewed by Two Core Contributors 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS 2 | Thumbs.db 3 | ehthumbs.db 4 | Desktop.ini 5 | .DS_Store 6 | ._* 7 | 8 | # Editors 9 | *~ 10 | *.swp 11 | *.tmproj 12 | *.tmproject 13 | *.sublime-* 14 | .idea/ 15 | .project/ 16 | .settings/ 17 | .vscode/ 18 | 19 | # Logs 20 | logs 21 | *.log 22 | npm-debug.log* 23 | 24 | # Dependency directories 25 | bower_components/ 26 | node_modules/ 27 | 28 | # Build-related directories 29 | dist/ 30 | docs/api/ 31 | test/dist/ 32 | .eslintcache 33 | .yo-rc.json 34 | 35 | # Ignore Chinese clones for now. 36 | lang/zh-Han*.json 37 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | lts/fermium 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | # [6.2.0](https://github.com/brightcove/videojs-errors/compare/v6.1.0...v6.2.0) (2024-02-03) 3 | 4 | ### Features 5 | 6 | * remove Flash errors ([#237](https://github.com/brightcove/videojs-errors/issues/237)) ([929b6ea](https://github.com/brightcove/videojs-errors/commit/929b6ea)) 7 | 8 | ### Chores 9 | 10 | * **deps-dev:** bump [@babel](https://github.com/babel)/traverse from 7.16.3 to 7.23.9 ([#240](https://github.com/brightcove/videojs-errors/issues/240)) ([92daba3](https://github.com/brightcove/videojs-errors/commit/92daba3)) 11 | * **deps-dev:** bump follow-redirects from 1.15.1 to 1.15.5 ([#239](https://github.com/brightcove/videojs-errors/issues/239)) ([9ea936e](https://github.com/brightcove/videojs-errors/commit/9ea936e)) 12 | * **deps-dev:** bump postcss from 8.4.14 to 8.4.31 ([#241](https://github.com/brightcove/videojs-errors/issues/241)) ([37110bf](https://github.com/brightcove/videojs-errors/commit/37110bf)) 13 | * **deps:** bump [@xmldom](https://github.com/xmldom)/xmldom from 0.7.5 to 0.7.8 ([#229](https://github.com/brightcove/videojs-errors/issues/229)) ([31ebae8](https://github.com/brightcove/videojs-errors/commit/31ebae8)) 14 | * **deps:** bump decode-uri-component from 0.2.0 to 0.2.2 ([#232](https://github.com/brightcove/videojs-errors/issues/232)) ([0c16a00](https://github.com/brightcove/videojs-errors/commit/0c16a00)) 15 | * **deps:** bump engine.io from 6.2.0 to 6.2.1 ([#231](https://github.com/brightcove/videojs-errors/issues/231)) ([7f2a4b4](https://github.com/brightcove/videojs-errors/commit/7f2a4b4)) 16 | * **deps:** bump json5 from 2.2.0 to 2.2.3 ([#233](https://github.com/brightcove/videojs-errors/issues/233)) ([661c16b](https://github.com/brightcove/videojs-errors/commit/661c16b)) 17 | * **deps:** bump minimatch from 3.0.4 to 3.1.2 ([#236](https://github.com/brightcove/videojs-errors/issues/236)) ([e74d664](https://github.com/brightcove/videojs-errors/commit/e74d664)) 18 | * **deps:** bump shell-quote from 1.7.2 to 1.7.3 ([#225](https://github.com/brightcove/videojs-errors/issues/225)) ([344733b](https://github.com/brightcove/videojs-errors/commit/344733b)) 19 | * **deps:** bump socket.io-parser and socket.io ([#242](https://github.com/brightcove/videojs-errors/issues/242)) ([90caad9](https://github.com/brightcove/videojs-errors/commit/90caad9)) 20 | * **deps:** bump socket.io-parser from 4.0.4 to 4.0.5 ([#230](https://github.com/brightcove/videojs-errors/issues/230)) ([713f3c0](https://github.com/brightcove/videojs-errors/commit/713f3c0)) 21 | * **deps:** bump terser from 5.10.0 to 5.14.2 ([#227](https://github.com/brightcove/videojs-errors/issues/227)) ([5b8026d](https://github.com/brightcove/videojs-errors/commit/5b8026d)) 22 | * **deps:** bump ua-parser-js from 0.7.31 to 0.7.33 ([#234](https://github.com/brightcove/videojs-errors/issues/234)) ([4148ab6](https://github.com/brightcove/videojs-errors/commit/4148ab6)) 23 | * **deps:** use video.js 8.x by default, but maintain backward compatibility ([#243](https://github.com/brightcove/videojs-errors/issues/243)) ([7f7c5b1](https://github.com/brightcove/videojs-errors/commit/7f7c5b1)) 24 | 25 | 26 | # [6.1.0](https://github.com/brightcove/videojs-errors/compare/v6.0.0...v6.1.0) (2023-11-01) 27 | 28 | ### Features 29 | 30 | * remove old flash errors ([#238](https://github.com/brightcove/videojs-errors/issues/238)) ([8697517](https://github.com/brightcove/videojs-errors/commit/8697517)) 31 | 32 | ### Bug Fixes 33 | 34 | * prevent lint-staged git add warning and mergeOptions warning on Video.js[@8](https://github.com/8) ([#235](https://github.com/brightcove/videojs-errors/issues/235)) ([2b0966f](https://github.com/brightcove/videojs-errors/commit/2b0966f)) 35 | 36 | 37 | # [6.0.0](https://github.com/brightcove/videojs-errors/compare/v5.1.0...v6.0.0) (2022-07-18) 38 | 39 | ### Features 40 | 41 | * Remove vjs-errors-dialog id ([#226](https://github.com/brightcove/videojs-errors/issues/226)) ([54aed49](https://github.com/brightcove/videojs-errors/commit/54aed49)) 42 | 43 | ### Chores 44 | 45 | * update dependenices, use postcss instead of node-sass, remove .travis.yml ([#224](https://github.com/brightcove/videojs-errors/issues/224)) ([0b724bf](https://github.com/brightcove/videojs-errors/commit/0b724bf)) 46 | 47 | 48 | ### BREAKING CHANGES 49 | 50 | * The `vjs-errors-dialog` id attribute will no longer be available to target the div wrapping error content and UI elements. A `vjs-errors-dialog` class will still be present on instances of the div, as per usual. 51 | 52 | 53 | # [5.1.0](https://github.com/brightcove/videojs-errors/compare/v5.0.0...v5.1.0) (2022-06-13) 54 | 55 | ### Features 56 | 57 | * improve the playback timeout error experience ([#223](https://github.com/brightcove/videojs-errors/issues/223)) ([235ff8f](https://github.com/brightcove/videojs-errors/commit/235ff8f)) 58 | 59 | 60 | # [5.0.0](https://github.com/brightcove/videojs-errors/compare/v4.5.0...v5.0.0) (2021-12-17) 61 | 62 | ### Chores 63 | 64 | * skip vjsverify es check ([#214](https://github.com/brightcove/videojs-errors/issues/214)) ([df89683](https://github.com/brightcove/videojs-errors/commit/df89683)) 65 | * Update generate-rollup-config to drop older browser support ([#211](https://github.com/brightcove/videojs-errors/issues/211)) ([e992df7](https://github.com/brightcove/videojs-errors/commit/e992df7)) 66 | 67 | 68 | ### BREAKING CHANGES 69 | 70 | * This removes support for some older browsers like IE 11 71 | 72 | Co-authored-by: Gary Katsevman 73 | 74 | 75 | # [4.5.0](https://github.com/brightcove/videojs-errors/compare/v4.4.0...v4.5.0) (2021-05-04) 76 | 77 | ### Features 78 | 79 | * add support for -1 timeout and backgroundTimeout values ([#206](https://github.com/brightcove/videojs-errors/issues/206)) ([cf0ea56](https://github.com/brightcove/videojs-errors/commit/cf0ea56)) 80 | 81 | 82 | # [4.4.0](https://github.com/brightcove/videojs-errors/compare/v4.3.2...v4.4.0) (2021-04-13) 83 | 84 | ### Features 85 | 86 | * Add backgroundTimeout option ([#198](https://github.com/brightcove/videojs-errors/issues/198)) ([d8d9efc](https://github.com/brightcove/videojs-errors/commit/d8d9efc)) 87 | * allow turning off timeout by setting it to Infinity ([#204](https://github.com/brightcove/videojs-errors/issues/204)) ([b73f160](https://github.com/brightcove/videojs-errors/commit/b73f160)), closes [#134](https://github.com/brightcove/videojs-errors/issues/134) 88 | 89 | ### Chores 90 | 91 | * don't run tests on version ([#205](https://github.com/brightcove/videojs-errors/issues/205)) ([c349883](https://github.com/brightcove/videojs-errors/commit/c349883)) 92 | 93 | 94 | ## [4.3.2](https://github.com/brightcove/videojs-errors/compare/v4.3.1...v4.3.2) (2020-03-13) 95 | 96 | ### Bug Fixes 97 | 98 | * Trigger error handler on contenterror as well ([#191](https://github.com/brightcove/videojs-errors/issues/191)) ([723b2a6](https://github.com/brightcove/videojs-errors/commit/723b2a6)) 99 | 100 | 101 | ## [4.3.1](https://github.com/brightcove/videojs-errors/compare/v4.3.0...v4.3.1) (2020-02-08) 102 | 103 | ### Bug Fixes 104 | 105 | * **lang:** Add missing Arabic translations ([#185](https://github.com/brightcove/videojs-errors/issues/185)) ([708b398](https://github.com/brightcove/videojs-errors/commit/708b398)) 106 | 107 | ### Chores 108 | 109 | * **package:** Update development dependencies ([#186](https://github.com/brightcove/videojs-errors/issues/186)) ([98ce727](https://github.com/brightcove/videojs-errors/commit/98ce727)) 110 | 111 | 112 | # [4.3.0](https://github.com/brightcove/videojs-errors/compare/v4.2.0...v4.3.0) (2019-08-06) 113 | 114 | ### Features 115 | 116 | * Add class to error code line and make colon bold ([#164](https://github.com/brightcove/videojs-errors/issues/164)) ([7734d36](https://github.com/brightcove/videojs-errors/commit/7734d36)) 117 | 118 | ### Bug Fixes 119 | 120 | * **lang:** Improved translations ([#165](https://github.com/brightcove/videojs-errors/issues/165)) ([5aa4877](https://github.com/brightcove/videojs-errors/commit/5aa4877)) 121 | * **lang:** Update Chinese (Simplified) and Chinese (Traditional) and clone them to more correct language codes ([#176](https://github.com/brightcove/videojs-errors/issues/176)) ([568d41d](https://github.com/brightcove/videojs-errors/commit/568d41d)) 122 | 123 | ### Chores 124 | 125 | * **package:** Update dependencies ([#172](https://github.com/brightcove/videojs-errors/issues/172)) ([90e8bbe](https://github.com/brightcove/videojs-errors/commit/90e8bbe)) 126 | * **package:** Update development dependencies. ([#167](https://github.com/brightcove/videojs-errors/issues/167)) ([a0f4f96](https://github.com/brightcove/videojs-errors/commit/a0f4f96)) 127 | * **package:** update lint-staged to version 8.1.0 ([#153](https://github.com/brightcove/videojs-errors/issues/153)) ([9322988](https://github.com/brightcove/videojs-errors/commit/9322988)) 128 | * **package:** update npm-run-all/videojs-generator-verify for security ([d355dd5](https://github.com/brightcove/videojs-errors/commit/d355dd5)) 129 | * **package:** update videojs-generate-karma-config to version 5.0.0 ([#152](https://github.com/brightcove/videojs-errors/issues/152)) ([7ffd25f](https://github.com/brightcove/videojs-errors/commit/7ffd25f)) 130 | * **package:** update videojs-generate-karma-config to version 5.1.0 ([553b961](https://github.com/brightcove/videojs-errors/commit/553b961)) 131 | * **package:** update videojs-generate-rollup-config to version 2.3.1 ([#154](https://github.com/brightcove/videojs-errors/issues/154)) ([f67d225](https://github.com/brightcove/videojs-errors/commit/f67d225)) 132 | * **package:** update videojs-languages to version 2.0.0 ([#142](https://github.com/brightcove/videojs-errors/issues/142)) ([0299e75](https://github.com/brightcove/videojs-errors/commit/0299e75)) 133 | * **package:** update videojs-standard to version 8.0.2 ([#155](https://github.com/brightcove/videojs-errors/issues/155)) ([cd0f8ca](https://github.com/brightcove/videojs-errors/commit/cd0f8ca)) 134 | 135 | 136 | # [4.2.0](https://github.com/brightcove/videojs-errors/compare/v4.1.3...v4.2.0) (2018-10-04) 137 | 138 | ### Bug Fixes 139 | 140 | * ignore progress events ([#143](https://github.com/brightcove/videojs-errors/issues/143)) ([348f670](https://github.com/brightcove/videojs-errors/commit/348f670)) 141 | * Remove the postinstall script to prevent install issues ([#138](https://github.com/brightcove/videojs-errors/issues/138)) ([a2b2839](https://github.com/brightcove/videojs-errors/commit/a2b2839)) 142 | 143 | ### Chores 144 | 145 | * update to generator-videojs-plugin[@7](https://github.com/7).2.0 ([1e77e8c](https://github.com/brightcove/videojs-errors/commit/1e77e8c)) 146 | * **package:** update rollup to version 0.66.0 ([#140](https://github.com/brightcove/videojs-errors/issues/140)) ([459f9fb](https://github.com/brightcove/videojs-errors/commit/459f9fb)) 147 | 148 | 149 | ## [4.1.3](https://github.com/brightcove/videojs-errors/compare/v4.1.2...v4.1.3) (2018-08-23) 150 | 151 | ### Chores 152 | 153 | * generator v7 ([#133](https://github.com/brightcove/videojs-errors/issues/133)) ([365e7b8](https://github.com/brightcove/videojs-errors/commit/365e7b8)) 154 | 155 | 156 | ## [4.1.2](https://github.com/brightcove/videojs-errors/compare/v4.1.1...v4.1.2) (2018-08-03) 157 | 158 | ### Bug Fixes 159 | 160 | * babel the es dist, by updating the generator ([#127](https://github.com/brightcove/videojs-errors/issues/127)) ([983b83f](https://github.com/brightcove/videojs-errors/commit/983b83f)) 161 | 162 | ### Chores 163 | 164 | * **package:** update dependencies, enable greenkeeper ([#126](https://github.com/brightcove/videojs-errors/issues/126)) ([7e95841](https://github.com/brightcove/videojs-errors/commit/7e95841)) 165 | 166 | 167 | ## [4.1.1](https://github.com/brightcove/videojs-errors/compare/v4.1.0...v4.1.1) (2018-07-05) 168 | 169 | ### Chores 170 | 171 | * generator v6 ([#122](https://github.com/brightcove/videojs-errors/issues/122)) ([846d151](https://github.com/brightcove/videojs-errors/commit/846d151)) 172 | 173 | 174 | # [4.1.0](https://github.com/brightcove/videojs-errors/compare/v4.0.0...v4.1.0) (2018-05-08) 175 | 176 | ### Features 177 | 178 | * add standard VERSION property ([#120](https://github.com/brightcove/videojs-errors/issues/120)) ([c475d12](https://github.com/brightcove/videojs-errors/commit/c475d12)) 179 | 180 | 181 | # [4.0.0](https://github.com/brightcove/videojs-errors/compare/v3.1.0...v4.0.0) (2018-05-02) 182 | 183 | ### Features 184 | 185 | * Add timeout getter/setter ([#114](https://github.com/brightcove/videojs-errors/issues/114)) ([cb45723](https://github.com/brightcove/videojs-errors/commit/cb45723)) 186 | * drop v5 support ([#119](https://github.com/brightcove/videojs-errors/issues/119)) ([f4440c1](https://github.com/brightcove/videojs-errors/commit/f4440c1)) 187 | 188 | ### Bug Fixes 189 | 190 | * make the plugin ready for videojs 7 ([#117](https://github.com/brightcove/videojs-errors/issues/117)) ([8d96f2a](https://github.com/brightcove/videojs-errors/commit/8d96f2a)), closes [#116](https://github.com/brightcove/videojs-errors/issues/116) 191 | * Restart timeout monitor if playing when reinitialised ([#113](https://github.com/brightcove/videojs-errors/issues/113)) ([af868ed](https://github.com/brightcove/videojs-errors/commit/af868ed)) 192 | 193 | ### Documentation 194 | 195 | * **README:** Add usage npm/bundler usage ([#108](https://github.com/brightcove/videojs-errors/issues/108)) ([ec86764](https://github.com/brightcove/videojs-errors/commit/ec86764)) 196 | * **README:** fix typo ([ec724b7](https://github.com/brightcove/videojs-errors/commit/ec724b7)) 197 | 198 | 199 | ### BREAKING CHANGES 200 | 201 | * drop v5 support. 202 | 203 | 204 | # [3.1.0](https://github.com/brightcove/videojs-errors/compare/v1.0.0...v3.1.0) (2017-12-13) 205 | 206 | ### Features 207 | 208 | * add custom error for flashls crossdomain errors ([#111](https://github.com/brightcove/videojs-errors/issues/111)) ([9d20fbd](https://github.com/brightcove/videojs-errors/commit/9d20fbd)) 209 | * Add Czech translation ([#106](https://github.com/brightcove/videojs-errors/issues/106)) ([3cb9c1e](https://github.com/brightcove/videojs-errors/commit/3cb9c1e)) 210 | * Add new custom errors and allow defining custom errors at runtime ([#90](https://github.com/brightcove/videojs-errors/issues/90)) ([4bd0cd9](https://github.com/brightcove/videojs-errors/commit/4bd0cd9)) 211 | * Change codes of recently-added errors, allow type and code to be shared, and add `getAll()` method. ([#96](https://github.com/brightcove/videojs-errors/issues/96)) ([f39c0f6](https://github.com/brightcove/videojs-errors/commit/f39c0f6)) 212 | 213 | ### Bug Fixes 214 | 215 | * Fix tests for video.js 6 ([#77](https://github.com/brightcove/videojs-errors/issues/77)) ([0d71164](https://github.com/brightcove/videojs-errors/commit/0d71164)) 216 | * Resolve an issue where 'error' events triggered on the player during contrib-ads playback would not be recognized. ([#109](https://github.com/brightcove/videojs-errors/issues/109)) ([3b48430](https://github.com/brightcove/videojs-errors/commit/3b48430)) 217 | * show spinner if player has stalled ([#104](https://github.com/brightcove/videojs-errors/issues/104)) ([a89513f](https://github.com/brightcove/videojs-errors/commit/a89513f)) 218 | 219 | ### Chores 220 | 221 | * **package:** update browserify to version 13.3.0 ([#58](https://github.com/brightcove/videojs-errors/issues/58)) ([e61edb6](https://github.com/brightcove/videojs-errors/commit/e61edb6)) 222 | * **package:** update karma to version 1.4.1 ([#69](https://github.com/brightcove/videojs-errors/issues/69)) ([7cd5e45](https://github.com/brightcove/videojs-errors/commit/7cd5e45)) 223 | * **package:** update node-sass to version 4.5.0 ([#70](https://github.com/brightcove/videojs-errors/issues/70)) ([f7d7793](https://github.com/brightcove/videojs-errors/commit/f7d7793)) 224 | * **package:** update npm-run-all to version 3.1.2 ([#48](https://github.com/brightcove/videojs-errors/issues/48)) ([0b3f13d](https://github.com/brightcove/videojs-errors/commit/0b3f13d)) 225 | * **package:** update portscanner to version 2.1.1 ([#47](https://github.com/brightcove/videojs-errors/issues/47)) ([b83b979](https://github.com/brightcove/videojs-errors/commit/b83b979)) 226 | * **package:** update shelljs to version 0.7.6 ([#60](https://github.com/brightcove/videojs-errors/issues/60)) ([9b966f6](https://github.com/brightcove/videojs-errors/commit/9b966f6)) 227 | * Add translations for some new strings. ([#101](https://github.com/brightcove/videojs-errors/issues/101)) ([b3dc97a](https://github.com/brightcove/videojs-errors/commit/b3dc97a)) 228 | * Update tooling using generator v5 prerelease. ([#99](https://github.com/brightcove/videojs-errors/issues/99)) ([b0e53e5](https://github.com/brightcove/videojs-errors/commit/b0e53e5)) 229 | * update travis ([#71](https://github.com/brightcove/videojs-errors/issues/71)) ([86d7807](https://github.com/brightcove/videojs-errors/commit/86d7807)) 230 | 231 | ### Code Refactoring 232 | 233 | * Updates for Video.js 6.0 compatibility. ([48ed04a](https://github.com/brightcove/videojs-errors/commit/48ed04a)) 234 | 235 | 236 | ### BREAKING CHANGES 237 | 238 | * Removed Bower support. 239 | * Changed the codes of recently-added errors; so, they will no avoid collisions more reliably with 1.x implementations. 240 | 241 | 242 | ## [3.0.3](https://github.com/brightcove/videojs-errors/compare/v3.0.2...v3.0.3) (2017-09-06) 243 | 244 | ### Features 245 | 246 | * Add Czech translation ([#106](https://github.com/brightcove/videojs-errors/issues/106)) ([3cb9c1e](https://github.com/brightcove/videojs-errors/commit/3cb9c1e)) 247 | 248 | ### Bug Fixes 249 | 250 | * Resolve an issue where 'error' events triggered on the player during contrib-ads playback would not be recognized. ([#109](https://github.com/brightcove/videojs-errors/issues/109)) ([3b48430](https://github.com/brightcove/videojs-errors/commit/3b48430)) 251 | 252 | 253 | ## [3.0.2](https://github.com/brightcove/videojs-errors/compare/v3.0.1...v3.0.2) (2017-06-08) 254 | 255 | ### Bug Fixes 256 | 257 | * show spinner if player has stalled ([#104](https://github.com/brightcove/videojs-errors/issues/104)) ([a89513f](https://github.com/brightcove/videojs-errors/commit/a89513f)) 258 | 259 | 260 | ## [3.0.1](https://github.com/brightcove/videojs-errors/compare/v3.0.0...v3.0.1) (2017-05-22) 261 | 262 | ### Chores 263 | 264 | * Add translations for some new strings. ([#101](https://github.com/brightcove/videojs-errors/issues/101)) ([b3dc97a](https://github.com/brightcove/videojs-errors/commit/b3dc97a)) 265 | 266 | 267 | # [3.0.0](https://github.com/brightcove/videojs-errors/compare/v1.0.0...v3.0.0) (2017-05-19) 268 | 269 | ### Chores 270 | 271 | * Update tooling using generator v5 prerelease. ([#99](https://github.com/brightcove/videojs-errors/issues/99)) ([b0e53e5](https://github.com/brightcove/videojs-errors/commit/b0e53e5)) 272 | 273 | ### BREAKING CHANGES 274 | 275 | * Removed Bower support. 276 | 277 | ## 2.0.2 (2017-05-15) 278 | * Fixed some tooling issues, including missing `dist/lang` files. 279 | 280 | ## 2.0.1 (2017-05-15) 281 | * Fixed mis-configured `package.json` fields. 282 | 283 | ## 2.0.0 (2017-05-15) 284 | * @misteroneill Move off of Spellbook for now and add pkg.module. ([#95](https://github.com/brightcove/videojs-errors/pull/95)) 285 | * @misteroneill __BREAKING CHANGE__: Change codes of recently-added errors, allow type and code to be shared, and add `getAll()` method. ([#95](https://github.com/brightcove/videojs-errors/pull/95)) 286 | 287 | ## 1.3.2 (2017-04-24) 288 | * @forbesjo Added option to disable watching progress events ([#91](https://github.com/brightcove/videojs-errors/pull/91)) 289 | 290 | ## 1.3.1 (2017-04-19) 291 | * @misteroneill Fix size detection to account for players that have no configured dimensions ([#92](https://github.com/brightcove/videojs-errors/pull/92)) 292 | 293 | ## 1.3.0 (2017-04-18) 294 | * @misteroneill Add new custom errors and an `extend` method to customize errors at runtime ([#90](https://github.com/brightcove/videojs-errors/pull/90)) 295 | 296 | ## 1.2.0 (2017-02-21) 297 | * @gfviegas Add support for Portuguese ([#42](https://github.com/brightcove/videojs-errors/pull/42)) 298 | * @bc-paul Allow errors to be non-dismissible ([#54](https://github.com/brightcove/videojs-errors/pull/54)) 299 | 300 | ## v1.1.4 (2017-02-09) 301 | * @misteroneill Remove deprecation warning about using videojs.plugin (#72) 302 | * @BrandonOCasey Update Travis build to run w/ Video.js 5 and 6 (#71) 303 | 304 | ## v1.1.3 (2017-01-27) 305 | * @BrandonOCasey Updates for Video.js 6.0 compatibility. (#67) 306 | 307 | ## v1.1.2 (2016-12-07) 308 | * @forbesjo Error if Flash tech is unusable (#50) 309 | 310 | ## v1.1.1 (2016-11-11) 311 | * @mjneil Cleanup event bindings when reinitialized (#44) 312 | 313 | ## v1.1.0 (2016-09-08) 314 | * @vdeshpande Add in a user-friendly message for disabled Flash in IE (#41) 315 | 316 | ## v1.0.5 (2016-08-10) 317 | * @vdeshpande Close-button errors message accessible fix (#40) 318 | * @mkody Fix typo in French translation (#39) 319 | 320 | ## v1.0.4 (2016-04-13) 321 | * Added listening for `'adtimeupdate'` ([#36](https://github.com/brightcove/videojs-errors/pull/36)) 322 | * Added Arabic and Turkish ([#38](https://github.com/brightcove/videojs-errors/pull/38)) 323 | 324 | ## v1.0.3 (2016-03-01) 325 | * Add Italian, Russian and Chinese (traditional and simplified). 326 | * Add English translations as a canonical template. 327 | 328 | ## v1.0.2 (2016-01-11) 329 | * Bower :) 330 | 331 | ## v1.0.1 (2016-01-11) 332 | * Updated to use `generator-videojs-plugin` conventions. 333 | 334 | ## v1.0.0 (2015-11-22) 335 | * Updates for video.js 5.0. 336 | 337 | ## v0.1.8 (2015-05-05) 338 | * Do not display errors when the player is paused or ended for timeouts. 339 | * Fix a vdata exception when dispose is called. 340 | 341 | ## v0.1.6 (2014-09-10) 342 | * Remove `dist/` from `.npmignore`. 343 | 344 | ## v0.1.5 (2014-09-03) 345 | * More localization improvements. 346 | 347 | ## v0.1.4 (2014-08-27) 348 | * Fix `dist/`. 349 | 350 | ## v0.1.3 (2014-08-27) 351 | * Localization improvements. 352 | 353 | ## v0.1.2 (2014-08-19) 354 | * Localization 355 | 356 | ## v0.1.1 (2014-06-13) 357 | * Ended videos should not cause player timeouts on IE11/Win8rt. 358 | 359 | ## v0.1.0 (2014-06-05) 360 | * Initial release. 361 | 362 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # CONTRIBUTING 2 | 3 | We welcome contributions from everyone! 4 | 5 | ## Getting Started 6 | 7 | Make sure you have Node.js 4.8 or higher and npm installed. 8 | 9 | 1. Fork this repository and clone your fork 10 | 1. Install dependencies: `npm install` 11 | 1. Run a development server: `npm start` 12 | 13 | ### Making Changes 14 | 15 | Refer to the [video.js plugin conventions][conventions] for more detail on best practices and tooling for video.js plugin authorship. 16 | 17 | When you've made your changes, push your commit(s) to your fork and issue a pull request against the original repository. 18 | 19 | ### Running Tests 20 | 21 | Testing is a crucial part of any software project. For all but the most trivial changes (typos, etc) test cases are expected. Tests are run in actual browsers using [Karma][karma]. 22 | 23 | - In all available and supported browsers: `npm test` 24 | - In a specific browser: `npm run test:chrome`, `npm run test:firefox`, etc. 25 | - While development server is running (`npm start`), navigate to [`http://localhost:9999/test/`][local] 26 | 27 | 28 | [karma]: http://karma-runner.github.io/ 29 | [local]: http://localhost:9999/test/ 30 | [conventions]: https://github.com/videojs/generator-videojs-plugin/blob/master/docs/conventions.md 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright Brightcove, Inc. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # videojs-errors 2 | 3 | [![Build Status](https://travis-ci.org/brightcove/videojs-errors.svg?branch=master)](https://travis-ci.org/brightcove/videojs-errors) 4 | [![Greenkeeper badge](https://badges.greenkeeper.io/brightcove/videojs-errors.svg)](https://greenkeeper.io/) 5 | [![Slack Status](http://slack.videojs.com/badge.svg)](http://slack.videojs.com) 6 | 7 | [![NPM](https://nodei.co/npm/videojs-errors.png?downloads=true&downloadRank=true)](https://nodei.co/npm/videojs-errors/) 8 | 9 | A plugin that displays user-friendly messages when Video.js encounters an error. 10 | 11 | Maintenance Status: Stable 12 | 13 | 14 | 15 | 16 | - [Getting Started](#getting-started) 17 | - [Localization](#localization) 18 | - [Supported Errors](#supported-errors) 19 | - [Custom Errors](#custom-errors) 20 | - [Custom Errors without a Type](#custom-errors-without-a-type) 21 | - [`getAll()`](#getall) 22 | - [`timeout()`](#timeout) 23 | - [`backgroundTimeout()`](#backgroundtimeout) 24 | - [Known Issues](#known-issues) 25 | 26 | 27 | 28 | ## Getting Started 29 | **Importing via npm/Babel/Browserify/webpack** 30 | ```shell 31 | npm install videojs-errors 32 | ``` 33 | 34 | Then import in your JavaScript 35 | ```js 36 | import videojs from 'video.js'; 37 | import 'videojs-errors'; 38 | ``` 39 | 40 | Installing the styles will depend on your build tool. 41 | [Here's](http://brunch.io/docs/using-modules) an example of including styles with brunch. See Including Module's styles section. 42 | 43 | 44 | **Importing via script tag** 45 | 46 | The plugin automatically registers itself when you include videojs.errors.js in your page: 47 | 48 | ```html 49 | 50 | ``` 51 | 52 | You probably want to include the default stylesheet, too. It displays error messages as a semi-transparent overlay on top of the video element itself. It's designed to match up fairly well with the default Video.js styles: 53 | 54 | ```html 55 | 56 | ``` 57 | 58 | If you're not a fan of the default styling, you can drop in your own stylesheet. The only new element to worry about is `vjs-errors-dialog` which is the container for the error messages. 59 | 60 | ### Localization 61 | The plugin supports multiple languages when using Video.JS v4.7.3 or greater. In order to add additional language support, add the language file after your plugin as follows: 62 | 63 | ```html 64 | 65 | 66 | ``` 67 | 68 | **Note:** A formatted example is available for Spanish under 'lang/es.js'. 69 | 70 | ### Supported Errors 71 | 72 | Once you've initialized Video.js, you can activate the errors plugin. The plugin has a set of default error messages for the standard HTML5 video errors keyed off their runtime values: 73 | 74 | - MEDIA_ERR_ABORTED (numeric value `1`) 75 | - MEDIA_ERR_NETWORK (numeric value `2`) 76 | - MEDIA_ERR_DECODE (numeric value `3`) 77 | - MEDIA_ERR_SRC_NOT_SUPPORTED (numeric value `4`) 78 | - MEDIA_ERR_ENCRYPTED (numeric value `5`) 79 | 80 | ### Custom Errors 81 | 82 | Additionally, some custom errors have been added as reference for future extension. 83 | 84 | - MEDIA_ERR_UNKNOWN (value `'unknown'`) 85 | - PLAYER_ERR_NO_SRC (numeric value `-1`) 86 | - PLAYER_ERR_TIMEOUT (numeric value `-2`) 87 | - PLAYER_ERR_DOMAIN_RESTRICTED 88 | - PLAYER_ERR_IP_RESTRICTED 89 | - PLAYER_ERR_GEO_RESTRICTED 90 | 91 | **Note:** 92 | 93 | - Custom errors should reference a code value of a string. 94 | - Two of the provided errors use negative numbers for historical reasons, but alpha-numeric/descriptive strings are less likely to cause collision issues. 95 | - Custom errors should have a `type` beginning with `PLAYER_ERR_` versus the standardized `MEDIA_ERR` to avoid confusion. 96 | - Custom errors can be chosen to be dismissible (boolean value `true`) 97 | 98 | If the video element emits any of those errors, the corresponding error message will be displayed. You can override and add custom error codes by supplying options to the plugin: 99 | 100 | ```js 101 | player.errors({ 102 | errors: { 103 | 3: { 104 | headline: 'This is an override for the generic MEDIA_ERR_DECODE', 105 | message: 'This is a custom error message' 106 | } 107 | } 108 | }); 109 | ``` 110 | 111 | Or by calling `player.errors.extend` _after_ initializing the plugin: 112 | 113 | ```js 114 | player.errors(); 115 | 116 | player.errors.extend({ 117 | 3: { 118 | headline: 'This is an override for the generic MEDIA_ERR_DECODE', 119 | message: 'This is a custom error message' 120 | }, 121 | foo: { 122 | headline: 'My custom "foo" error', 123 | message: 'A custom "foo" error message.', 124 | type: 'PLAYER_ERR_FOO' 125 | } 126 | }); 127 | ``` 128 | 129 | If you define custom error messages, you'll need to let Video.js know when to emit them yourself: 130 | 131 | ```js 132 | player.error({code: 'foo', dismiss: true}); 133 | ``` 134 | 135 | If an error is emitted that doesn't have an associated key, a generic, catch-all message is displayed. You can override that text by supplying a message for the key `unknown`. 136 | 137 | ### Custom Errors without a Type 138 | 139 | As of v2.0.0, custom errors can be defined without a code. In these cases, the key provided will be used as the code. For example, the custom `foo` error above could be: 140 | 141 | ```js 142 | player.errors.extend({ 143 | PLAYER_ERR_FOO: { 144 | headline: 'My custom "foo" error', 145 | message: 'A custom "foo" error message.' 146 | } 147 | }); 148 | ``` 149 | 150 | The difference here being that one would then trigger it via: 151 | 152 | ```js 153 | player.error({code: 'PLAYER_ERR_FOO'}); 154 | ``` 155 | 156 | ### `getAll()` 157 | 158 | After the errors plugin has been initialized on a player, a `getAll()` method is available on the `errors()` plugin method. This function returns an object with all the errors the plugin currently understands: 159 | 160 | ```js 161 | player.errors(); 162 | 163 | var errors = player.errors.getAll(); 164 | 165 | console.log(errors['1'].type); // "MEDIA_ERR_ABORTED" 166 | ``` 167 | 168 | ### `timeout()` 169 | 170 | After the errors plugin has been initialized on a player, a `timeout()` method is available on the `errors()` plugin method. 171 | 172 | A new timeout may be set by passing a timeout in milliseconds, e.g. `player.errors.timeout(5 * 1000)`. 173 | 174 | Setting the timeout to `Infinity` or `-1` will turn off this check. 175 | 176 | If no argument is passed, the current timeout value is returned. 177 | 178 | ### `backgroundTimeout()` 179 | 180 | This functions exactly like [timeout](#timeout) except the default value is 5 minutes. 181 | 182 | ## Known Issues 183 | 184 | On iPhones, default errors are not dismissible. The video element intercepts all user interaction so error message dialogs miss the tap events. If your video is busted anyways, you may not be that upset about this. 185 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | videojs-errors Demo 6 | 7 | 8 | 9 | 10 | 23 | 24 | 25 |
26 |

27 | You can see the Video.js Errors plugin in action below. 28 | Look at the source of this page to see how to use it with your videos. 29 |

30 |
31 |

Standard Video Player Size 640x360

32 | 43 |
44 |

Mini Video Player Size 300x150

45 | 56 | 57 | 60 | 61 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /lang/ar.json: -------------------------------------------------------------------------------- 1 | { 2 | "No video has been loaded": "لم يتم تحميل أي فيديو", 3 | "Could not download the video": "لا يمكن تحميل الفيديو", 4 | "The video connection was lost, please confirm you are connected to the internet": "فقد الاتصال بالفيديو، يرجى التأكد من الاتصال بالإنترنت", 5 | "The video is bad or in a format that cannot be played on your browser": "جودة الفيديو سيئة أو في صيغة لا يمكن للمتصفحك تشغيلها", 6 | "This video is either unavailable or not supported in this browser": "هذا الفيديو غير متوفر أو غير معتمد في هذا المتصفح", 7 | "Error Code": "كود الخطأ", 8 | "Technical details": "تفاصيل تقنية", 9 | "The video download was cancelled": "تم إلغاء تحميل الفيديو", 10 | "The video you are trying to watch is encrypted and we do not know how to decrypt it": "تم تشفير الفيديو الذي تحاول مشاهدته و لا نعرف كيفية فك تشفيرها", 11 | "An unanticipated problem was encountered, check back soon and try again": "نواجه مشكلة غير متوقعة، تحقق وحاول مرة أخرى بعد قليل", 12 | "This video is restricted from playing on your current domain": "يحظر تشغيل مقطع الفيديو هذا في نطاقك الحالي", 13 | "This video is restricted at your current IP address": "يحظر تشغيل مقطع الفيديو هذا في عنوان IP الحالي الخاص بك", 14 | "This video is restricted from playing in your current geographic region": "يحظر تشغيل مقطع الفيديو هذا في منطقتك الجغرافية الحالية", 15 | "OK": "موافق" 16 | } 17 | -------------------------------------------------------------------------------- /lang/cs.json: -------------------------------------------------------------------------------- 1 | { 2 | "No video has been loaded": "Nebylo načteno žádné video", 3 | "Could not download the video": "Video nelze stáhnout", 4 | "The video connection was lost, please confirm you are connected to the internet": "Připojení k serveru s videem bylo ztraceno, ujistěte se, že jste připojeni k internetu", 5 | "The video is bad or in a format that cannot be played on your browser": "Video je požkozené, nebo je ve formátu, který nemůže být vaším prohlížečem přehrán", 6 | "This video is either unavailable or not supported in this browser": "Toto video je buď nedostupné nebo nepodporované v tomto prohlížeči", 7 | "Error Code": "Chybový kód", 8 | "Technical details": "Technické informace", 9 | "The video download was cancelled": "Stahování videa bylo přerušeno", 10 | "The video you are trying to watch is encrypted and we do not know how to decrypt it": "Video, které se snažíte přehrát, je zašifrované a my nevíme, jak ho rozšifrovat", 11 | "An unanticipated problem was encountered, check back soon and try again": "Nastal neočekávaný problém, zkuste to brzy znovu", 12 | "This video is restricted from playing on your current domain": "Toto video je zakázané přehrávat na vaší současné doméně", 13 | "This video is restricted at your current IP address": "Toto video je zakázané přehrávat na vaší současné IP adrese", 14 | "This video is restricted from playing in your current geographic region": "Toto video je zakázané přehrávat ve vaší současné geografické oblasti", 15 | "OK": "OK" 16 | } 17 | -------------------------------------------------------------------------------- /lang/de.json: -------------------------------------------------------------------------------- 1 | { 2 | "No video has been loaded": "Es wurde kein Video geladen.", 3 | "Could not download the video": "Das Video konnte nicht heruntergeladen werden.", 4 | "The video connection was lost, please confirm you are connected to the internet": "Die Videoverbindung wurde abgebrochen. Überprüfen Sie ob eine Internetverbindung besteht.", 5 | "The video is bad or in a format that cannot be played on your browser": "Dieses Video ist beschädigt oder das Format ist mit Ihrem Browser nicht abspielbar.", 6 | "This video is either unavailable or not supported in this browser": "Dieses Video ist entweder nicht verfügbar oder es wird in diesem Browser nicht unterstützt.", 7 | "Error Code": "Fehlercode", 8 | "Technical details": "Technische Details", 9 | "The video download was cancelled": "Das Herunterladen des Videos wurde abgebrochen.", 10 | "The video you are trying to watch is encrypted and we do not know how to decrypt it": "Das Video das Sie versuchen abzuspielen ist verschlüsselt und es kann nicht entschlüsselt werden.", 11 | "An unanticipated problem was encountered, check back soon and try again": "Ein unvorhergesehenes Problem ist aufgetreten, versuchen Sie es in Kürze noch einmal.", 12 | "This video is restricted from playing on your current domain": "Dieses Video darf aufgrund von Einschränkungen für Ihre Domäne nicht abgespielt werden.", 13 | "This video is restricted at your current IP address": "Ihre IP-Adresse ist nicht berechtigt auf dieses Video zuzugreifen.", 14 | "This video is restricted from playing in your current geographic region": "Dieses Video darf aufgrund von Einschränkungen für Ihre geografische Region nicht abgespielt werden.", 15 | "OK": "OK" 16 | } 17 | -------------------------------------------------------------------------------- /lang/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "No video has been loaded": "No video has been loaded", 3 | "Could not download the video": "Could not download the video", 4 | "The video connection was lost, please confirm you are connected to the internet": "The video connection was lost, please confirm you are connected to the internet", 5 | "The video is bad or in a format that cannot be played on your browser": "The video is bad or in a format that cannot be played on your browser", 6 | "This video is either unavailable or not supported in this browser": "This video is either unavailable or not supported in this browser", 7 | "Error Code": "Error Code", 8 | "Technical details": "Technical details", 9 | "The video download was cancelled": "The video download was cancelled", 10 | "The video you are trying to watch is encrypted and we do not know how to decrypt it": "The video you are trying to watch is encrypted and we do not know how to decrypt it", 11 | "An unanticipated problem was encountered, check back soon and try again": "An unanticipated problem was encountered, check back soon and try again", 12 | "This video is restricted from playing on your current domain": "This video is restricted from playing on your current domain", 13 | "This video is restricted at your current IP address": "This video is restricted at your current IP address", 14 | "This video is restricted from playing in your current geographic region": "This video is restricted from playing in your current geographic region", 15 | "OK": "OK" 16 | } 17 | -------------------------------------------------------------------------------- /lang/es.json: -------------------------------------------------------------------------------- 1 | { 2 | "No video has been loaded": "No hay ningún vídeo cargado.", 3 | "Could not download the video": "No se ha podido descargar el vídeo.", 4 | "The video connection was lost, please confirm you are connected to the internet": "Se ha perdido la conexión al vídeo. Compruebe si su equipo está conectado a internet.", 5 | "The video is bad or in a format that cannot be played on your browser": "El vídeo está dañado o tiene un formato no reproducible en su navegador.", 6 | "This video is either unavailable or not supported in this browser": "Este vídeo no está disponible o es incompatible con este navegador.", 7 | "Error Code": "Código de error", 8 | "Technical details": "Detalles técnicos", 9 | "The video download was cancelled": "La descarga ha sido cancelada", 10 | "The video you are trying to watch is encrypted and we do not know how to decrypt it": "El vídeo que está tratando de visualizar está encriptado y no es posible desencriptarlo", 11 | "An unanticipated problem was encountered, check back soon and try again": "Ha surgido un problema imprevisto. Vuelva a intentarlo más tarde.", 12 | "This video is restricted from playing on your current domain": "Este vídeo tiene restricciones y no puede reproducirse en su dominio actual.", 13 | "This video is restricted at your current IP address": "Este vídeo tiene restricciones y no puede reproducirse en su dirección IP actual.", 14 | "This video is restricted from playing in your current geographic region": "Este vídeo tiene restricciones y no puede reproducirse en su región geográfica actual." 15 | } 16 | -------------------------------------------------------------------------------- /lang/fr.json: -------------------------------------------------------------------------------- 1 | { 2 | "No video has been loaded": "Aucune vidéo n'a été chargée.", 3 | "Could not download the video": "Impossible de télécharger la vidéo.", 4 | "The video connection was lost, please confirm you are connected to the internet": "La connexion à la vidéo a été perdue, vérifiez que vous êtes bien connecté à Internet.", 5 | "The video is bad or in a format that cannot be played on your browser": "La vidéo est de mauvaise qualité ou dans un format incompatible avec votre navigateur.", 6 | "This video is either unavailable or not supported in this browser": "Cette vidéo est indisponible ou non prise en charge par ce navigateur.", 7 | "Error Code": "Code d'erreur", 8 | "Technical details": "Détails techniques", 9 | "An unanticipated problem was encountered, check back soon and try again": "Un problème inattendu s’est produit ; veuillez réessayer ultérieurement.", 10 | "This video is restricted from playing on your current domain": "La lecture de cette vidéo est restreinte sur votre domaine actuel.", 11 | "This video is restricted at your current IP address": "Cette vidéo est restreinte sur votre adresse IP actuelle.", 12 | "This video is restricted from playing in your current geographic region": "La lecture de cette vidéo est restreinte dans votre région géographique." 13 | } 14 | -------------------------------------------------------------------------------- /lang/gl.json: -------------------------------------------------------------------------------- 1 | { 2 | "No video has been loaded": "Non se cargou ningún vídeo", 3 | "Could not download the video": "Non se puido descargar o vídeo", 4 | "The video connection was lost, please confirm you are connected to the internet": "Perdeuse a conexión ao vídeo. Comprobe que o seu equipo estea conectado a Internet", 5 | "The video is bad or in a format that cannot be played on your browser": "O vídeo está danado ou ten un formato incompatible co navegador", 6 | "This video is either unavailable or not supported in this browser": "Este vídeo non está dispoñible ou é incompatible co navegador", 7 | "Error Code": "Código de erro", 8 | "Technical details": "Detalles técnicos", 9 | "The video download was cancelled": "Cancelouse a descarga", 10 | "The video you are trying to watch is encrypted and we do not know how to decrypt it": "O vídeo que está tratando de reproducir está encriptado e non é posible desencriptalo", 11 | "An unanticipated problem was encountered, check back soon and try again": "Xurdiu un problema imprevisto. Volva a intentalo máis tarde.", 12 | "This video is restricted from playing on your current domain": "Este vídeo ten restriccións e non pode reproducirse no seu dominio actual.", 13 | "This video is restricted at your current IP address": "Este vídeo ten restriccións e non pode reproducirse na súa dirección IP actual.", 14 | "This video is restricted from playing in your current geographic region": "Este vídeo ten restriccións e non pode reproducirse na súa rexión xeográfica actual." 15 | } 16 | -------------------------------------------------------------------------------- /lang/it.json: -------------------------------------------------------------------------------- 1 | { 2 | "No video has been loaded": "Nessun video caricato", 3 | "Could not download the video": "Impossibile eseguire il download dei video", 4 | "The video connection was lost, please confirm you are connected to the internet": "La connessione al video è andata persa. Verificare di essere connessi a Internet", 5 | "The video is bad or in a format that cannot be played on your browser": "Il video è corrotto oppure è in un formato non riproducibile dal browser in uso", 6 | "This video is either unavailable or not supported in this browser": "Questo video non è disponibile oppure non è supportato dal browser in uso", 7 | "Error Code": "Codice di Errore", 8 | "Technical details": "Dettagli tecnici", 9 | "The video download was cancelled": "Nessun video caricato", 10 | "The video you are trying to watch is encrypted and we do not know how to decrypt it": "Il video che si sta cercando di riprodurre è codificato in un formato non riconosciuto", 11 | "An unanticipated problem was encountered, check back soon and try again": "Si è verificato un problema imprevisto. Riprovare più tardi" 12 | } 13 | -------------------------------------------------------------------------------- /lang/ja.json: -------------------------------------------------------------------------------- 1 | { 2 | "No video has been loaded": "動画をロードできませんでした", 3 | "Could not download the video": "動画をダウンロードできませんでした", 4 | "The video connection was lost, please confirm you are connected to the internet": "動画の接続が失われました。インターネットに接続していることをご確認ください", 5 | "The video is bad or in a format that cannot be played on your browser": "動画になんらかの問題があるか、ご使用のブラウザで再生できない動画フォーマットです。", 6 | "This video is either unavailable or not supported in this browser": "該当の動画は現在有効ではない、又は、ご使用のブラウザでサポートされておりません", 7 | "Error Code": "エラーコード", 8 | "Technical details": "テクニカルディテール", 9 | "The video download was cancelled": "動画のダウンロードが中止されました", 10 | "The video you are trying to watch is encrypted and we do not know how to decrypt it": "暗号化された該当の動画の復号化方法が不明です。", 11 | "An unanticipated problem was encountered, check back soon and try again": "予期しない問題が発生しました。少し経ってから確認し再度お試し下さい。", 12 | "This video is restricted from playing on your current domain": "この動画は制限されているため、該当のドメインでは再生できません。", 13 | "This video is restricted at your current IP address": "この動画は制限されているため、ご使用の環境の IP アドレスでは再生できません。", 14 | "This video is restricted from playing in your current geographic region": "この動画は制限されているため、該当の地域からは再生できません。", 15 | "OK": "OK" 16 | } 17 | -------------------------------------------------------------------------------- /lang/ko.json: -------------------------------------------------------------------------------- 1 | { 2 | "No video has been loaded": "비디오가 로드되지 않았습니다.", 3 | "Could not download the video": "비디오를 다운로드하지 못 했습니다.", 4 | "The video connection was lost, please confirm you are connected to the internet": "비디오 연결이 끊겼습니다. 인터넷 연결 상태를 확인하십시오.", 5 | "The video is bad or in a format that cannot be played on your browser": "비디오 상태가 불량이거나 브라우저에서 재생할 수 없는 형식입니다.", 6 | "This video is either unavailable or not supported in this browser": "브라우저에서 이 비디오를 사용할 수 없거나 지원하지 않습니다.", 7 | "Error Code": "오류 코드", 8 | "Technical details": "기술 세부사항", 9 | "The video download was cancelled": "비디오 다운로드가 취소되었습니다", 10 | "The video you are trying to watch is encrypted and we do not know how to decrypt it": "재생하려는 비디오가 암호화 되었지만, 어떻게 복호화 하는지 알 수 없습니다.", 11 | "An unanticipated problem was encountered, check back soon and try again": "예상치 못한 문제가 발생했습니다. 확인 후 다시 시도하십시오.", 12 | "This video is restricted from playing on your current domain": "현재 도메인에서 재생이 제한된 비디오입니다.", 13 | "This video is restricted at your current IP address": "현재 IP 주소에서 제한된 비디오입니다.", 14 | "This video is restricted from playing in your current geographic region": "현 소재지에서 재생이 제한된 비디오입니다.", 15 | "OK": "OK" 16 | } 17 | -------------------------------------------------------------------------------- /lang/pt.json: -------------------------------------------------------------------------------- 1 | { 2 | "No video has been loaded": "Nenhum vídeo foi carregado", 3 | "Could not download the video": "Não foi possível realizar o download do vídeo", 4 | "The video connection was lost, please confirm you are connected to the internet": "A conexão com o vídeo foi perdida. Por favor, confirme que está conectado a internet", 5 | "The video is bad or in a format that cannot be played on your browser": "O vídeo está em um formato que o seu browser não suporta", 6 | "This video is either unavailable or not supported in this browser": "O vídeo não está disponível ou não é suportado pelo seu browser", 7 | "Error Code": "Código do Erro", 8 | "Technical details": "Detalhes técnicos", 9 | "The video download was cancelled": "O download do vídeo foi cancelado", 10 | "The video you are trying to watch is encrypted and we do not know how to decrypt it": "O vídeo que você está tentando assistir é criptografado e nós não sabemos como decodificá-lo", 11 | "An unanticipated problem was encountered, check back soon and try again": "Aconteceu um erro inesperado, tente novamente mais tarde" 12 | } 13 | -------------------------------------------------------------------------------- /lang/ru.json: -------------------------------------------------------------------------------- 1 | { 2 | "No video has been loaded": "Видео не загружено", 3 | "Could not download the video": "Не удалось загрузить видео", 4 | "The video connection was lost, please confirm you are connected to the internet": "Видеосоединение потеряно; проверьте подключение к интернету", 5 | "The video is bad or in a format that cannot be played on your browser": "Видео некачественное или имеет формат, который не воспроизводится вашим браузером", 6 | "This video is either unavailable or not supported in this browser": "Указанное видео недоступно или не поддерживается браузером", 7 | "Error Code": "Код ошибки", 8 | "Technical details": "Технические подробности", 9 | "The video download was cancelled": "Загрузка видео отменена", 10 | "The video you are trying to watch is encrypted and we do not know how to decrypt it": "Видео, которое вы пытаетесь воспроизвести, зашифровано; метод его расшифровки неизвестен", 11 | "An unanticipated problem was encountered, check back soon and try again": "Возникла неожиданная проблема; повторите попытку через некоторое время" 12 | } 13 | -------------------------------------------------------------------------------- /lang/tr.json: -------------------------------------------------------------------------------- 1 | { 2 | "No video has been loaded": "Video yüklenmedi", 3 | "Could not download the video": "Video indirilemedi", 4 | "The video connection was lost, please confirm you are connected to the internet": "Bağlantı kesildi. Lütfen internet bağlantınızı kontrol edin", 5 | "The video is bad or in a format that cannot be played on your browser": "Bozuk veya bu tarayıcıda oynatılmayacak bir video formatıdır", 6 | "This video is either unavailable or not supported in this browser": "Video kullanım dışı veya tarayıcı tarafından desteklenmemektedir", 7 | "Error Code": "Hata Kodu", 8 | "Technical details": "Teknik Detaylar", 9 | "The video download was cancelled": "Video'nun indirimi iptal edildi", 10 | "The video you are trying to watch is encrypted and we do not know how to decrypt it": "Şifresi çözülenemeyen bir video izlemeye çalışıyorsunuz", 11 | "An unanticipated problem was encountered, check back soon and try again": "Beklenmedik bir hata oluştu. Lütfen daha sonra deneyin." 12 | } 13 | -------------------------------------------------------------------------------- /lang/zh-CN.json: -------------------------------------------------------------------------------- 1 | { 2 | "No video has been loaded": "未加载视频", 3 | "Could not download the video": "无法下载视频", 4 | "The video connection was lost, please confirm you are connected to the internet": "视频连接丢失,请确认已连接至互联网", 5 | "The video is bad or in a format that cannot be played on your browser": "视频已损坏,或使用无法在浏览器中播放的格式", 6 | "This video is either unavailable or not supported in this browser": "此视频不可用或不受此浏览器支持", 7 | "Error Code": "错误代码", 8 | "Technical details": "技术细节", 9 | "The video download was cancelled": "视频下载已取消", 10 | "The video you are trying to watch is encrypted and we do not know how to decrypt it": "您尝试观看的视频已加密,而且解密方法未知", 11 | "An unanticipated problem was encountered, check back soon and try again": "发生意外问题,稍后请检查并重试", 12 | "This video is restricted from playing on your current domain": "此视频受到限制,无法在当前域上播放", 13 | "This video is restricted at your current IP address": "此视频受到限制,无法在当前 IP 地址播放", 14 | "This video is restricted from playing in your current geographic region": "此视频受到限制,无法在当前地理区域播放", 15 | "OK": "确定" 16 | } 17 | -------------------------------------------------------------------------------- /lang/zh-TW.json: -------------------------------------------------------------------------------- 1 | { 2 | "No video has been loaded": "未載入任何影片", 3 | "Could not download the video": "無法載入影片", 4 | "The video connection was lost, please confirm you are connected to the internet": "影片連線已中斷,請確認您已連線至網際網路", 5 | "The video is bad or in a format that cannot be played on your browser": "影片錯誤或使用無法在瀏覽器上播放的格式", 6 | "This video is either unavailable or not supported in this browser": "此影片無法使用或在此瀏覽器中不受支援", 7 | "Error Code": "錯誤代碼", 8 | "Technical details": "技術細節", 9 | "The video download was cancelled": "已取消下載影片", 10 | "The video you are trying to watch is encrypted and we do not know how to decrypt it": "您嘗試觀看的影片已加密,而且解密方法未知", 11 | "An unanticipated problem was encountered, check back soon and try again": "發生未預期的問題,請稍後查看並再試一次", 12 | "This video is restricted from playing on your current domain": "此視訊遭到限制,無法在您目前的網域播放", 13 | "This video is restricted at your current IP address": "此視訊遭到限制,無法在您目前的 IP 位址播放", 14 | "This video is restricted from playing in your current geographic region": "此視訊遭到限制,無法在您目前的地理區域播放", 15 | "OK": "確定" 16 | } 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "videojs-errors", 3 | "version": "6.2.0", 4 | "author": "Brightcove, Inc.", 5 | "description": "A Video.js plugin for custom error reporting", 6 | "license": "Apache-2.0", 7 | "main": "dist/videojs-errors.cjs.js", 8 | "module": "dist/videojs-errors.es.js", 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/brightcove/videojs-errors.git" 12 | }, 13 | "keywords": [ 14 | "videojs", 15 | "videojs-plugin" 16 | ], 17 | "generator-videojs-plugin": { 18 | "version": "7.3.2" 19 | }, 20 | "scripts": { 21 | "prebuild": "npm run clean", 22 | "build": "npm-run-all -p build:*", 23 | "build:css": "postcss -o dist/videojs-errors.css --config scripts/postcss.config.js src/plugin.css", 24 | "build:js": "rollup -c scripts/rollup.config.js", 25 | "prebuild:lang": "npm-run-all -s prebuild:lang:*", 26 | "prebuild:lang:chinese-s": "shx cp lang/zh-CN.json lang/zh-Hans.json", 27 | "prebuild:lang:chinese-t": "shx cp lang/zh-TW.json lang/zh-Hant.json", 28 | "build:lang": "vjslang --dir dist/lang", 29 | "postbuild:lang": "shx cp -R lang/* dist/lang/", 30 | "clean": "shx rm -rf ./dist ./test/dist ./lang/zh-Han*.json", 31 | "postclean": "shx mkdir -p ./dist ./test/dist", 32 | "docs": "doctoc README.md", 33 | "lint": "vjsstandard", 34 | "server": "karma start scripts/karma.conf.js --singleRun=false --auto-watch", 35 | "start": "npm-run-all -p server watch", 36 | "pretest": "npm-run-all lint build", 37 | "test": "karma start scripts/karma.conf.js", 38 | "update-changelog": "conventional-changelog -p videojs -i CHANGELOG.md -s", 39 | "version": "is-prerelease || npm run update-changelog && git add CHANGELOG.md", 40 | "watch": "npm-run-all -p watch:*", 41 | "watch:css": "npm run build:css -- -w", 42 | "watch:js": "npm run build:js -- -w", 43 | "posttest": "shx cat test/dist/coverage/text.txt", 44 | "prepublishOnly": "npm run build && vjsverify --skip-es-check" 45 | }, 46 | "vjsstandard": { 47 | "ignore": [ 48 | "dist", 49 | "docs", 50 | "test/dist" 51 | ] 52 | }, 53 | "files": [ 54 | "CONTRIBUTING.md", 55 | "dist/", 56 | "docs/", 57 | "index.html", 58 | "scripts/", 59 | "src/", 60 | "test/" 61 | ], 62 | "dependencies": { 63 | "global": "^4.4.0", 64 | "video.js": "^6 || ^7 || ^8" 65 | }, 66 | "devDependencies": { 67 | "@babel/cli": "^7.17.10", 68 | "conventional-changelog-cli": "^2.0.31", 69 | "conventional-changelog-videojs": "^3.0.2", 70 | "doctoc": "^2.2.0", 71 | "husky": "^1.0.0-rc.13", 72 | "karma": "^6.4.0", 73 | "lint-staged": "^13.0.1", 74 | "not-prerelease": "^1.0.1", 75 | "npm-merge-driver-install": "^3.0.0", 76 | "npm-run-all": "^4.1.5", 77 | "pkg-ok": "^3.0.0", 78 | "postcss": "^8.4.14", 79 | "postcss-cli": "^8.3.1", 80 | "rollup": "^2.61.1", 81 | "shx": "^0.3.2", 82 | "sinon": "^6.1.5", 83 | "videojs-generate-karma-config": "^8.0.1", 84 | "videojs-generate-postcss-config": "^3.0.0", 85 | "videojs-generate-rollup-config": "^7.0.0", 86 | "videojs-generator-verify": "^4.0.1", 87 | "videojs-languages": "^2.0.0", 88 | "videojs-standard": "^9.0.1" 89 | }, 90 | "lint-staged": { 91 | "*.js": "vjsstandard --fix", 92 | "README.md": "npm run docs" 93 | }, 94 | "husky": { 95 | "hooks": { 96 | "pre-commit": "lint-staged" 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /scripts/karma.conf.js: -------------------------------------------------------------------------------- 1 | const generate = require('videojs-generate-karma-config'); 2 | 3 | module.exports = function(config) { 4 | 5 | // see https://github.com/videojs/videojs-generate-karma-config 6 | // for options 7 | const options = {}; 8 | 9 | config = generate(config, options); 10 | 11 | // any other custom stuff not supported by options here! 12 | }; 13 | 14 | -------------------------------------------------------------------------------- /scripts/postcss.config.js: -------------------------------------------------------------------------------- 1 | const generate = require('videojs-generate-postcss-config'); 2 | 3 | module.exports = function(context) { 4 | const result = generate({}, context); 5 | 6 | // do custom stuff here 7 | 8 | return result; 9 | }; 10 | -------------------------------------------------------------------------------- /scripts/rollup.config.js: -------------------------------------------------------------------------------- 1 | const generate = require('videojs-generate-rollup-config'); 2 | 3 | // see https://github.com/videojs/videojs-generate-rollup-config 4 | // for options 5 | const options = {}; 6 | const config = generate(options); 7 | 8 | // Add additonal builds/customization here! 9 | 10 | // export the builds to rollup 11 | export default Object.values(config.builds); 12 | -------------------------------------------------------------------------------- /src/plugin.css: -------------------------------------------------------------------------------- 1 | /* 2 | Default State: Hidden 3 | ------------------------------------------------------------------------------- 4 | */ 5 | 6 | .vjs-error-display { 7 | color: #fff; 8 | display: none; 9 | font-family: Helvetica, Arial, sans serif; 10 | font-size: 16px; 11 | line-height: 1.428; 12 | } 13 | 14 | .vjs-error .vjs-error-display { 15 | display: block; 16 | position: absolute; 17 | top: 0; 18 | left: 0; 19 | right: 0; 20 | bottom: 0; 21 | background: rgba(0, 0, 0, 0.5); 22 | } 23 | 24 | .vjs-error .vjs-error-display .vjs-modal-dialog-content { 25 | font-size: 14px; 26 | } 27 | 28 | /* 29 | Full Size Styles 30 | ------------------------------------------------------------------------------- 31 | */ 32 | 33 | .vjs-errors-dialog { 34 | text-align: left; 35 | border: 1px #999 solid; 36 | overflow: hidden; 37 | position: absolute; 38 | top: 2%; 39 | bottom: 2%; 40 | left: 5%; 41 | right: 5%; 42 | padding-left: 1%; 43 | padding-right: 1%; 44 | background: rgba(24, 24, 24, 0.8); 45 | } 46 | 47 | .vjs-errors-details { 48 | margin-top: 15px; 49 | } 50 | 51 | .vjs-errors-message { 52 | border: 1px #999 solid; 53 | background-color: #2c2c2c; 54 | overflow: auto; 55 | margin-top: 15px; 56 | padding: 15px; 57 | } 58 | 59 | .vjs-errors-ok-button-container { 60 | display: block; 61 | position: absolute; 62 | bottom: 15px; 63 | left: 15px; 64 | right: 15px; 65 | text-align: center; 66 | } 67 | 68 | .vjs-errors-ok-button { 69 | display: block; 70 | margin: 0 auto; 71 | width: 80px; 72 | height: 36px; 73 | background-color: #000; 74 | border: 1px #999 solid; 75 | border-radius: 5px; 76 | color: #999; 77 | font-size: 14px; 78 | cursor: pointer; 79 | } 80 | 81 | .vjs-errors-ok-button:hover { 82 | border: 1px #fff solid; 83 | color: #ccc; 84 | } 85 | 86 | .vjs-errors-timeout-button-container { 87 | display: flex; 88 | flex-direction: row; 89 | justify-content: center; 90 | gap: 10px; 91 | position: absolute; 92 | bottom: 15px; 93 | left: 15px; 94 | right: 15px; 95 | text-align: center; 96 | } 97 | 98 | .vjs-errors-timeout-button-container button { 99 | display: block; 100 | width: 120px; 101 | height: 36px; 102 | background-color: #000; 103 | border: 1px #999 solid; 104 | border-radius: 5px; 105 | color: #999; 106 | font-size: 14px; 107 | cursor: pointer; 108 | } 109 | 110 | .vjs-errors-timeout-button-container button:hover { 111 | border: 1px #fff solid; 112 | color: #ccc; 113 | } 114 | 115 | .vjs-errors-content-container { 116 | overflow: auto; 117 | position: absolute; 118 | padding-bottom: 15px; 119 | top: 0; 120 | left: 15px; 121 | right: 15px; 122 | bottom: 61px; /* dialog padding + ok button + 10 */ 123 | } 124 | 125 | .vjs-errors-headline { 126 | font-size: 14px; 127 | font-weight: bold; 128 | padding-right: 3em; 129 | } 130 | 131 | .vjs-errors-dialog .vjs-control.vjs-close-button { 132 | width: 3em; 133 | height: 3em; 134 | top: 0; 135 | } 136 | 137 | /* 138 | "Extra small" Styles 139 | ------------------------------------------------------------------------------- 140 | */ 141 | 142 | .vjs-xs.vjs-error-display { 143 | font-size: 14px; 144 | background-color: #000; 145 | } 146 | 147 | .vjs-xs.vjs-error-display .vjs-errors-details, 148 | .vjs-xs.vjs-error-display .vjs-errors-message { 149 | display: none; 150 | } 151 | 152 | .vjs-xs .vjs-errors-content-container { 153 | top: 0; 154 | } 155 | 156 | .vjs-xs .vjs-errors-headline { 157 | font-size: 16px; 158 | font-weight: bold; 159 | } 160 | 161 | .vjs-xs .vjs-errors-dialog { 162 | border: none; 163 | top: 0; 164 | bottom: 0; 165 | left: 0; 166 | right: 0; 167 | } 168 | 169 | /* 170 | Media query for player sizes of 600x250 or less. NOTE: This is a 171 | copy of the extra small styles above yet without ".vjs-xs". 172 | ------------------------------------------------------------------------------- 173 | */ 174 | 175 | @media (max-width: 600px), (max-height: 250px) { 176 | .vjs-error-display { 177 | font-size: 14px; 178 | background-color: #000; 179 | } 180 | 181 | .vjs-error-display .vjs-errors-details, 182 | .vjs-error-display .vjs-errors-message { 183 | display: none; 184 | } 185 | 186 | .vjs-error-display .vjs-errors-content-container { 187 | top: 15px; 188 | } 189 | 190 | .vjs-error-display .vjs-errors-headline { 191 | font-size: 16px; 192 | font-weight: bold; 193 | } 194 | 195 | .vjs-error-display .vjs-errors-dialog { 196 | border: none; 197 | top: 0; 198 | bottom: 0; 199 | left: 0; 200 | right: 0; 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /src/plugin.js: -------------------------------------------------------------------------------- 1 | import videojs from 'video.js'; 2 | import document from 'global/document'; 3 | import {version as VERSION} from '../package.json'; 4 | 5 | const merge = (videojs.obj && videojs.obj.merge) || videojs.mergeOptions; 6 | 7 | const defaultDismiss = !videojs.browser.IS_IPHONE; 8 | 9 | // Default options for the plugin. 10 | const defaults = { 11 | header: '', 12 | code: '', 13 | message: '', 14 | timeout: 45 * 1000, 15 | backgroundTimeout: 300 * 1000, 16 | dismiss: defaultDismiss, 17 | errors: { 18 | '1': { 19 | type: 'MEDIA_ERR_ABORTED', 20 | headline: 'The video download was cancelled' 21 | }, 22 | '2': { 23 | type: 'MEDIA_ERR_NETWORK', 24 | headline: 'The video connection was lost, please confirm you are ' + 25 | 'connected to the internet' 26 | }, 27 | '3': { 28 | type: 'MEDIA_ERR_DECODE', 29 | headline: 'The video is bad or in a format that cannot be played on your browser' 30 | }, 31 | '4': { 32 | type: 'MEDIA_ERR_SRC_NOT_SUPPORTED', 33 | headline: 'This video is either unavailable or not supported in this browser' 34 | }, 35 | '5': { 36 | type: 'MEDIA_ERR_ENCRYPTED', 37 | headline: 'The video you are trying to watch is encrypted and we do not know how ' + 38 | 'to decrypt it' 39 | }, 40 | 'unknown': { 41 | type: 'MEDIA_ERR_UNKNOWN', 42 | headline: 'An unanticipated problem was encountered, check back soon and try again' 43 | }, 44 | '-1': { 45 | type: 'PLAYER_ERR_NO_SRC', 46 | headline: 'No video has been loaded' 47 | }, 48 | '-2': { 49 | type: 'PLAYER_ERR_TIMEOUT', 50 | headline: 'It looks like you\'re having playback issues. Reloading the video may help.' 51 | }, 52 | 'PLAYER_ERR_DOMAIN_RESTRICTED': { 53 | headline: 'This video is restricted from playing on your current domain' 54 | }, 55 | 'PLAYER_ERR_IP_RESTRICTED': { 56 | headline: 'This video is restricted at your current IP address' 57 | }, 58 | 'PLAYER_ERR_GEO_RESTRICTED': { 59 | headline: 'This video is restricted from playing in your current geographic region' 60 | } 61 | } 62 | }; 63 | 64 | const initPlugin = function(player, options) { 65 | let monitor; 66 | let waiting; 67 | let isStalling; 68 | const listeners = []; 69 | 70 | const updateErrors = function(updates) { 71 | options.errors = merge(options.errors, updates); 72 | 73 | // Create `code`s from errors which don't have them (based on their keys). 74 | Object.keys(options.errors).forEach(k => { 75 | const err = options.errors[k]; 76 | 77 | if (!err.type) { 78 | err.type = k; 79 | } 80 | }); 81 | }; 82 | 83 | // Make sure we flesh out initially-provided errors. 84 | updateErrors(); 85 | 86 | // clears the previous monitor timeout and sets up a new one 87 | const resetMonitor = function() { 88 | // clear out any existing player error now that playback has recovered 89 | if (player.error() && player.error().code === -2) { 90 | player.error(null); 91 | } 92 | 93 | player.clearTimeout(waiting); 94 | player.clearTimeout(monitor); 95 | 96 | if (isStalling) { 97 | isStalling = false; 98 | 99 | player.removeClass('vjs-waiting'); 100 | } 101 | 102 | // Disable timeouts in the background altogether according to the backgroundTimeout 103 | // option, or if the player is muted, as browsers may throttle javascript timers to 104 | // 1 minute in that case 105 | const disableValues = [Infinity, -1]; 106 | const disableValueMatch = (valArray, option) => valArray.indexOf(option) !== -1; 107 | 108 | if ((document.visibilityState === 'hidden' && 109 | (player.muted() || disableValueMatch(disableValues, options.backgroundTimeout))) || 110 | (document.visibilityState === 'visible' && disableValueMatch(disableValues, options.timeout))) { 111 | return; 112 | } 113 | 114 | // start the loading spinner if player has stalled 115 | waiting = player.setTimeout(function() { 116 | // player already has an error 117 | // or is not playing under normal conditions 118 | if (player.error() || player.paused() || player.ended()) { 119 | return; 120 | } 121 | 122 | isStalling = true; 123 | player.addClass('vjs-waiting'); 124 | }, 1000); 125 | 126 | monitor = player.setTimeout(function() { 127 | // player already has an error 128 | // or is not playing under normal conditions 129 | if (player.error() || player.paused() || player.ended()) { 130 | return; 131 | } 132 | 133 | player.error({ 134 | code: -2, 135 | type: 'PLAYER_ERR_TIMEOUT' 136 | }); 137 | }, document.visibilityState === 'hidden' ? options.backgroundTimeout : options.timeout); 138 | }; 139 | 140 | // clear any previously registered listeners 141 | const cleanup = function() { 142 | let listener; 143 | 144 | while (listeners.length) { 145 | listener = listeners.shift(); 146 | player.off(listener[0], listener[1]); 147 | } 148 | player.clearTimeout(monitor); 149 | player.clearTimeout(waiting); 150 | }; 151 | 152 | // creates and tracks a player listener if the player looks alive 153 | const healthcheck = function(type, fn) { 154 | const check = function() { 155 | // if there's an error do not reset the monitor and 156 | // clear the error unless time is progressing 157 | if (!player.error()) { 158 | // playback isn't expected if the player is paused 159 | if (player.paused()) { 160 | return resetMonitor(); 161 | } 162 | // playback isn't expected once the video has ended 163 | if (player.ended()) { 164 | return resetMonitor(); 165 | } 166 | } 167 | 168 | fn.call(this); 169 | }; 170 | 171 | player.on(type, check); 172 | listeners.push([type, check]); 173 | }; 174 | 175 | const onPlayStartMonitor = function() { 176 | let lastTime = 0; 177 | 178 | cleanup(); 179 | 180 | // if no playback is detected for long enough, trigger a timeout error 181 | resetMonitor(); 182 | healthcheck(['timeupdate', 'adtimeupdate'], function() { 183 | const currentTime = player.currentTime(); 184 | 185 | // playback is operating normally or has recovered 186 | if (currentTime !== lastTime) { 187 | lastTime = currentTime; 188 | resetMonitor(); 189 | } 190 | }); 191 | 192 | // Restart timeout monitor when the document transitions between the 193 | // foreground and background 194 | player.off(document, 'visibilitychange', onPlayStartMonitor); 195 | player.on(document, 'visibilitychange', onPlayStartMonitor); 196 | }; 197 | 198 | const onPlayNoSource = function() { 199 | if (!player.currentSrc()) { 200 | player.error({ 201 | code: -1, 202 | type: 'PLAYER_ERR_NO_SRC' 203 | }); 204 | } 205 | }; 206 | 207 | const onErrorHandler = function() { 208 | let details = ''; 209 | let error = player.error(); 210 | const content = document.createElement('div'); 211 | let dialogContent = ''; 212 | 213 | // In the rare case when `error()` does not return an error object, 214 | // defensively escape the handler function. 215 | if (!error) { 216 | return; 217 | } 218 | 219 | // Stop restarting the monitor on visibilitychanges now that an error has occurred 220 | player.off(document, 'visibilitychange', onPlayStartMonitor); 221 | 222 | error = merge(error, options.errors[error.code || error.type || 0]); 223 | 224 | if (error.message) { 225 | details = `
${player.localize('Technical details')} 226 | :
${player.localize(error.message)}
227 |
`; 228 | } 229 | 230 | const display = player.getChild('errorDisplay'); 231 | 232 | content.className = 'vjs-errors-dialog'; 233 | 234 | const errorCode = `
${this.localize('Error Code')}: ${(error.type || error.code)}
`; 235 | const isTimeoutError = error.code === -2; 236 | 237 | dialogContent = 238 | `
239 |

${this.localize(error.headline)}

240 | ${isTimeoutError ? '' : errorCode} 241 | ${isTimeoutError ? '' : details} 242 |
`; 243 | 244 | const closeable = display.closeable(!('dismiss' in error) || error.dismiss); 245 | 246 | if (isTimeoutError) { 247 | dialogContent += 248 | `
249 | 250 | 251 |
`; 252 | content.innerHTML = dialogContent; 253 | display.fillWith(content); 254 | 255 | display.getChild('closeButton').hide(); 256 | 257 | const reloadButton = display.el().querySelector('.vjs-errors-timeout-button-container > button:first-child'); 258 | const dismissButton = display.el().querySelector('.vjs-errors-timeout-button-container > button:last-child'); 259 | const reloadHandler = () => { 260 | const source = player.currentSource(); 261 | 262 | player.reset(); 263 | player.src(source); 264 | }; 265 | 266 | player.on(reloadButton, 'click', reloadHandler); 267 | 268 | display.one('modalclose', () => { 269 | player.off(reloadButton, 'click', reloadHandler); 270 | player.off(dismissButton, 'click', display.close); 271 | }); 272 | } else if (closeable) { 273 | // We should get a close button 274 | dialogContent += 275 | `
276 | 277 |
`; 278 | content.innerHTML = dialogContent; 279 | display.fillWith(content); 280 | // Get the close button inside the error display 281 | display.contentEl().firstChild.appendChild(display.getChild('closeButton').el()); 282 | 283 | const okButton = display.el().querySelector('.vjs-errors-ok-button'); 284 | 285 | player.on(okButton, 'click', function() { 286 | display.close(); 287 | }); 288 | } else { 289 | content.innerHTML = dialogContent; 290 | display.fillWith(content); 291 | } 292 | 293 | if (player.currentWidth() <= 600 || player.currentHeight() <= 250) { 294 | display.addClass('vjs-xs'); 295 | } 296 | 297 | display.one('modalclose', () => player.error(null)); 298 | }; 299 | 300 | const onDisposeHandler = function() { 301 | cleanup(); 302 | 303 | player.removeClass('vjs-errors'); 304 | player.off('play', onPlayStartMonitor); 305 | player.off(document, 'visibilitychange', onPlayStartMonitor); 306 | player.off('play', onPlayNoSource); 307 | player.off('dispose', onDisposeHandler); 308 | player.off(['aderror', 'error'], onErrorHandler); 309 | }; 310 | 311 | const reInitPlugin = function(newOptions) { 312 | onDisposeHandler(); 313 | initPlugin(player, merge(defaults, newOptions)); 314 | }; 315 | 316 | reInitPlugin.extend = (errors) => updateErrors(errors); 317 | reInitPlugin.getAll = () => merge(options.errors); 318 | 319 | // Get / set timeout value. Restart monitor if changed. 320 | reInitPlugin.timeout = function(timeout) { 321 | if (typeof timeout === 'undefined') { 322 | return options.timeout; 323 | } 324 | if (timeout !== options.timeout) { 325 | options.timeout = timeout; 326 | if (!player.paused()) { 327 | onPlayStartMonitor(); 328 | } 329 | } 330 | }; 331 | 332 | // Get / set backgroundTimeout value. Restart monitor if changed. 333 | reInitPlugin.backgroundTimeout = function(timeout) { 334 | if (typeof timeout === 'undefined') { 335 | return options.backgroundTimeout; 336 | } 337 | if (timeout !== options.backgroundTimeout) { 338 | options.backgroundTimeout = timeout; 339 | if (!player.paused()) { 340 | onPlayStartMonitor(); 341 | } 342 | } 343 | }; 344 | 345 | // no-op API 346 | // TODO: remove in a major version 347 | reInitPlugin.disableProgress = () => {}; 348 | 349 | player.on('play', onPlayStartMonitor); 350 | player.on('play', onPlayNoSource); 351 | player.on('dispose', onDisposeHandler); 352 | player.on(['aderror', 'contenterror', 'error'], onErrorHandler); 353 | 354 | player.ready(() => { 355 | player.addClass('vjs-errors'); 356 | }); 357 | 358 | // if the plugin is re-initialised during playback, start the timeout handler. 359 | if (!player.paused()) { 360 | onPlayStartMonitor(); 361 | } 362 | 363 | // Include the version number. 364 | reInitPlugin.VERSION = VERSION; 365 | 366 | player.errors = reInitPlugin; 367 | }; 368 | 369 | const errors = function(options) { 370 | initPlugin(this, merge(defaults, options)); 371 | }; 372 | 373 | ['extend', 'getAll'].forEach(k => { 374 | errors[k] = function() { 375 | videojs.log.warn(`The errors.${k}() method is not available until the plugin has been initialized!`); 376 | }; 377 | }); 378 | 379 | // Include the version number. 380 | errors.VERSION = VERSION; 381 | 382 | // Register the plugin with video.js. 383 | videojs.registerPlugin('errors', errors); 384 | 385 | export default errors; 386 | -------------------------------------------------------------------------------- /test/plugin.test.js: -------------------------------------------------------------------------------- 1 | import document from 'global/document'; 2 | 3 | import QUnit from 'qunit'; 4 | import sinon from 'sinon'; 5 | import videojs from 'video.js'; 6 | import Events from 'video.js'; 7 | 8 | import plugin from '../src/plugin'; 9 | 10 | const Player = videojs.getComponent('Player'); 11 | 12 | const sources = [{ 13 | src: 'movie.mp4', 14 | type: 'video/mp4' 15 | }, { 16 | src: 'movie.webm', 17 | type: 'video/webm' 18 | }]; 19 | 20 | // Override document.visibilityState so we can set it in tests 21 | Object.defineProperty(document, 'visibilityState', { 22 | value: document.visibilityState, 23 | writable: true 24 | }); 25 | 26 | QUnit.test('the environment is sane', function(assert) { 27 | assert.strictEqual(typeof Array.isArray, 'function', 'es5 exists'); 28 | assert.strictEqual(typeof sinon, 'object', 'sinon exists'); 29 | assert.strictEqual(typeof videojs, 'function', 'videojs exists'); 30 | assert.strictEqual(typeof plugin, 'function', 'plugin is a function'); 31 | }); 32 | 33 | QUnit.module('videojs-errors', { 34 | 35 | beforeEach() { 36 | 37 | // Mock the environment's timers because certain things - particularly 38 | // player readiness - are asynchronous in video.js 5. 39 | this.clock = sinon.useFakeTimers(); 40 | this.fixture = document.getElementById('qunit-fixture'); 41 | this.video = document.createElement('video'); 42 | this.fixture.appendChild(this.video); 43 | this.player = videojs(this.video); 44 | 45 | this.player.buffered = function() { 46 | return (videojs.time && videojs.time.createTimeRanges || videojs.createTimeRange)(0, 0); 47 | }; 48 | this.player.paused = function() { 49 | return false; 50 | }; 51 | this.player.pause = function() { 52 | return false; 53 | }; 54 | 55 | this.originalVisibilityState_ = document.visibilityState; 56 | 57 | // initialize the plugin with the default options 58 | this.player.errors(); 59 | this.errorDisplay = this.player.getChild('errorDisplay'); 60 | 61 | // Tick forward so the player is ready. 62 | this.clock.tick(1); 63 | }, 64 | 65 | afterEach() { 66 | document.visibilityState = this.originalVisibilityState_; 67 | 68 | this.player.dispose(); 69 | this.clock.restore(); 70 | } 71 | }); 72 | 73 | QUnit.test('registers itself with video.js', function(assert) { 74 | assert.expect(2); 75 | 76 | assert.strictEqual( 77 | typeof Player.prototype.errors, 78 | 'function', 79 | 'videojs-errors plugin was registered' 80 | ); 81 | 82 | this.player.errors(); 83 | 84 | // Tick the clock forward enough to trigger the player to be "ready". 85 | this.clock.tick(1); 86 | 87 | assert.ok( 88 | this.player.hasClass('vjs-errors'), 89 | 'the plugin adds a class to the player' 90 | ); 91 | }); 92 | 93 | QUnit.test('play() without a src is an error', function(assert) { 94 | let errors = 0; 95 | 96 | this.player.on('error', function() { 97 | errors++; 98 | }); 99 | this.player.trigger('play'); 100 | 101 | assert.strictEqual(errors, 1, 'emitted an error'); 102 | assert.strictEqual(this.player.error().code, -1, 'error code is -1'); 103 | assert.strictEqual( 104 | this.player.error().type, 105 | 'PLAYER_ERR_NO_SRC', 106 | 'error type is no source' 107 | ); 108 | }); 109 | 110 | QUnit.test('no progress for 1 second shows the loading spinner', function(assert) { 111 | this.player.src(sources); 112 | this.player.trigger('play'); 113 | this.clock.tick(1 * 1000); 114 | 115 | assert.ok( 116 | this.player.hasClass('vjs-waiting'), 117 | 'the plugin adds spinner class to the player' 118 | ); 119 | }); 120 | 121 | QUnit.test('progress events while playing reset the spinner', function(assert) { 122 | this.player.src(sources); 123 | this.player.trigger('play'); 124 | // stalled for awhile 125 | this.clock.tick(44 * 1000); 126 | assert.ok( 127 | this.player.hasClass('vjs-waiting'), 128 | 'the plugin adds spinner class to the player' 129 | ); 130 | 131 | // resume playback 132 | this.player.currentTime = function() { 133 | return 1; 134 | }; 135 | this.player.trigger('timeupdate'); 136 | assert.notOk(this.player.hasClass('vjs-waiting'), 'spinner removed'); 137 | }); 138 | 139 | QUnit.test('no progress for 45 seconds is an error', function(assert) { 140 | let errors = 0; 141 | 142 | this.player.on('error', function() { 143 | errors++; 144 | }); 145 | this.player.src(sources); 146 | this.player.trigger('play'); 147 | 148 | // Triggering playing is necessary as of Video.js 8, which introduced "retry 149 | // on error" behavior 150 | this.player.trigger('playing'); 151 | this.clock.tick(45 * 1000); 152 | 153 | assert.strictEqual(errors, 1, 'emitted an error'); 154 | assert.strictEqual(this.player.error().code, -2, 'error code is -2'); 155 | assert.strictEqual(this.player.error().type, 'PLAYER_ERR_TIMEOUT'); 156 | }); 157 | 158 | QUnit.test('timeout in background is 5 minutes by default if the document is hidden when playback starts', function(assert) { 159 | let errors = 0; 160 | 161 | this.player.on('error', function() { 162 | errors++; 163 | }); 164 | this.player.src(sources); 165 | 166 | document.visibilityState = 'hidden'; 167 | 168 | // simulates case where player starts playing while already in a background 169 | // tab (i.e. no 'visibilitychange' event is observed) 170 | this.player.trigger('play'); 171 | 172 | // Triggering playing is necessary as of Video.js 8, which introduced "retry 173 | // on error" behavior 174 | this.player.trigger('playing'); 175 | 176 | this.clock.tick(1 * 1000); 177 | 178 | assert.ok( 179 | this.player.hasClass('vjs-waiting'), 180 | 'the plugin adds spinner class to the player after 1 sec of no progress' 181 | ); 182 | 183 | this.clock.tick(44 * 1000); 184 | 185 | assert.strictEqual(errors, 0, 'did not emit an error after default foreground 45sec timeout'); 186 | 187 | this.clock.tick(255 * 1000); 188 | 189 | assert.strictEqual(errors, 1, 'emitted an error after 5 minutes'); 190 | assert.strictEqual(this.player.error().code, -2, 'error code is -2'); 191 | assert.strictEqual(this.player.error().type, 'PLAYER_ERR_TIMEOUT'); 192 | }); 193 | 194 | QUnit.test('timeout in background is 5 minutes by default if the document is hidden after playback starts', function(assert) { 195 | let errors = 0; 196 | 197 | this.player.on('error', function() { 198 | errors++; 199 | }); 200 | this.player.src(sources); 201 | 202 | this.player.trigger('play'); 203 | 204 | // Triggering playing is necessary as of Video.js 8, which introduced "retry 205 | // on error" behavior 206 | this.player.trigger('playing'); 207 | 208 | this.clock.tick(1 * 1000); 209 | 210 | assert.ok( 211 | this.player.hasClass('vjs-waiting'), 212 | 'the plugin adds spinner class to the player after 1 sec of no progress' 213 | ); 214 | 215 | // document becomes hidden 216 | document.visibilityState = 'hidden'; 217 | Events.trigger(document, 'visibilitychange'); 218 | 219 | this.clock.tick(299 * 1000); 220 | 221 | assert.strictEqual(errors, 0, 'did not emit an error in background after 4 min 59 sec'); 222 | 223 | this.clock.tick(1 * 1000); 224 | 225 | assert.strictEqual(errors, 1, 'emitted an error after 5 minutes'); 226 | assert.strictEqual(this.player.error().code, -2, 'error code is -2'); 227 | assert.strictEqual(this.player.error().type, 'PLAYER_ERR_TIMEOUT'); 228 | }); 229 | 230 | QUnit.test('background timeout can be set with backgroundTimeout option', function(assert) { 231 | let errors = 0; 232 | 233 | // Init with custom option 234 | this.player.errors({backgroundTimeout: 10 * 1000}); 235 | 236 | this.player.on('error', function() { 237 | errors++; 238 | }); 239 | this.player.src(sources); 240 | 241 | this.player.trigger('play'); 242 | 243 | // Triggering playing is necessary as of Video.js 8, which introduced "retry 244 | // on error" behavior 245 | this.player.trigger('playing'); 246 | 247 | this.clock.tick(1 * 1000); 248 | 249 | assert.ok( 250 | this.player.hasClass('vjs-waiting'), 251 | 'the plugin adds spinner class to the player after 1 sec of no progress' 252 | ); 253 | 254 | // document becomes hidden 255 | document.visibilityState = 'hidden'; 256 | Events.trigger(document, 'visibilitychange'); 257 | 258 | this.clock.tick(9 * 1000); 259 | 260 | assert.strictEqual(errors, 0, 'did not emit an error in background after 9 sec'); 261 | 262 | this.clock.tick(1 * 1000); 263 | 264 | assert.strictEqual(errors, 1, 'emitted an error after 10 sec'); 265 | assert.strictEqual(this.player.error().code, -2, 'error code is -2'); 266 | assert.strictEqual(this.player.error().type, 'PLAYER_ERR_TIMEOUT'); 267 | }); 268 | 269 | QUnit.test('background timeout can be set/get via player.errors.backgroundTimeout()', function(assert) { 270 | let errors = 0; 271 | 272 | assert.strictEqual(this.player.errors.backgroundTimeout(), 300000, 'backgroundTimeout() returns default value'); 273 | 274 | this.player.errors.backgroundTimeout(20 * 1000); 275 | 276 | assert.strictEqual(this.player.errors.backgroundTimeout(), 20000, 'backgroundTimeout() returns new value'); 277 | 278 | this.player.on('error', function() { 279 | errors++; 280 | }); 281 | this.player.src(sources); 282 | 283 | this.player.trigger('play'); 284 | 285 | // Triggering playing is necessary as of Video.js 8, which introduced "retry 286 | // on error" behavior 287 | this.player.trigger('playing'); 288 | 289 | this.clock.tick(1 * 1000); 290 | 291 | assert.ok( 292 | this.player.hasClass('vjs-waiting'), 293 | 'the plugin adds spinner class to the player after 1 sec of no progress' 294 | ); 295 | 296 | // document becomes hidden 297 | document.visibilityState = 'hidden'; 298 | Events.trigger(document, 'visibilitychange'); 299 | 300 | this.clock.tick(19 * 1000); 301 | 302 | assert.strictEqual(errors, 0, 'did not emit an error in background after 19 sec'); 303 | 304 | this.clock.tick(1 * 1000); 305 | 306 | assert.strictEqual(errors, 1, 'emitted an error after 20 sec'); 307 | assert.strictEqual(this.player.error().code, -2, 'error code is -2'); 308 | assert.strictEqual(this.player.error().type, 'PLAYER_ERR_TIMEOUT'); 309 | }); 310 | 311 | QUnit.test('timeout is disabled in background if backgroundTimeout option === Infinity', function(assert) { 312 | let errors = 0; 313 | 314 | // Init with custom option 315 | this.player.errors({backgroundTimeout: Infinity}); 316 | 317 | this.player.on('error', function() { 318 | errors++; 319 | }); 320 | this.player.src(sources); 321 | 322 | this.player.trigger('play'); 323 | 324 | this.clock.tick(1 * 1000); 325 | 326 | assert.ok( 327 | this.player.hasClass('vjs-waiting'), 328 | 'the plugin adds spinner class to the player after 1 sec of no progress' 329 | ); 330 | 331 | // document becomes hidden 332 | document.visibilityState = 'hidden'; 333 | Events.trigger(document, 'visibilitychange'); 334 | 335 | this.clock.tick(300 * 1000); 336 | 337 | assert.strictEqual(errors, 0, 'did not emit an error in background after 5 minutes'); 338 | 339 | this.clock.tick(300 * 1000); 340 | 341 | assert.strictEqual(errors, 0, 'still did not emit an error in background after another 5 minutes'); 342 | }); 343 | 344 | QUnit.test('timeout is disabled in background if backgroundTimeout option === -1', function(assert) { 345 | let errors = 0; 346 | 347 | // Init with custom option 348 | this.player.errors({backgroundTimeout: -1}); 349 | 350 | this.player.on('error', function() { 351 | errors++; 352 | }); 353 | this.player.src(sources); 354 | 355 | this.player.trigger('play'); 356 | 357 | this.clock.tick(1 * 1000); 358 | 359 | assert.ok( 360 | this.player.hasClass('vjs-waiting'), 361 | 'the plugin adds spinner class to the player after 1 sec of no progress' 362 | ); 363 | 364 | // document becomes hidden 365 | document.visibilityState = 'hidden'; 366 | Events.trigger(document, 'visibilitychange'); 367 | 368 | this.clock.tick(300 * 1000); 369 | 370 | assert.strictEqual(errors, 0, 'did not emit an error in background after 5 minutes'); 371 | 372 | this.clock.tick(300 * 1000); 373 | 374 | assert.strictEqual(errors, 0, 'still did not emit an error in background after another 5 minutes'); 375 | }); 376 | 377 | QUnit.test('timeout is disabled in background if the player is muted', function(assert) { 378 | let errors = 0; 379 | 380 | this.player.on('error', function() { 381 | errors++; 382 | }); 383 | this.player.src(sources); 384 | 385 | this.player.trigger('play'); 386 | 387 | this.clock.tick(1 * 1000); 388 | 389 | assert.ok( 390 | this.player.hasClass('vjs-waiting'), 391 | 'the plugin adds spinner class to the player after 1 sec of no progress' 392 | ); 393 | 394 | this.player.muted(true); 395 | 396 | // document becomes hidden 397 | document.visibilityState = 'hidden'; 398 | Events.trigger(document, 'visibilitychange'); 399 | 400 | this.clock.tick(300 * 1000); 401 | 402 | assert.strictEqual(errors, 0, 'did not emit an error in background after 5 minutes'); 403 | }); 404 | 405 | QUnit.test('timeout is disabled in foreground if timeout option === Infinity', function(assert) { 406 | let errors = 0; 407 | 408 | // Init with custom option 409 | this.player.errors({timeout: Infinity}); 410 | 411 | this.player.on('error', function() { 412 | errors++; 413 | }); 414 | this.player.src(sources); 415 | 416 | this.player.trigger('play'); 417 | 418 | this.clock.tick(1 * 1000); 419 | 420 | assert.notOk( 421 | this.player.hasClass('vjs-waiting'), 422 | 'the plugin does not have a spinner class because timeout is disabled' 423 | ); 424 | 425 | // document is visible 426 | document.visibilityState = 'visible'; 427 | 428 | this.clock.tick(45 * 1000); 429 | 430 | assert.strictEqual(errors, 0, 'did not emit an error in background after 45 seconds'); 431 | 432 | this.clock.tick(45 * 1000); 433 | 434 | assert.strictEqual(errors, 0, 'still did not emit an error in background after another 45 seconds'); 435 | }); 436 | 437 | QUnit.test('timeout is disabled in foreground if timeout option === -1', function(assert) { 438 | let errors = 0; 439 | 440 | // Init with custom option 441 | this.player.errors({timeout: -1}); 442 | 443 | this.player.on('error', function() { 444 | errors++; 445 | }); 446 | this.player.src(sources); 447 | 448 | this.player.trigger('play'); 449 | 450 | this.clock.tick(1 * 1000); 451 | 452 | assert.notOk( 453 | this.player.hasClass('vjs-waiting'), 454 | 'the plugin does not have a spinner class because timeout is disabled' 455 | ); 456 | 457 | // document is visible 458 | document.visibilityState = 'visible'; 459 | 460 | this.clock.tick(45 * 1000); 461 | 462 | assert.strictEqual(errors, 0, 'did not emit an error in background after 45 seconds'); 463 | 464 | this.clock.tick(45 * 1000); 465 | 466 | assert.strictEqual(errors, 0, 'still did not emit an error in background after another 45 seconds'); 467 | }); 468 | 469 | QUnit.test('background/foreground timeout toggling is disabled after error has occurred', function(assert) { 470 | let errors = 0; 471 | 472 | this.player.on('error', function() { 473 | errors++; 474 | }); 475 | this.player.src(sources); 476 | this.player.trigger('play'); 477 | 478 | // Triggering playing is necessary as of Video.js 8, which introduced "retry 479 | // on error" behavior 480 | this.player.trigger('playing'); 481 | 482 | this.clock.tick(45 * 1000); 483 | 484 | assert.strictEqual(errors, 1, 'emitted an error'); 485 | assert.strictEqual(this.player.error().code, -2, 'error code is -2'); 486 | assert.strictEqual(this.player.error().type, 'PLAYER_ERR_TIMEOUT'); 487 | 488 | // document becomes hidden then visible 489 | document.visibilityState = 'hidden'; 490 | Events.trigger(document, 'visibilitychange'); 491 | document.visibilityState = 'visible'; 492 | Events.trigger(document, 'visibilitychange'); 493 | 494 | this.clock.tick(1 * 1000); 495 | 496 | // resetMonitor() will set player.error(null) when the 'visibilitychange' handler is 497 | // triggered, so we should expect that *not* to happen if the handler has been removed 498 | assert.strictEqual(errors, 1, 'still has one error'); 499 | assert.ok(this.player.error() !== null, 'error is not null after visibilitychange'); 500 | assert.strictEqual(this.player.error().code, -2, 'error code is -2'); 501 | }); 502 | 503 | QUnit.test('progress events are ignored during timeout', function(assert) { 504 | let errors = 0; 505 | 506 | this.player.on('error', function() { 507 | errors++; 508 | }); 509 | this.player.src(sources); 510 | this.player.trigger('play'); 511 | 512 | // Triggering playing is necessary as of Video.js 8, which introduced "retry 513 | // on error" behavior 514 | this.player.trigger('playing'); 515 | this.clock.tick(40 * 1000); 516 | this.player.trigger('progress'); 517 | this.clock.tick(5 * 1000); 518 | 519 | assert.strictEqual(errors, 1, 'emitted an error'); 520 | assert.strictEqual(this.player.error().code, -2, 'error code is -2'); 521 | assert.strictEqual(this.player.error().type, 'PLAYER_ERR_TIMEOUT'); 522 | }); 523 | 524 | QUnit.test( 525 | 'the plugin cleans up after its previous incarnation when called again', 526 | function(assert) { 527 | let errors = 0; 528 | 529 | this.player.on('error', () => errors++); 530 | 531 | // Call plugin multiple times 532 | this.player.errors(); 533 | this.player.errors(); 534 | 535 | // Tick the clock forward enough to trigger the player to be "ready". 536 | this.clock.tick(1); 537 | 538 | this.player.trigger('play'); 539 | 540 | assert.strictEqual(errors, 1, 'emitted a single error'); 541 | assert.strictEqual(this.player.error().code, -1, 'error code is -1'); 542 | assert.strictEqual(this.player.error().type, 'PLAYER_ERR_NO_SRC'); 543 | } 544 | ); 545 | 546 | QUnit.test('when dispose is triggered should not throw error ', function(assert) { 547 | this.player.src(sources); 548 | this.player.trigger('play'); 549 | this.player.dispose(); 550 | this.clock.tick(45 * 1000); 551 | 552 | assert.ok( 553 | !this.player.error(), 554 | 'should not throw player error when dispose is called.' 555 | ); 556 | 557 | // reset this.player because otherwise afterEach will fail 558 | this.fixture.appendChild(this.video); 559 | this.player = videojs(this.video); 560 | }); 561 | 562 | QUnit.test('progress does not clear player timeout errors', function(assert) { 563 | let errors = 0; 564 | 565 | this.player.on('error', function() { 566 | errors++; 567 | }); 568 | this.player.src(sources); 569 | this.player.trigger('play'); 570 | 571 | // Triggering playing is necessary as of Video.js 8, which introduced "retry 572 | // on error" behavior 573 | this.player.trigger('playing'); 574 | 575 | this.clock.tick(45 * 1000); 576 | 577 | assert.strictEqual(errors, 1, 'emitted an error'); 578 | assert.strictEqual(this.player.error().code, -2, 'error code is -2'); 579 | assert.strictEqual(this.player.error().type, 'PLAYER_ERR_TIMEOUT'); 580 | 581 | this.player.trigger('progress'); 582 | assert.strictEqual(this.player.error().code, -2, 'error code is -2'); 583 | }); 584 | 585 | QUnit.test('reinitialising plugin during playback starts timeout handler', function(assert) { 586 | let errors = 0; 587 | 588 | this.player.on('error', function() { 589 | errors++; 590 | }); 591 | this.player.src(sources); 592 | this.player.trigger('play'); 593 | 594 | // Triggering playing is necessary as of Video.js 8, which introduced "retry 595 | // on error" behavior 596 | this.player.trigger('playing'); 597 | 598 | // reinitialise while playing 599 | this.player.errors(); 600 | 601 | this.clock.tick(45 * 1000); 602 | 603 | assert.strictEqual(errors, 1, 'emitted an error'); 604 | assert.strictEqual(this.player.error().code, -2, 'error code is -2'); 605 | assert.strictEqual(this.player.error().type, 'PLAYER_ERR_TIMEOUT'); 606 | }); 607 | 608 | QUnit.test('timeout can be got and set', function(assert) { 609 | assert.strictEqual(this.player.errors.timeout(), 45 * 1000, 'default timeout reported'); 610 | 611 | this.player.errors.timeout(1 * 1000); 612 | 613 | assert.strictEqual(this.player.errors.timeout(), 1 * 1000, 'timeout was updated'); 614 | }); 615 | 616 | QUnit.test('updating timeout during playback restarts timeout monitor', function(assert) { 617 | let errors = 0; 618 | 619 | this.player.on('error', function() { 620 | errors++; 621 | }); 622 | this.player.src(sources); 623 | this.player.trigger('play'); 624 | 625 | // Triggering playing is necessary as of Video.js 8, which introduced "retry 626 | // on error" behavior 627 | this.player.trigger('playing'); 628 | 629 | // reinitialise while playing 630 | this.player.errors.timeout(1000); 631 | 632 | this.clock.tick(1 * 1000); 633 | 634 | assert.strictEqual(errors, 1, 'emitted an error'); 635 | assert.strictEqual(this.player.error().code, -2, 'error code is -2'); 636 | assert.strictEqual(this.player.error().type, 'PLAYER_ERR_TIMEOUT'); 637 | }); 638 | 639 | // safari 7 on OSX can emit stalls when playback is just fine 640 | QUnit.test('stalling by itself is not an error', function(assert) { 641 | this.player.src(sources); 642 | this.player.trigger('play'); 643 | this.player.trigger('stalled'); 644 | 645 | assert.ok(!this.player.error(), 'no error fired'); 646 | }); 647 | 648 | QUnit.test('timing out multiple times only throws a single error', function(assert) { 649 | let errors = 0; 650 | 651 | this.player.on('error', function() { 652 | errors++; 653 | }); 654 | this.player.src(sources); 655 | this.player.trigger('play'); 656 | // trigger a player timeout 657 | this.clock.tick(45 * 1000); 658 | assert.strictEqual(errors, 1, 'one error fired'); 659 | 660 | // wait long enough for another timeout 661 | this.clock.tick(50 * 1000); 662 | assert.strictEqual(errors, 1, 'only one error fired'); 663 | }); 664 | 665 | QUnit.test('no signs of playback triggers a player timeout', function(assert) { 666 | let errors = 0; 667 | 668 | this.player.src(sources); 669 | this.player.on('error', function() { 670 | errors++; 671 | }); 672 | // swallow any timeupdate events 673 | this.player.on('timeupdate', function(event) { 674 | event.stopImmediatePropagation(); 675 | }); 676 | this.player.trigger('play'); 677 | 678 | // Triggering playing is necessary as of Video.js 8, which introduced "retry 679 | // on error" behavior 680 | this.player.trigger('playing'); 681 | this.clock.tick(45 * 1000); 682 | 683 | assert.strictEqual(errors, 1, 'emitted a single error'); 684 | assert.strictEqual(this.player.error().code, -2, 'error code is -2'); 685 | assert.strictEqual( 686 | this.player.error().type, 687 | 'PLAYER_ERR_TIMEOUT', 688 | 'type is player timeout' 689 | ); 690 | }); 691 | 692 | QUnit.test('time changes while playing reset the player timeout', function(assert) { 693 | let errors = 0; 694 | 695 | this.player.src(sources); 696 | this.player.on('error', function() { 697 | errors++; 698 | }); 699 | this.player.trigger('play'); 700 | this.clock.tick(44 * 1000); 701 | this.player.currentTime = function() { 702 | return 1; 703 | }; 704 | this.player.trigger('timeupdate'); 705 | this.clock.tick(10 * 1000); 706 | 707 | assert.strictEqual(errors, 0, 'no error emitted'); 708 | }); 709 | 710 | QUnit.test('time changes while playing ads reset the player timeout', function(assert) { 711 | let errors = 0; 712 | 713 | this.player.src(sources); 714 | this.player.on('error', function() { 715 | errors++; 716 | }); 717 | this.player.trigger('play'); 718 | this.clock.tick(44 * 1000); 719 | this.player.currentTime = function() { 720 | return 1; 721 | }; 722 | this.player.trigger('adtimeupdate'); 723 | this.clock.tick(10 * 1000); 724 | 725 | assert.strictEqual(errors, 0, 'no error emitted'); 726 | }); 727 | 728 | QUnit.test('time changes after a player timeout clears the error', function(assert) { 729 | this.player.src(sources); 730 | this.player.trigger('play'); 731 | this.clock.tick(45 * 1000); 732 | this.player.currentTime = function() { 733 | return 1; 734 | }; 735 | this.player.trigger('timeupdate'); 736 | 737 | assert.ok(!this.player.error(), 'cleared the timeout'); 738 | }); 739 | 740 | QUnit.test('player timeouts do not occur if the player is paused', function(assert) { 741 | let errors = 0; 742 | 743 | this.player.src(sources); 744 | this.player.on('error', function() { 745 | errors++; 746 | }); 747 | this.player.on('timeupdate', function(event) { 748 | event.stopImmediatePropagation(); 749 | }); 750 | this.player.trigger('play'); 751 | // simulate a misbehaving player that doesn't fire `paused` 752 | this.player.paused = function() { 753 | return true; 754 | }; 755 | this.clock.tick(45 * 1000); 756 | 757 | assert.strictEqual(errors, 0, 'no error emitted'); 758 | }); 759 | 760 | // video.paused is false at the end of a video on IE11, Win8 RT 761 | QUnit.test('player timeouts do not occur if the video is ended', function(assert) { 762 | let errors = 0; 763 | 764 | this.player.src(sources); 765 | this.player.on('error', function() { 766 | errors++; 767 | }); 768 | this.player.on('timeupdate', function(event) { 769 | event.stopImmediatePropagation(); 770 | }); 771 | this.player.trigger('play'); 772 | 773 | // Triggering playing is necessary as of Video.js 8, which introduced "retry 774 | // on error" behavior 775 | this.player.trigger('playing'); 776 | // simulate a misbehaving player that doesn't fire `ended` 777 | this.player.ended = function() { 778 | return true; 779 | }; 780 | this.clock.tick(45 * 1000); 781 | 782 | assert.strictEqual(errors, 0, 'no error emitted'); 783 | }); 784 | 785 | QUnit.test('player timeouts do not overwrite existing errors', function(assert) { 786 | this.player.src(sources); 787 | this.player.trigger('play'); 788 | 789 | // Triggering playing is necessary as of Video.js 8, which introduced "retry 790 | // on error" behavior 791 | this.player.trigger('playing'); 792 | this.player.error({ 793 | type: 'custom', 794 | code: -7 795 | }); 796 | this.clock.tick(45 * 1000); 797 | 798 | assert.strictEqual(-7, this.player.error().code, 'error was not overwritten'); 799 | }); 800 | 801 | QUnit.test('unrecognized error codes do not cause exceptions', function(assert) { 802 | let errors = 0; 803 | 804 | this.player.on('error', function() { 805 | errors++; 806 | }); 807 | this.player.error({ 808 | code: 'something-custom-that-no-one-could-have-predicted', 809 | type: 'NOT_AN_ERROR_CONSTANT' 810 | }); 811 | assert.ok(true, 'does not throw an exception'); 812 | assert.strictEqual(errors, 1, 'emitted an error'); 813 | 814 | // intentionally missing properties 815 | this.player.error({}); 816 | assert.ok(true, 'does not throw an exception'); 817 | 818 | assert.strictEqual(errors, 2, 'emitted an error'); 819 | }); 820 | 821 | QUnit.test('custom error details should override defaults', function(assert) { 822 | const customError = {headline: 'test headline', message: 'test details'}; 823 | 824 | // initialize the plugin with custom options 825 | this.player.errors({errors: {4: customError}}); 826 | // tick forward enough to ready the player 827 | this.clock.tick(1); 828 | // trigger the error in question 829 | this.player.error(4); 830 | // confirm results 831 | assert.strictEqual( 832 | this.errorDisplay.$('.vjs-errors-headline').textContent, 833 | customError.headline, 'headline should match custom override value' 834 | ); 835 | assert.strictEqual( 836 | this.errorDisplay.$('.vjs-errors-message').textContent, 837 | customError.message, 'message should match custom override value' 838 | ); 839 | }); 840 | 841 | QUnit.test('default error is dismissible', function(assert) { 842 | // initialize the plugin 843 | this.player.errors(); 844 | // tick forward enough to ready the player 845 | this.clock.tick(1); 846 | // trigger the error in question 847 | this.player.error(2); 848 | // confirm results 849 | assert.ok(this.errorDisplay.$('.vjs-errors-ok-button'), 'ok button is present'); 850 | assert.ok(this.errorDisplay.$('.vjs-close-button'), 'close button is present'); 851 | }); 852 | 853 | QUnit.test('custom error is dismissible', function(assert) { 854 | const customErrorDismiss = { 855 | headline: 'test headline', 856 | message: 'test details', 857 | dismiss: true 858 | }; 859 | 860 | // initialize the plugin with custom options 861 | this.player.errors({errors: {4: customErrorDismiss}}); 862 | // tick forward enough to ready the player 863 | this.clock.tick(1); 864 | // trigger the error in question 865 | this.player.error(4); 866 | // confirm results 867 | assert.ok(this.errorDisplay.$('.vjs-errors-ok-button'), 'ok button is present'); 868 | assert.ok(this.errorDisplay.$('.vjs-close-button'), 'close button is present'); 869 | }); 870 | 871 | QUnit.test('custom error is not dismissible', function(assert) { 872 | const customErrorNoDimiss = { 873 | headline: 'test headline', 874 | message: 'test details', 875 | dismiss: false 876 | }; 877 | 878 | // initialize the plugin with custom options 879 | this.player.errors({errors: {4: customErrorNoDimiss}}); 880 | // tick forward enough to ready the player 881 | this.clock.tick(1); 882 | // trigger the error in question 883 | this.player.error(4); 884 | // confirm results 885 | assert.ok(!this.errorDisplay.$('.vjs-errors-ok-button'), 'ok button is not present'); 886 | assert.ok(!this.errorDisplay.$('.vjs-close-button'), 'close button is not present'); 887 | }); 888 | 889 | QUnit.test('custom errors can be added at runtime', function(assert) { 890 | this.player.errors(); 891 | 892 | // tick forward enough to ready the player 893 | this.clock.tick(1); 894 | 895 | const error = { 896 | '-3': { 897 | type: 'TEST', 898 | headline: 'test', 899 | message: 'test test' 900 | } 901 | }; 902 | 903 | this.player.errors.extend(error); 904 | 905 | this.player.error({code: -3}); 906 | 907 | assert.strictEqual( 908 | this.player.errorDisplay.$('.vjs-errors-headline').textContent, 909 | error['-3'].headline, 910 | 'headline should match custom override value' 911 | ); 912 | 913 | assert.strictEqual( 914 | this.player.errorDisplay.$('.vjs-errors-message').textContent, 915 | error['-3'].message, 916 | 'message should match custom override value' 917 | ); 918 | }); 919 | 920 | QUnit.test('custom errors can be defined without a type at init time', function(assert) { 921 | const error = { 922 | TEST: { 923 | headline: 'test', 924 | message: 'test test' 925 | } 926 | }; 927 | 928 | this.player.errors({errors: error}); 929 | 930 | // tick forward enough to ready the player 931 | this.clock.tick(1); 932 | 933 | this.player.error({code: 'TEST'}); 934 | 935 | assert.strictEqual( 936 | this.player.errorDisplay.$('.vjs-errors-headline').textContent, 937 | error.TEST.headline, 938 | 'headline should match custom override value' 939 | ); 940 | 941 | assert.strictEqual( 942 | this.player.errorDisplay.$('.vjs-errors-message').textContent, 943 | error.TEST.message, 944 | 'message should match custom override value' 945 | ); 946 | }); 947 | 948 | QUnit.test('custom errors can be defined without a type at init time', function(assert) { 949 | const error = { 950 | TEST: { 951 | headline: 'test', 952 | message: 'test test' 953 | } 954 | }; 955 | 956 | this.player.errors(); 957 | 958 | // tick forward enough to ready the player 959 | this.clock.tick(1); 960 | 961 | this.player.errors.extend(error); 962 | this.player.error({code: 'TEST'}); 963 | 964 | assert.strictEqual( 965 | this.player.errorDisplay.$('.vjs-errors-headline').textContent, 966 | error.TEST.headline, 967 | 'headline should match custom override value' 968 | ); 969 | 970 | assert.strictEqual( 971 | this.player.errorDisplay.$('.vjs-errors-message').textContent, 972 | error.TEST.message, 973 | 'message should match custom override value' 974 | ); 975 | }); 976 | 977 | QUnit.test('getAll()', function(assert) { 978 | this.player.errors(); 979 | 980 | let errors = this.player.errors.getAll(); 981 | 982 | assert.strictEqual(errors['1'].type, 'MEDIA_ERR_ABORTED'); 983 | assert.strictEqual(errors['2'].type, 'MEDIA_ERR_NETWORK'); 984 | 985 | this.player.errors.extend({ 986 | TEST: { 987 | headline: 'test', 988 | message: 'test test' 989 | } 990 | }); 991 | 992 | errors = this.player.errors.getAll(); 993 | 994 | assert.strictEqual(errors['1'].type, 'MEDIA_ERR_ABORTED'); 995 | assert.strictEqual(errors['2'].type, 'MEDIA_ERR_NETWORK'); 996 | assert.strictEqual(errors.TEST.type, 'TEST'); 997 | }); 998 | --------------------------------------------------------------------------------