From a4a0e55eac92595d8a3bb5b52566eebb57f2d78b Mon Sep 17 00:00:00 2001 From: Denis Mosolov Date: Tue, 24 Aug 2021 17:00:56 +0300 Subject: [PATCH 01/47] Add 3-2-1 countdown --- src/css/components/countdown-overlay.scss | 19 ++++++++ src/css/videojs.record.scss | 1 + src/js/controls/countdown-overlay.js | 53 +++++++++++++++++++++++ src/js/controls/record-toggle.js | 14 +++++- src/js/videojs.record.js | 20 +++++++-- 5 files changed, 102 insertions(+), 5 deletions(-) create mode 100644 src/css/components/countdown-overlay.scss create mode 100644 src/js/controls/countdown-overlay.js diff --git a/src/css/components/countdown-overlay.scss b/src/css/components/countdown-overlay.scss new file mode 100644 index 000000000..41fb48280 --- /dev/null +++ b/src/css/components/countdown-overlay.scss @@ -0,0 +1,19 @@ +/* Countdown Overlay (3-2-1 countdown when start recording) +-------------------------------------------------------------------------------- +*/ + +.vjs-record-countdown { + position: absolute; + bottom: 3em; + left: 0; + right: 0; + top: 0; + pointer-events: none; + + display: flex; + justify-content: center; /* align horizontal */ + align-items: center; /* align vertical */ + + // make the font bigger + font-size: 6em; +} \ No newline at end of file diff --git a/src/css/videojs.record.scss b/src/css/videojs.record.scss index cf04a05b9..71068d9dc 100644 --- a/src/css/videojs.record.scss +++ b/src/css/videojs.record.scss @@ -7,3 +7,4 @@ @import "components/camera-button"; @import "components/record-indicator"; @import "components/picture-in-picture-toggle"; +@import "components/countdown-overlay"; \ No newline at end of file diff --git a/src/js/controls/countdown-overlay.js b/src/js/controls/countdown-overlay.js new file mode 100644 index 000000000..11c9b8e85 --- /dev/null +++ b/src/js/controls/countdown-overlay.js @@ -0,0 +1,53 @@ +/** + * @file countdown-overlay.js + * @since 4.5.0 + */ + +import videojs from 'video.js'; + +const Component = videojs.getComponent('Component'); + +/** + * Overlay for displaying the countdown. + * + * @class + * @augments videojs.Component + */ +class CountdownOverlay extends Component { + /** + * Create the `Countdown`s DOM element. + * + * @return {Element} + * The dom element that gets created. + */ + createEl() { + let props = { + className: 'vjs-record-countdown', + dir: 'ltr' + }; + let attr = { + }; + + return super.createEl('div', props, attr); + } + + /** + * Show the `CountdownOverlay` element if it is hidden by removing the + * 'vjs-hidden' class name from it. + */ + show() { + if (this.layoutExclude && this.layoutExclude === true) { + // ignore + return; + } + super.show(); + } + + setCountdownValue(value) { + this.el().innerHTML = value; + } +} + +Component.registerComponent('CountdownOverlay', CountdownOverlay); + +export default CountdownOverlay; diff --git a/src/js/controls/record-toggle.js b/src/js/controls/record-toggle.js index d5759d481..a234753d6 100644 --- a/src/js/controls/record-toggle.js +++ b/src/js/controls/record-toggle.js @@ -72,7 +72,19 @@ class RecordToggle extends Button { handleClick(event) { let recorder = this.player_.record(); if (!recorder.isRecording()) { - recorder.start(); + this.player_.countdownOverlay.show(); + this.player_.countdownOverlay.setCountdownValue(3); + setTimeout(() => { + this.player_.countdownOverlay.setCountdownValue(2); + setTimeout(() => { + this.player_.countdownOverlay.setCountdownValue(1); + setTimeout(() => { + this.player_.countdownOverlay.setCountdownValue(0); + this.player_.countdownOverlay.hide(); + recorder.start(); + }, 1000); + }, 1000); + }, 1000); } else { recorder.stop(); } diff --git a/src/js/videojs.record.js b/src/js/videojs.record.js index 9537af2df..cf17e8227 100644 --- a/src/js/videojs.record.js +++ b/src/js/videojs.record.js @@ -12,6 +12,7 @@ import RecordCanvas from './controls/record-canvas'; import DeviceButton from './controls/device-button'; import CameraButton from './controls/camera-button'; import RecordToggle from './controls/record-toggle'; +import CountdownOverlay from './controls/countdown-overlay'; import RecordIndicator from './controls/record-indicator'; import PictureInPictureToggle from './controls/picture-in-picture-toggle'; @@ -127,6 +128,11 @@ class Record extends Plugin { player.recordToggle = new RecordToggle(player, options); player.recordToggle.hide(); + // add countdown overlay + player.countdownOverlay = new CountdownOverlay(player, options); + player.addChild(player.countdownOverlay); + player.countdownOverlay.hide(); + // picture-in-picture let oldVideoJS = videojs.VERSION === undefined || compareVersion(videojs.VERSION, '7.6.0') === -1; if (!('pictureInPictureEnabled' in document)) { @@ -148,7 +154,7 @@ class Record extends Plugin { // exclude custom UI elements if (this.player.options_.controlBar) { let customUIElements = ['deviceButton', 'recordIndicator', - 'cameraButton', 'recordToggle']; + 'cameraButton', 'recordToggle', 'countdownOverlay']; if (player.pipToggle) { customUIElements.push('pipToggle'); } @@ -626,6 +632,9 @@ class Record extends Plugin { // store reference to stream for stopping etc. this.stream = stream; + // hide countdown overlay + this.player.countdownOverlay.hide(); + // hide device selection button this.player.deviceButton.hide(); @@ -1289,8 +1298,8 @@ class Record extends Plugin { this.player.controlBar.durationDisplay.contentEl() && this.player.controlBar.durationDisplay.contentEl().lastChild) { this.player.controlBar.durationDisplay.formattedTime_ = - this.player.controlBar.durationDisplay.contentEl().lastChild.textContent = - this._formatTime(duration, duration, this.displayMilliseconds); + this.player.controlBar.durationDisplay.contentEl().lastChild.textContent = + this._formatTime(duration, duration, this.displayMilliseconds); } break; } @@ -1466,6 +1475,9 @@ class Record extends Plugin { this.player.controlBar.playToggle.hide(); } + // hide countdown overlay + this.player.countdownOverlay.hide(); + // show device selection button this.player.deviceButton.show(); @@ -1674,7 +1686,7 @@ class Record extends Plugin { // importing ImageCapture can fail when enabling chrome flag is still required. // if so; ignore and continue if ((detected.browser === 'chrome' && detected.version >= 60) && - (typeof ImageCapture === typeof Function)) { + (typeof ImageCapture === typeof Function)) { try { let imageCapture = new ImageCapture(track); // take picture From c6610d28eec5a365167180574897c081ff4846ac Mon Sep 17 00:00:00 2001 From: Denis Mosolov Date: Tue, 24 Aug 2021 17:47:24 +0300 Subject: [PATCH 02/47] Add an option to remove the "Device" button and ask the for the device permissions immidiately --- examples/audio-video.html | 3 ++- src/js/defaults.js | 4 +++- src/js/videojs.record.js | 43 +++++++++++++++++++++++++++++---------- 3 files changed, 37 insertions(+), 13 deletions(-) diff --git a/examples/audio-video.html b/examples/audio-video.html index aabc65af3..638f36baa 100644 --- a/examples/audio-video.html +++ b/examples/audio-video.html @@ -39,7 +39,8 @@ audio: true, video: true, maxLength: 10, - debug: true + debug: true, + deviceButton: false } } }; diff --git a/src/js/defaults.js b/src/js/defaults.js index 085042a6a..a48a35184 100644 --- a/src/js/defaults.js +++ b/src/js/defaults.js @@ -137,7 +137,9 @@ const pluginDefaultOptions = { hotKeys: false, // Use this object to specify additional settings for the library used by the // plugin (only used in opus-recorder, ffmpeg.js, ffmpeg.wasm and vmsg plugins). - pluginLibraryOptions: {} + pluginLibraryOptions: {}, + // Display "Device" button if true, or request a media device on init if false + deviceButton: true }; export default pluginDefaultOptions; diff --git a/src/js/videojs.record.js b/src/js/videojs.record.js index cf17e8227..4c097f9b5 100644 --- a/src/js/videojs.record.js +++ b/src/js/videojs.record.js @@ -98,12 +98,14 @@ class Record extends Plugin { } // add custom interface elements - DeviceButton.prototype.buildCSSClass = () => { - // use dynamic icon class - return 'vjs-record vjs-device-button vjs-control vjs-icon-' + deviceIcon; - }; - player.deviceButton = new DeviceButton(player, options); - player.addChild(player.deviceButton); + if (this.deviceButton) { + DeviceButton.prototype.buildCSSClass = () => { + // use dynamic icon class + return 'vjs-record vjs-device-button vjs-control vjs-icon-' + deviceIcon; + }; + player.deviceButton = new DeviceButton(player, options); + player.addChild(player.deviceButton); + } // add blinking record indicator player.recordIndicator = new RecordIndicator(player, options); @@ -153,8 +155,12 @@ class Record extends Plugin { // exclude custom UI elements if (this.player.options_.controlBar) { - let customUIElements = ['deviceButton', 'recordIndicator', + let customUIElements = ['recordIndicator', 'cameraButton', 'recordToggle', 'countdownOverlay']; + window.console.log('device button'); + if (this.deviceButton) { + customUIElements.unshift('deviceButton'); + } if (player.pipToggle) { customUIElements.push('pipToggle'); } @@ -167,6 +173,11 @@ class Record extends Plugin { }); } + if (!this.deviceButton) { + // immediately ask for device + this.getDevice(); + } + // wait until player ui is ready this.player.one(Event.READY, this.setupUI.bind(this)); } @@ -232,6 +243,9 @@ class Record extends Plugin { // animation settings this.animationFrameRate = recordOptions.animationFrameRate; this.animationQuality = recordOptions.animationQuality; + + // cuc settings + this.deviceButton = recordOptions.deviceButton; } /** @@ -635,8 +649,10 @@ class Record extends Plugin { // hide countdown overlay this.player.countdownOverlay.hide(); - // hide device selection button - this.player.deviceButton.hide(); + if (this.deviceButton) { + // hide device selection button + this.player.deviceButton.hide(); + } // reset time (e.g. when stopDevice was used) this.setDuration(this.maxLength); @@ -1478,8 +1494,13 @@ class Record extends Plugin { // hide countdown overlay this.player.countdownOverlay.hide(); - // show device selection button - this.player.deviceButton.show(); + if (this.deviceButton) { + // show device selection button + this.player.deviceButton.show(); + } else { + // ask for device + this.getDevice(); + } // hide record button this.player.recordToggle.hide(); From 0ec79932618f17da63e2e9d3522ca6842e00c502 Mon Sep 17 00:00:00 2001 From: Denis Mosolov Date: Tue, 24 Aug 2021 18:41:22 +0300 Subject: [PATCH 03/47] Update REC indicator with the recording progress --- examples/audio-video.html | 2 +- src/js/controls/record-indicator.js | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/examples/audio-video.html b/examples/audio-video.html index 638f36baa..a8bdc096f 100644 --- a/examples/audio-video.html +++ b/examples/audio-video.html @@ -38,7 +38,7 @@ record: { audio: true, video: true, - maxLength: 10, + maxLength: 60, debug: true, deviceButton: false } diff --git a/src/js/controls/record-indicator.js b/src/js/controls/record-indicator.js index 3458928c7..eb0c3b690 100644 --- a/src/js/controls/record-indicator.js +++ b/src/js/controls/record-indicator.js @@ -51,6 +51,7 @@ class RecordIndicator extends Component { */ enable() { this.on(this.player_, Event.START_RECORD, this.show); + this.on(this.player_, Event.PROGRESS_RECORD, this.onProgress); this.on(this.player_, Event.STOP_RECORD, this.hide); } @@ -59,6 +60,7 @@ class RecordIndicator extends Component { */ disable() { this.off(this.player_, Event.START_RECORD, this.show); + this.off(this.player_, Event.PROGRESS_RECORD, this.onProgress); this.off(this.player_, Event.STOP_RECORD, this.hide); } @@ -73,6 +75,27 @@ class RecordIndicator extends Component { } super.show(); } + + /** + * Displays current recording time instead of REC + * @param {Object} label - formatted time. + */ + setProgressValue(label) { + this.el().dataset.label = label; + } + + /** + * Invoked during recording and displays the remaining time. + */ + onProgress() { + let recorder = this.player_.record(); + let now = performance.now(); + let duration = recorder.maxLength; + let currentTime = (now - (recorder.startTime + + recorder.pausedTime)) / 1000; // buddy ignore:line + + this.setProgressValue(recorder._formatTime(Math.min(currentTime, duration), duration, recorder.displayMilliseconds)); + } } Component.registerComponent('RecordIndicator', RecordIndicator); From 58a3134dfb094b69bd8b0793b5e19265f8c60893 Mon Sep 17 00:00:00 2001 From: Denis Mosolov Date: Tue, 24 Aug 2021 18:53:27 +0300 Subject: [PATCH 04/47] Respect linter --- src/js/controls/record-toggle.js | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/js/controls/record-toggle.js b/src/js/controls/record-toggle.js index a234753d6..0fdd18bcd 100644 --- a/src/js/controls/record-toggle.js +++ b/src/js/controls/record-toggle.js @@ -10,6 +10,8 @@ import Event from '../event'; const Button = videojs.getComponent('Button'); const Component = videojs.getComponent('Component'); +const COUNTDOWN_INTERVAL = 1000; + /** * Button to toggle between start and stop recording. * @@ -72,19 +74,22 @@ class RecordToggle extends Button { handleClick(event) { let recorder = this.player_.record(); if (!recorder.isRecording()) { + let countdown = 3; + let down = () => { + if (countdown <= 0) { + this.player_.countdownOverlay.hide(); + recorder.start(); + } else { + countdown--; + this.player_.countdownOverlay.setCountdownValue(countdown); + setTimeout(down, COUNTDOWN_INTERVAL); + } + }; + this.player_.countdownOverlay.show(); - this.player_.countdownOverlay.setCountdownValue(3); - setTimeout(() => { - this.player_.countdownOverlay.setCountdownValue(2); - setTimeout(() => { - this.player_.countdownOverlay.setCountdownValue(1); - setTimeout(() => { - this.player_.countdownOverlay.setCountdownValue(0); - this.player_.countdownOverlay.hide(); - recorder.start(); - }, 1000); - }, 1000); - }, 1000); + this.player_.countdownOverlay.setCountdownValue(countdown); + + down(); } else { recorder.stop(); } From 9ebfa7a5997159f67eb3c1c9c8f5d50cdc663e33 Mon Sep 17 00:00:00 2001 From: Denis Mosolov Date: Tue, 24 Aug 2021 19:11:59 +0300 Subject: [PATCH 05/47] Fix tests --- src/js/controls/record-toggle.js | 36 ++++++++++++++++++-------------- test/defaults.spec.js | 3 ++- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/src/js/controls/record-toggle.js b/src/js/controls/record-toggle.js index 0fdd18bcd..60e52a41e 100644 --- a/src/js/controls/record-toggle.js +++ b/src/js/controls/record-toggle.js @@ -74,22 +74,26 @@ class RecordToggle extends Button { handleClick(event) { let recorder = this.player_.record(); if (!recorder.isRecording()) { - let countdown = 3; - let down = () => { - if (countdown <= 0) { - this.player_.countdownOverlay.hide(); - recorder.start(); - } else { - countdown--; - this.player_.countdownOverlay.setCountdownValue(countdown); - setTimeout(down, COUNTDOWN_INTERVAL); - } - }; - - this.player_.countdownOverlay.show(); - this.player_.countdownOverlay.setCountdownValue(countdown); - - down(); + if (recorder.deviceButton) { + recorder.start(); + } else { + let countdown = 3; + let down = () => { + if (countdown <= 0) { + this.player_.countdownOverlay.hide(); + recorder.start(); + } else { + countdown--; + this.player_.countdownOverlay.setCountdownValue(countdown); + setTimeout(down, COUNTDOWN_INTERVAL); + } + }; + + this.player_.countdownOverlay.show(); + this.player_.countdownOverlay.setCountdownValue(countdown); + + down(); + } } else { recorder.stop(); } diff --git a/test/defaults.spec.js b/test/defaults.spec.js index e1c8edc44..116bdbba3 100644 --- a/test/defaults.spec.js +++ b/test/defaults.spec.js @@ -57,7 +57,8 @@ describe('pluginDefaultOptions', () => { convertOptions: [], convertAuto: true, hotKeys: false, - pluginLibraryOptions: {} + pluginLibraryOptions: {}, + deviceButton: true }); }); }); From 37ab95d3867b2df649c64177a8bd144ec91aadde Mon Sep 17 00:00:00 2001 From: Denis Mosolov Date: Wed, 25 Aug 2021 03:45:39 +0300 Subject: [PATCH 06/47] Drop everything except the countdown --- src/css/components/countdown-overlay.scss | 9 +-- src/js/controls/countdown-overlay.js | 12 ++-- src/js/controls/record-indicator.js | 23 ------- src/js/controls/record-toggle.js | 51 +++++++++------ src/js/defaults.js | 10 ++- src/js/videojs.record.js | 76 ++++++++++------------- test/defaults.spec.js | 6 +- 7 files changed, 85 insertions(+), 102 deletions(-) diff --git a/src/css/components/countdown-overlay.scss b/src/css/components/countdown-overlay.scss index 41fb48280..c5cdd5186 100644 --- a/src/css/components/countdown-overlay.scss +++ b/src/css/components/countdown-overlay.scss @@ -11,9 +11,10 @@ pointer-events: none; display: flex; - justify-content: center; /* align horizontal */ - align-items: center; /* align vertical */ + justify-content: center; + align-items: center; - // make the font bigger - font-size: 6em; + span { + font-size: 6em; + } } \ No newline at end of file diff --git a/src/js/controls/countdown-overlay.js b/src/js/controls/countdown-overlay.js index 11c9b8e85..627731865 100644 --- a/src/js/controls/countdown-overlay.js +++ b/src/js/controls/countdown-overlay.js @@ -21,14 +21,14 @@ class CountdownOverlay extends Component { * The dom element that gets created. */ createEl() { - let props = { + const spanElement = videojs.dom.createEl('span'); + const el = super.createEl('div', { className: 'vjs-record-countdown', dir: 'ltr' - }; - let attr = { - }; + }); + el.appendChild(spanElement); - return super.createEl('div', props, attr); + return el; } /** @@ -44,7 +44,7 @@ class CountdownOverlay extends Component { } setCountdownValue(value) { - this.el().innerHTML = value; + this.el().firstChild.innerText = value; } } diff --git a/src/js/controls/record-indicator.js b/src/js/controls/record-indicator.js index eb0c3b690..3458928c7 100644 --- a/src/js/controls/record-indicator.js +++ b/src/js/controls/record-indicator.js @@ -51,7 +51,6 @@ class RecordIndicator extends Component { */ enable() { this.on(this.player_, Event.START_RECORD, this.show); - this.on(this.player_, Event.PROGRESS_RECORD, this.onProgress); this.on(this.player_, Event.STOP_RECORD, this.hide); } @@ -60,7 +59,6 @@ class RecordIndicator extends Component { */ disable() { this.off(this.player_, Event.START_RECORD, this.show); - this.off(this.player_, Event.PROGRESS_RECORD, this.onProgress); this.off(this.player_, Event.STOP_RECORD, this.hide); } @@ -75,27 +73,6 @@ class RecordIndicator extends Component { } super.show(); } - - /** - * Displays current recording time instead of REC - * @param {Object} label - formatted time. - */ - setProgressValue(label) { - this.el().dataset.label = label; - } - - /** - * Invoked during recording and displays the remaining time. - */ - onProgress() { - let recorder = this.player_.record(); - let now = performance.now(); - let duration = recorder.maxLength; - let currentTime = (now - (recorder.startTime + - recorder.pausedTime)) / 1000; // buddy ignore:line - - this.setProgressValue(recorder._formatTime(Math.min(currentTime, duration), duration, recorder.displayMilliseconds)); - } } Component.registerComponent('RecordIndicator', RecordIndicator); diff --git a/src/js/controls/record-toggle.js b/src/js/controls/record-toggle.js index 60e52a41e..9cca5bb13 100644 --- a/src/js/controls/record-toggle.js +++ b/src/js/controls/record-toggle.js @@ -10,8 +10,6 @@ import Event from '../event'; const Button = videojs.getComponent('Button'); const Component = videojs.getComponent('Component'); -const COUNTDOWN_INTERVAL = 1000; - /** * Button to toggle between start and stop recording. * @@ -74,31 +72,44 @@ class RecordToggle extends Button { handleClick(event) { let recorder = this.player_.record(); if (!recorder.isRecording()) { - if (recorder.deviceButton) { - recorder.start(); + if (recorder.countdownOverlay) { + this.startWithCountdown(recorder); } else { - let countdown = 3; - let down = () => { - if (countdown <= 0) { - this.player_.countdownOverlay.hide(); - recorder.start(); - } else { - countdown--; - this.player_.countdownOverlay.setCountdownValue(countdown); - setTimeout(down, COUNTDOWN_INTERVAL); - } - }; - - this.player_.countdownOverlay.show(); - this.player_.countdownOverlay.setCountdownValue(countdown); - - down(); + recorder.start(); } } else { recorder.stop(); } } + /** + * Display the countdown and start the recording when 0 is reached + * + * @param {Record} recorder + * Instance of the Record plugin. + */ + startWithCountdown(recorder) { + let currentValue = recorder.countdownSteps; + let interval = recorder.countdownTimeBetweenSteps; + let startOrDown = () => { + if (currentValue <= 0) { + this.player_.countdownOverlay.hide(); + recorder.start(); + } else { + this.player_.countdownOverlay.setCountdownValue(currentValue); + currentValue--; + setTimeout(startOrDown, interval); + } + }; + + this.player_.countdownOverlay.show(); + this.player_.countdownOverlay.setCountdownValue(currentValue); + + // @todo hide the record button while countdown is displaying + + startOrDown(); + } + /** * Add the vjs-icon-record-stop class to the element so it can change appearance. * diff --git a/src/js/defaults.js b/src/js/defaults.js index a48a35184..a43bc3be1 100644 --- a/src/js/defaults.js +++ b/src/js/defaults.js @@ -135,11 +135,15 @@ const pluginDefaultOptions = { convertAuto: true, // Enable keyboard hotkeys. hotKeys: false, + // Enable the 3-2-1 countdown before the recording + countdownOverlay: false, + // Time interval in milliseconds after which it decreases the countdown + countdownTimeBetweenSteps: 1000, + // Countdown start value + countdownSteps: 3, // Use this object to specify additional settings for the library used by the // plugin (only used in opus-recorder, ffmpeg.js, ffmpeg.wasm and vmsg plugins). - pluginLibraryOptions: {}, - // Display "Device" button if true, or request a media device on init if false - deviceButton: true + pluginLibraryOptions: {} }; export default pluginDefaultOptions; diff --git a/src/js/videojs.record.js b/src/js/videojs.record.js index 4c097f9b5..aae7e8f75 100644 --- a/src/js/videojs.record.js +++ b/src/js/videojs.record.js @@ -98,14 +98,12 @@ class Record extends Plugin { } // add custom interface elements - if (this.deviceButton) { - DeviceButton.prototype.buildCSSClass = () => { - // use dynamic icon class - return 'vjs-record vjs-device-button vjs-control vjs-icon-' + deviceIcon; - }; - player.deviceButton = new DeviceButton(player, options); - player.addChild(player.deviceButton); - } + DeviceButton.prototype.buildCSSClass = () => { + // use dynamic icon class + return 'vjs-record vjs-device-button vjs-control vjs-icon-' + deviceIcon; + }; + player.deviceButton = new DeviceButton(player, options); + player.addChild(player.deviceButton); // add blinking record indicator player.recordIndicator = new RecordIndicator(player, options); @@ -130,10 +128,12 @@ class Record extends Plugin { player.recordToggle = new RecordToggle(player, options); player.recordToggle.hide(); - // add countdown overlay - player.countdownOverlay = new CountdownOverlay(player, options); - player.addChild(player.countdownOverlay); - player.countdownOverlay.hide(); + if (this.countdownOverlay) { + // add countdown overlay + player.countdownOverlay = new CountdownOverlay(player, options); + player.addChild(player.countdownOverlay); + player.countdownOverlay.hide(); + } // picture-in-picture let oldVideoJS = videojs.VERSION === undefined || compareVersion(videojs.VERSION, '7.6.0') === -1; @@ -155,15 +155,14 @@ class Record extends Plugin { // exclude custom UI elements if (this.player.options_.controlBar) { - let customUIElements = ['recordIndicator', - 'cameraButton', 'recordToggle', 'countdownOverlay']; - window.console.log('device button'); - if (this.deviceButton) { - customUIElements.unshift('deviceButton'); - } + let customUIElements = ['deviceButton', 'recordIndicator', + 'cameraButton', 'recordToggle']; if (player.pipToggle) { customUIElements.push('pipToggle'); } + if (player.countdownOverlay) { + customUIElements.push('countdownOverlay'); + } customUIElements.forEach((element) => { if (this.player.options_.controlBar[element] !== undefined) { @@ -173,11 +172,6 @@ class Record extends Plugin { }); } - if (!this.deviceButton) { - // immediately ask for device - this.getDevice(); - } - // wait until player ui is ready this.player.one(Event.READY, this.setupUI.bind(this)); } @@ -244,8 +238,10 @@ class Record extends Plugin { this.animationFrameRate = recordOptions.animationFrameRate; this.animationQuality = recordOptions.animationQuality; - // cuc settings - this.deviceButton = recordOptions.deviceButton; + // countdown settings + this.countdownOverlay = recordOptions.countdownOverlay; + this.countdownTimeBetweenSteps = recordOptions.countdownTimeBetweenSteps; + this.countdownSteps = recordOptions.countdownSteps; } /** @@ -646,13 +642,8 @@ class Record extends Plugin { // store reference to stream for stopping etc. this.stream = stream; - // hide countdown overlay - this.player.countdownOverlay.hide(); - - if (this.deviceButton) { - // hide device selection button - this.player.deviceButton.hide(); - } + // hide device selection button + this.player.deviceButton.hide(); // reset time (e.g. when stopDevice was used) this.setDuration(this.maxLength); @@ -1314,8 +1305,8 @@ class Record extends Plugin { this.player.controlBar.durationDisplay.contentEl() && this.player.controlBar.durationDisplay.contentEl().lastChild) { this.player.controlBar.durationDisplay.formattedTime_ = - this.player.controlBar.durationDisplay.contentEl().lastChild.textContent = - this._formatTime(duration, duration, this.displayMilliseconds); + this.player.controlBar.durationDisplay.contentEl().lastChild.textContent = + this._formatTime(duration, duration, this.displayMilliseconds); } break; } @@ -1491,17 +1482,14 @@ class Record extends Plugin { this.player.controlBar.playToggle.hide(); } - // hide countdown overlay - this.player.countdownOverlay.hide(); - - if (this.deviceButton) { - // show device selection button - this.player.deviceButton.show(); - } else { - // ask for device - this.getDevice(); + if (this.player.countdownOverlay) { + // hide countdown overlay + this.player.countdownOverlay.hide(); } + // show device selection button + this.player.deviceButton.show(); + // hide record button this.player.recordToggle.hide(); @@ -1707,7 +1695,7 @@ class Record extends Plugin { // importing ImageCapture can fail when enabling chrome flag is still required. // if so; ignore and continue if ((detected.browser === 'chrome' && detected.version >= 60) && - (typeof ImageCapture === typeof Function)) { + (typeof ImageCapture === typeof Function)) { try { let imageCapture = new ImageCapture(track); // take picture diff --git a/test/defaults.spec.js b/test/defaults.spec.js index 116bdbba3..61df99ea1 100644 --- a/test/defaults.spec.js +++ b/test/defaults.spec.js @@ -57,8 +57,10 @@ describe('pluginDefaultOptions', () => { convertOptions: [], convertAuto: true, hotKeys: false, - pluginLibraryOptions: {}, - deviceButton: true + countdownOverlay: false, + countdownSteps: 3, + countdownTimeBetweenSteps: 1000, + pluginLibraryOptions: {} }); }); }); From 2180baf22ca806a2a15ca21b1fe6963b12798db1 Mon Sep 17 00:00:00 2001 From: Denis Mosolov Date: Wed, 25 Aug 2021 03:48:15 +0300 Subject: [PATCH 07/47] Update examples --- examples/3-2-1-countdown.html | 84 +++++++++++++++++++++++++++++++++++ examples/audio-video.html | 6 +-- 2 files changed, 87 insertions(+), 3 deletions(-) create mode 100644 examples/3-2-1-countdown.html diff --git a/examples/3-2-1-countdown.html b/examples/3-2-1-countdown.html new file mode 100644 index 000000000..004e73f9e --- /dev/null +++ b/examples/3-2-1-countdown.html @@ -0,0 +1,84 @@ + + + + + Countdown Overlay Example - Record Plugin for Video.js + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/audio-video.html b/examples/audio-video.html index a8bdc096f..825dfd988 100644 --- a/examples/audio-video.html +++ b/examples/audio-video.html @@ -38,9 +38,9 @@ record: { audio: true, video: true, - maxLength: 60, - debug: true, - deviceButton: false + maxLength: 10, + countdownOverlay: true, + debug: true } } }; From 144b96e0c3d34494a8ab702f8dc900ef16da10da Mon Sep 17 00:00:00 2001 From: Denis Mosolov Date: Wed, 25 Aug 2021 15:29:47 +0300 Subject: [PATCH 08/47] Add tests --- test/controls/countdown-overlay.spec.js | 70 +++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 test/controls/countdown-overlay.spec.js diff --git a/test/controls/countdown-overlay.spec.js b/test/controls/countdown-overlay.spec.js new file mode 100644 index 000000000..5296e3378 --- /dev/null +++ b/test/controls/countdown-overlay.spec.js @@ -0,0 +1,70 @@ +/** + * @since 4.5.0 + */ + +import TestHelpers from '../test-helpers'; + +import CountdownOverlay from '../../src/js/controls/countdown-overlay'; +import RecordToggle from '../../src/js/controls/record-toggle'; +import Event from '../../src/js/event'; + + +/** @test {countdown-overlay} */ +describe('controls.CountdownOverlay', () => { + let player; + + beforeEach(() => { + // create new player + player = TestHelpers.makeAudioVideoPlayer({ + plugins: { + record: { + countdownOverlay: true, + countdownSteps: 2, + countdownTimeBetweenSteps: 1000 + } + } + }); + }); + + afterEach(() => { + player.dispose(); + }); + + it('creates the correct DOM element', () => { + let countdown = new CountdownOverlay(player); + + expect(countdown.el().nodeName).toEqual('DIV'); + expect(countdown.hasClass('vjs-record-countdown')).toBeTrue(); + expect(countdown.el().innerHTML).toEqual(''); + }); + + it('start record after the countdown', (done) => { + let toggle = new RecordToggle(player); + + player.one(Event.DEVICE_READY, () => { + // start + toggle.trigger('click'); + expect(player.record().isRecording()).toBeFalse(); + + setTimeout(() => { + // started after 2 seconds + expect(player.record().isRecording()).toBeTrue(); + }, 3000); + + setTimeout(() => { + // stop recording + player.record().stop(); + }, 4000); + }); + + player.one(Event.FINISH_RECORD, () => { + // wait till it's loaded before destroying + // (XXX: create new event for this) + setTimeout(done, 1000); + }); + + player.one(Event.READY, () => { + player.record().getDevice(); + }); + }); +}); \ No newline at end of file From ca39c58c8f9d69ea93eb08dde62eafc6415bf186 Mon Sep 17 00:00:00 2001 From: Denis Mosolov Date: Thu, 26 Aug 2021 12:20:19 +0300 Subject: [PATCH 09/47] Add docs --- docs/demo/3-2-1-countdown.html | 92 ++++++++++++++++++++++++++++++++++ docs/examples.md | 1 + docs/options.md | 3 ++ 3 files changed, 96 insertions(+) create mode 100644 docs/demo/3-2-1-countdown.html diff --git a/docs/demo/3-2-1-countdown.html b/docs/demo/3-2-1-countdown.html new file mode 100644 index 000000000..62631efe6 --- /dev/null +++ b/docs/demo/3-2-1-countdown.html @@ -0,0 +1,92 @@ + + + + + Audio/Video Example - Record Plugin for Video.js + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/examples.md b/docs/examples.md index cf76da2bd..644a28954 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -13,6 +13,7 @@ View the examples online: | [Screen-only](https://collab-project.github.io/videojs-record/demo/screen-only.html) | Basic screen-only example | [example source](https://github.com/collab-project/videojs-record/blob/master/examples/screen-only.html) | | [Audio/screen](https://collab-project.github.io/videojs-record/demo/audio-screen.html) | Basic audio/screen example | [example source](https://github.com/collab-project/videojs-record/blob/master/examples/audio-screen.html) | | [Animated GIF](https://collab-project.github.io/videojs-record/demo/animated-gif.html) | Basic animated GIF example | [example source](https://github.com/collab-project/videojs-record/blob/master/examples/animated-gif.html) | +| [3-2-1 Countdown](https://collab-project.github.io/videojs-record/demo/3-2-1-countdown.html) | Basic audio/video example with countdown | [example source](https://github.com/collab-project/videojs-record/blob/master/examples/3-2-1-countdown.html) | | [Timeslice](https://collab-project.github.io/videojs-record/demo/timeslice.html) | Get data during recording with specific time-intervals | [example source](https://github.com/collab-project/videojs-record/blob/master/examples/timeslice.html) | | [Hotkeys](https://collab-project.github.io/videojs-record/demo/hot-keys.html) | Control this plugin using a keyboard | [example source](https://github.com/collab-project/videojs-record/blob/master/examples/hot-keys.html) | | [Multi](https://collab-project.github.io/videojs-record/demo/multi.html) | Using multiple video.js players on a single page | [example source](https://github.com/collab-project/videojs-record/blob/master/examples/multi.html) | diff --git a/docs/options.md b/docs/options.md index f7e372641..adb542344 100644 --- a/docs/options.md +++ b/docs/options.md @@ -54,4 +54,7 @@ Additional options for this plugin are: | `convertOptions` | array | `[]` | List of string options to pass to the convert engine. | | `convertAuto` | boolean | `true` | By default the converter automatically starts once recording completed. Use `false` to disable this behavior, allowing you to start the converter manually instead. | | `hotKeys` | boolean or function | `false` | Enable [keyboard hotkeys](hotkeys.md). Disabled by default. | +| `countdownOverlay` | boolean | `false` | Adds the countdown overlay after clicking on the record button. The recording starts once the countdown reaches 0. | +| `countdownSteps` | float | `3` | Amount of countdown steps (Initial value). | +| `countdownTimeBetweenSteps` | float | `1000` | Time interval in milliseconds after which it decreases the countdown value by 1. | | `pluginLibraryOptions` | object | `{}` | Use this object to specify additional settings for the library used by the plugin. Currently only used for the ffmpeg.wasm, ffmpeg.js, opus-recorder and vmsg plugins. | From e921226342f443ffe71def3673f67750bf540aae Mon Sep 17 00:00:00 2001 From: Denis Mosolov Date: Thu, 26 Aug 2021 13:59:08 +0300 Subject: [PATCH 10/47] Disable record button while the countdown is running --- src/js/controls/record-toggle.js | 5 ++-- test/controls/record-toggle.spec.js | 41 +++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/src/js/controls/record-toggle.js b/src/js/controls/record-toggle.js index 9cca5bb13..108d65d3b 100644 --- a/src/js/controls/record-toggle.js +++ b/src/js/controls/record-toggle.js @@ -73,6 +73,7 @@ class RecordToggle extends Button { let recorder = this.player_.record(); if (!recorder.isRecording()) { if (recorder.countdownOverlay) { + // @todo move to recorder this.startWithCountdown(recorder); } else { recorder.start(); @@ -94,6 +95,7 @@ class RecordToggle extends Button { let startOrDown = () => { if (currentValue <= 0) { this.player_.countdownOverlay.hide(); + this.enable(); recorder.start(); } else { this.player_.countdownOverlay.setCountdownValue(currentValue); @@ -102,11 +104,10 @@ class RecordToggle extends Button { } }; + this.disable(); this.player_.countdownOverlay.show(); this.player_.countdownOverlay.setCountdownValue(currentValue); - // @todo hide the record button while countdown is displaying - startOrDown(); } diff --git a/test/controls/record-toggle.spec.js b/test/controls/record-toggle.spec.js index b44fcc42c..617a2efb9 100644 --- a/test/controls/record-toggle.spec.js +++ b/test/controls/record-toggle.spec.js @@ -84,4 +84,45 @@ describe('controls.RecordToggle', () => { player.record().getDevice(); }); }); + + it('record button is locked while the countdown is running', (done) => { + // create an instance of a player with the countdown + player.dispose(); + player = TestHelpers.makeAudioVideoPlayer({ + plugins: { + record: { + countdownOverlay: true, + countdownSteps: 2, + countdownTimeBetweenSteps: 1000 + } + } + }); + + let toggle = new RecordToggle(player); + + player.one(Event.DEVICE_READY, () => { + // start + toggle.trigger('click'); + + setTimeout(() => { + // countdown is running, record button is locked + expect(toggle.el().hasAttribute('disabled')).toBeTrue(); + }, 1000); + + setTimeout(() => { + // stop recording + player.record().stop(); + }, 3000); + }); + + player.one(Event.FINISH_RECORD, () => { + // wait till it's loaded before destroying + // (XXX: create new event for this) + setTimeout(done, 1000); + }); + + player.one(Event.READY, () => { + player.record().getDevice(); + }); + }); }); \ No newline at end of file From a6d81147c8ecac3da615296cdb5f40c7857a6807 Mon Sep 17 00:00:00 2001 From: Denis Mosolov Date: Thu, 26 Aug 2021 14:01:53 +0300 Subject: [PATCH 11/47] Rename the example file --- docs/demo/{3-2-1-countdown.html => countdown.html} | 0 docs/examples.md | 2 +- examples/{3-2-1-countdown.html => countdown.html} | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename docs/demo/{3-2-1-countdown.html => countdown.html} (100%) rename examples/{3-2-1-countdown.html => countdown.html} (100%) diff --git a/docs/demo/3-2-1-countdown.html b/docs/demo/countdown.html similarity index 100% rename from docs/demo/3-2-1-countdown.html rename to docs/demo/countdown.html diff --git a/docs/examples.md b/docs/examples.md index 644a28954..72726c542 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -13,7 +13,7 @@ View the examples online: | [Screen-only](https://collab-project.github.io/videojs-record/demo/screen-only.html) | Basic screen-only example | [example source](https://github.com/collab-project/videojs-record/blob/master/examples/screen-only.html) | | [Audio/screen](https://collab-project.github.io/videojs-record/demo/audio-screen.html) | Basic audio/screen example | [example source](https://github.com/collab-project/videojs-record/blob/master/examples/audio-screen.html) | | [Animated GIF](https://collab-project.github.io/videojs-record/demo/animated-gif.html) | Basic animated GIF example | [example source](https://github.com/collab-project/videojs-record/blob/master/examples/animated-gif.html) | -| [3-2-1 Countdown](https://collab-project.github.io/videojs-record/demo/3-2-1-countdown.html) | Basic audio/video example with countdown | [example source](https://github.com/collab-project/videojs-record/blob/master/examples/3-2-1-countdown.html) | +| [Countdown](https://collab-project.github.io/videojs-record/demo/countdown.html) | Basic audio/video example with countdown | [example source](https://github.com/collab-project/videojs-record/blob/master/examples/countdown.html) | | [Timeslice](https://collab-project.github.io/videojs-record/demo/timeslice.html) | Get data during recording with specific time-intervals | [example source](https://github.com/collab-project/videojs-record/blob/master/examples/timeslice.html) | | [Hotkeys](https://collab-project.github.io/videojs-record/demo/hot-keys.html) | Control this plugin using a keyboard | [example source](https://github.com/collab-project/videojs-record/blob/master/examples/hot-keys.html) | | [Multi](https://collab-project.github.io/videojs-record/demo/multi.html) | Using multiple video.js players on a single page | [example source](https://github.com/collab-project/videojs-record/blob/master/examples/multi.html) | diff --git a/examples/3-2-1-countdown.html b/examples/countdown.html similarity index 100% rename from examples/3-2-1-countdown.html rename to examples/countdown.html From f134bd167692bc4b0ca62ea536208124812258f3 Mon Sep 17 00:00:00 2001 From: Denis Mosolov Date: Sun, 29 Aug 2021 14:59:45 +0300 Subject: [PATCH 12/47] Replace countdownOverlay, countdownSteps and countdownTimeBetweenSteps options with countdown. --- docs/demo/countdown.html | 11 ++++++++--- docs/options.md | 4 +--- examples/countdown.html | 11 ++++++++--- src/js/controls/record-toggle.js | 18 +++++++++--------- src/js/defaults.js | 8 ++------ src/js/videojs.record.js | 6 ++---- test/controls/countdown-overlay.spec.js | 7 ++++--- test/controls/record-toggle.spec.js | 7 ++++--- test/defaults.spec.js | 4 +--- 9 files changed, 39 insertions(+), 37 deletions(-) diff --git a/docs/demo/countdown.html b/docs/demo/countdown.html index 62631efe6..86ff3fcfd 100644 --- a/docs/demo/countdown.html +++ b/docs/demo/countdown.html @@ -47,9 +47,14 @@ video: true, maxLength: 10, countdownOverlay: true, - countdownSteps: 5, - countdownTimeBetweenSteps: 500, - displayMilliseconds: false, + countdown: [ + {value: 'Five!', time: 1000}, + {value: 'Four!', time: 1000}, + {value: 'Three', time: 1000}, + {value: '2', time: 1000}, + {value: '1', time: 500}, + {value: 'Go!', time: 500}, + ], debug: true } } diff --git a/docs/options.md b/docs/options.md index adb542344..eeada10c0 100644 --- a/docs/options.md +++ b/docs/options.md @@ -54,7 +54,5 @@ Additional options for this plugin are: | `convertOptions` | array | `[]` | List of string options to pass to the convert engine. | | `convertAuto` | boolean | `true` | By default the converter automatically starts once recording completed. Use `false` to disable this behavior, allowing you to start the converter manually instead. | | `hotKeys` | boolean or function | `false` | Enable [keyboard hotkeys](hotkeys.md). Disabled by default. | -| `countdownOverlay` | boolean | `false` | Adds the countdown overlay after clicking on the record button. The recording starts once the countdown reaches 0. | -| `countdownSteps` | float | `3` | Amount of countdown steps (Initial value). | -| `countdownTimeBetweenSteps` | float | `1000` | Time interval in milliseconds after which it decreases the countdown value by 1. | +| `countdown` | array | `[]` | List of steps to display as a countdown after clicking on the record button (before the recording starts). Each step has a value (string) to display and time interval (in milliseconds). For example `[{time:500,value:'3'},{time:500,value:'2'},{time:500,value:'1'}]` will display "3" for 0.5 sec, then "2" for 0.5 sec, then "1" for 0.5 sec and after that start the recording. | | `pluginLibraryOptions` | object | `{}` | Use this object to specify additional settings for the library used by the plugin. Currently only used for the ffmpeg.wasm, ffmpeg.js, opus-recorder and vmsg plugins. | diff --git a/examples/countdown.html b/examples/countdown.html index 004e73f9e..c6af9c3ff 100644 --- a/examples/countdown.html +++ b/examples/countdown.html @@ -39,9 +39,14 @@ audio: true, video: true, maxLength: 10, - countdownOverlay: true, - countdownSteps: 5, - countdownTimeBetweenSteps: 1000, + countdown: [ + {value: 'Five!', time: 1000}, + {value: 'Four!', time: 1000}, + {value: 'Three', time: 1000}, + {value: '2', time: 1000}, + {value: '1', time: 500}, + {value: 'Go!', time: 500}, + ], debug: true } } diff --git a/src/js/controls/record-toggle.js b/src/js/controls/record-toggle.js index 108d65d3b..95029112c 100644 --- a/src/js/controls/record-toggle.js +++ b/src/js/controls/record-toggle.js @@ -72,7 +72,7 @@ class RecordToggle extends Button { handleClick(event) { let recorder = this.player_.record(); if (!recorder.isRecording()) { - if (recorder.countdownOverlay) { + if (recorder.countdown.length) { // @todo move to recorder this.startWithCountdown(recorder); } else { @@ -84,29 +84,29 @@ class RecordToggle extends Button { } /** - * Display the countdown and start the recording when 0 is reached + * Display the countdown and start the recording when the last countdown step is reached * * @param {Record} recorder * Instance of the Record plugin. */ startWithCountdown(recorder) { - let currentValue = recorder.countdownSteps; - let interval = recorder.countdownTimeBetweenSteps; + let countdownSteps = [...recorder.countdown]; let startOrDown = () => { - if (currentValue <= 0) { + if (countdownSteps.length === 0) { this.player_.countdownOverlay.hide(); this.enable(); recorder.start(); } else { - this.player_.countdownOverlay.setCountdownValue(currentValue); - currentValue--; - setTimeout(startOrDown, interval); + let value, time; + // @todo validation? + ({value, time} = countdownSteps.shift()); + this.player_.countdownOverlay.setCountdownValue(value); + setTimeout(startOrDown, time); } }; this.disable(); this.player_.countdownOverlay.show(); - this.player_.countdownOverlay.setCountdownValue(currentValue); startOrDown(); } diff --git a/src/js/defaults.js b/src/js/defaults.js index a43bc3be1..e44c7ccaf 100644 --- a/src/js/defaults.js +++ b/src/js/defaults.js @@ -135,12 +135,8 @@ const pluginDefaultOptions = { convertAuto: true, // Enable keyboard hotkeys. hotKeys: false, - // Enable the 3-2-1 countdown before the recording - countdownOverlay: false, - // Time interval in milliseconds after which it decreases the countdown - countdownTimeBetweenSteps: 1000, - // Countdown start value - countdownSteps: 3, + // Countdown steps before the recording. + countdown: [], // Use this object to specify additional settings for the library used by the // plugin (only used in opus-recorder, ffmpeg.js, ffmpeg.wasm and vmsg plugins). pluginLibraryOptions: {} diff --git a/src/js/videojs.record.js b/src/js/videojs.record.js index aae7e8f75..9345d611d 100644 --- a/src/js/videojs.record.js +++ b/src/js/videojs.record.js @@ -128,7 +128,7 @@ class Record extends Plugin { player.recordToggle = new RecordToggle(player, options); player.recordToggle.hide(); - if (this.countdownOverlay) { + if (this.countdown.length) { // add countdown overlay player.countdownOverlay = new CountdownOverlay(player, options); player.addChild(player.countdownOverlay); @@ -239,9 +239,7 @@ class Record extends Plugin { this.animationQuality = recordOptions.animationQuality; // countdown settings - this.countdownOverlay = recordOptions.countdownOverlay; - this.countdownTimeBetweenSteps = recordOptions.countdownTimeBetweenSteps; - this.countdownSteps = recordOptions.countdownSteps; + this.countdown = recordOptions.countdown; } /** diff --git a/test/controls/countdown-overlay.spec.js b/test/controls/countdown-overlay.spec.js index 5296e3378..c6d027aa7 100644 --- a/test/controls/countdown-overlay.spec.js +++ b/test/controls/countdown-overlay.spec.js @@ -18,9 +18,10 @@ describe('controls.CountdownOverlay', () => { player = TestHelpers.makeAudioVideoPlayer({ plugins: { record: { - countdownOverlay: true, - countdownSteps: 2, - countdownTimeBetweenSteps: 1000 + countdown: [ + {value: '2', time: 1000}, + {value: '1', time: 1000}, + ], } } }); diff --git a/test/controls/record-toggle.spec.js b/test/controls/record-toggle.spec.js index 617a2efb9..2084e016f 100644 --- a/test/controls/record-toggle.spec.js +++ b/test/controls/record-toggle.spec.js @@ -91,9 +91,10 @@ describe('controls.RecordToggle', () => { player = TestHelpers.makeAudioVideoPlayer({ plugins: { record: { - countdownOverlay: true, - countdownSteps: 2, - countdownTimeBetweenSteps: 1000 + countdown: [ + {value: '2', time: 1000}, + {value: '1', time: 1000}, + ] } } }); diff --git a/test/defaults.spec.js b/test/defaults.spec.js index 61df99ea1..357d1c181 100644 --- a/test/defaults.spec.js +++ b/test/defaults.spec.js @@ -57,9 +57,7 @@ describe('pluginDefaultOptions', () => { convertOptions: [], convertAuto: true, hotKeys: false, - countdownOverlay: false, - countdownSteps: 3, - countdownTimeBetweenSteps: 1000, + countdown: [], pluginLibraryOptions: {} }); }); From e81d8c7f066b76d24b9490a618304e05fafcc61e Mon Sep 17 00:00:00 2001 From: Denis Mosolov Date: Thu, 2 Sep 2021 02:02:07 +0300 Subject: [PATCH 13/47] Add countdown to changelog --- CHANGES.md | 1 + src/js/controls/countdown-overlay.js | 2 +- test/controls/countdown-overlay.spec.js | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 8ce6eb311..933af982d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,6 +3,7 @@ ## 4.6.0 - unreleased - Bump required version for videojs-wavesurfer (3.8.0 or newer) +- Add `countdown` option for displaying 3-2-1 countdown before the recording. ## 4.5.0 - 2021/06/14 diff --git a/src/js/controls/countdown-overlay.js b/src/js/controls/countdown-overlay.js index 627731865..daea0cc13 100644 --- a/src/js/controls/countdown-overlay.js +++ b/src/js/controls/countdown-overlay.js @@ -1,6 +1,6 @@ /** * @file countdown-overlay.js - * @since 4.5.0 + * @since 4.6.0 */ import videojs from 'video.js'; diff --git a/test/controls/countdown-overlay.spec.js b/test/controls/countdown-overlay.spec.js index c6d027aa7..762535f23 100644 --- a/test/controls/countdown-overlay.spec.js +++ b/test/controls/countdown-overlay.spec.js @@ -1,5 +1,5 @@ /** - * @since 4.5.0 + * @since 4.6.0 */ import TestHelpers from '../test-helpers'; From 71c0dda0a2aa48165575d3f355440ba0a7880808 Mon Sep 17 00:00:00 2001 From: Denis Mosolov Date: Thu, 2 Sep 2021 15:25:26 +0300 Subject: [PATCH 14/47] Fix titles, spacing and comments --- docs/demo/countdown.html | 3 +-- docs/options.md | 2 +- examples/audio-video.html | 1 - src/css/components/countdown-overlay.scss | 2 +- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/docs/demo/countdown.html b/docs/demo/countdown.html index 86ff3fcfd..1a91b6ef3 100644 --- a/docs/demo/countdown.html +++ b/docs/demo/countdown.html @@ -2,7 +2,7 @@ - Audio/Video Example - Record Plugin for Video.js + Countdown Overlay Example - Record Plugin for Video.js @@ -46,7 +46,6 @@ audio: true, video: true, maxLength: 10, - countdownOverlay: true, countdown: [ {value: 'Five!', time: 1000}, {value: 'Four!', time: 1000}, diff --git a/docs/options.md b/docs/options.md index eeada10c0..ee569a8b2 100644 --- a/docs/options.md +++ b/docs/options.md @@ -54,5 +54,5 @@ Additional options for this plugin are: | `convertOptions` | array | `[]` | List of string options to pass to the convert engine. | | `convertAuto` | boolean | `true` | By default the converter automatically starts once recording completed. Use `false` to disable this behavior, allowing you to start the converter manually instead. | | `hotKeys` | boolean or function | `false` | Enable [keyboard hotkeys](hotkeys.md). Disabled by default. | -| `countdown` | array | `[]` | List of steps to display as a countdown after clicking on the record button (before the recording starts). Each step has a value (string) to display and time interval (in milliseconds). For example `[{time:500,value:'3'},{time:500,value:'2'},{time:500,value:'1'}]` will display "3" for 0.5 sec, then "2" for 0.5 sec, then "1" for 0.5 sec and after that start the recording. | +| `countdown` | array | `[]` | List of steps to display as a countdown after clicking on the record button (before the recording starts). Each step has a value (string) to display and time interval (in milliseconds). For example `[{time: 500, value: '3'}, {time: 500, value: '2'}, {time: 500, value: '1'}]` will display "3" for 0.5 sec, then "2" for 0.5 sec, then "1" for 0.5 sec and after that start the recording. | | `pluginLibraryOptions` | object | `{}` | Use this object to specify additional settings for the library used by the plugin. Currently only used for the ffmpeg.wasm, ffmpeg.js, opus-recorder and vmsg plugins. | diff --git a/examples/audio-video.html b/examples/audio-video.html index 825dfd988..aabc65af3 100644 --- a/examples/audio-video.html +++ b/examples/audio-video.html @@ -39,7 +39,6 @@ audio: true, video: true, maxLength: 10, - countdownOverlay: true, debug: true } } diff --git a/src/css/components/countdown-overlay.scss b/src/css/components/countdown-overlay.scss index c5cdd5186..4e3110eac 100644 --- a/src/css/components/countdown-overlay.scss +++ b/src/css/components/countdown-overlay.scss @@ -1,4 +1,4 @@ -/* Countdown Overlay (3-2-1 countdown when start recording) +/* Countdown Overlay (countdown when start recording) -------------------------------------------------------------------------------- */ From 4227e77ab8091ffe5fdd67e997e3fa9f23c840c6 Mon Sep 17 00:00:00 2001 From: Denis Mosolov Date: Tue, 7 Sep 2021 11:39:45 +0300 Subject: [PATCH 15/47] Validate the countdown option --- src/js/controls/record-toggle.js | 8 ++- src/js/utils/validate-countdown-steps.js | 34 ++++++++++ test/utils/validate-countdown-steps.spec.js | 70 +++++++++++++++++++++ 3 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 src/js/utils/validate-countdown-steps.js create mode 100644 test/utils/validate-countdown-steps.spec.js diff --git a/src/js/controls/record-toggle.js b/src/js/controls/record-toggle.js index 95029112c..dd606d57b 100644 --- a/src/js/controls/record-toggle.js +++ b/src/js/controls/record-toggle.js @@ -6,6 +6,7 @@ import videojs from 'video.js'; import Event from '../event'; +import validateCountdownSteps from '../utils/validate-countdown-steps'; const Button = videojs.getComponent('Button'); const Component = videojs.getComponent('Component'); @@ -71,8 +72,12 @@ class RecordToggle extends Button { */ handleClick(event) { let recorder = this.player_.record(); + let countdownStepsAreValid = validateCountdownSteps(recorder.countdown); + if (!countdownStepsAreValid) { + window.console.log('videojs-record countdown option is not valid. Check out the reference https://collab-project.github.io/videojs-record/#/options'); + } if (!recorder.isRecording()) { - if (recorder.countdown.length) { + if (countdownStepsAreValid && recorder.countdown.length) { // @todo move to recorder this.startWithCountdown(recorder); } else { @@ -98,7 +103,6 @@ class RecordToggle extends Button { recorder.start(); } else { let value, time; - // @todo validation? ({value, time} = countdownSteps.shift()); this.player_.countdownOverlay.setCountdownValue(value); setTimeout(startOrDown, time); diff --git a/src/js/utils/validate-countdown-steps.js b/src/js/utils/validate-countdown-steps.js new file mode 100644 index 000000000..7a3ff4165 --- /dev/null +++ b/src/js/utils/validate-countdown-steps.js @@ -0,0 +1,34 @@ +/** + * @file validate-countdown-steps.js + * @since 4.6.0 + */ + +/** + * Check if the given step is valid. + * @param {object} step - Countdown step. + * @returns {boolean} - Returns true if step is valid. + */ +const validateStep = function (step) { + return step.hasOwnProperty('time') && step.hasOwnProperty('value') && Number.isInteger(step.time); +}; + +/** + * Check if countdown steps are valid. + * @param {object[]} countdown - Countdown steps. + * @returns {boolean} - Returns true if all steps are valid. + */ +const validateCountdownSteps = function(countdown) { + if (!Array.isArray(countdown)) { + return false; + } + + for (const step of countdown) { + if (!validateStep(step)) { + return false; + } + } + + return true; +}; + +export default validateCountdownSteps; diff --git a/test/utils/validate-countdown-steps.spec.js b/test/utils/validate-countdown-steps.spec.js new file mode 100644 index 000000000..3ae0767f1 --- /dev/null +++ b/test/utils/validate-countdown-steps.spec.js @@ -0,0 +1,70 @@ +/** + * @since 4.6.0 + */ + +import validateCountdownSteps from '../../src/js/utils/validate-countdown-steps'; + + +/** @test {validate-countdown-steps} */ +describe('validate-countdown-steps', () => { + + /** @test {validateCountdownSteps} */ + it('undefined is not valid', () => { + expect(validateCountdownSteps(undefined)).toBeFalse(); + }); + + /** @test {validateCountdownSteps} */ + it('empty array is valid', () => { + expect(validateCountdownSteps([])).toBeTrue(); + }); + + /** @test {validateCountdownSteps} */ + it('empty step is not valid', () => { + expect(validateCountdownSteps([{}])).toBeFalse(); + }); + + /** @test {validateCountdownSteps} */ + it('step without value is not valid', () => { + expect(validateCountdownSteps([{time: 1000}])).toBeFalse(); + }); + + /** @test {validateCountdownSteps} */ + it('step without time is not valid', () => { + expect(validateCountdownSteps([{value: 'Three'}])).toBeFalse(); + }); + + /** @test {validateCountdownSteps} */ + it('step with time and value is valid', () => { + expect(validateCountdownSteps([{time: 1000, value: 'Three'}])).toBeTrue(); + }); + + /** @test {validateCountdownSteps} */ + it('step with a string int value is not valid', () => { + expect(validateCountdownSteps([{time: '1000', value: 'Three'}])).toBeFalse(); + }); + + /** @test {validateCountdownSteps} */ + it('step with a string value is not valid', () => { + expect(validateCountdownSteps([{time: 1000, value: 'Three'}, {time: 'Two', value: 'Two'}])).toBeFalse(); + }); + + /** @test {validateCountdownSteps} */ + it('step without time is not valid', () => { + expect(validateCountdownSteps([{time: 1000, value: 'Three'}, {value: 'Two'}])).toBeFalse(); + }); + + /** @test {validateCountdownSteps} */ + it('step without value is not valid', () => { + expect(validateCountdownSteps([{time: 1000, value: 'Three'}, {time: 400}])).toBeFalse(); + }); + + /** @test {validateCountdownSteps} */ + it('steps with time and value are valid', () => { + expect(validateCountdownSteps([{time: 500, value: 'Three'}, {time: 400, value: 'Two'}])).toBeTrue(); + }); + + /** @test {validateCountdownSteps} */ + it('step with extra property is valid', () => { + expect(validateCountdownSteps([{time: 500, value: 'Three', foo: 'bar'}])).toBeTrue(); + }); +}); From 818e7a588fa25bfb6cb00092e011d8bc6d0caab2 Mon Sep 17 00:00:00 2001 From: Denis Mosolov Date: Wed, 8 Sep 2021 11:02:16 +0300 Subject: [PATCH 16/47] Add a check if firstChild exists --- src/js/controls/countdown-overlay.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/js/controls/countdown-overlay.js b/src/js/controls/countdown-overlay.js index daea0cc13..03ce52cb4 100644 --- a/src/js/controls/countdown-overlay.js +++ b/src/js/controls/countdown-overlay.js @@ -44,7 +44,11 @@ class CountdownOverlay extends Component { } setCountdownValue(value) { - this.el().firstChild.innerText = value; + if (this.el().firstChild) { + this.el().firstChild.innerText = value; + } else { + window.console.error('videojs-record countdown overlay is missing'); + } } } From 51ab0553357c6f36e76f50dc55b6c97afa3972d6 Mon Sep 17 00:00:00 2001 From: Denis Mosolov Date: Wed, 8 Sep 2021 11:03:12 +0300 Subject: [PATCH 17/47] Use 4 spaces for indentation --- src/css/components/countdown-overlay.scss | 24 +++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/css/components/countdown-overlay.scss b/src/css/components/countdown-overlay.scss index 4e3110eac..f0f5f9cf4 100644 --- a/src/css/components/countdown-overlay.scss +++ b/src/css/components/countdown-overlay.scss @@ -3,18 +3,18 @@ */ .vjs-record-countdown { - position: absolute; - bottom: 3em; - left: 0; - right: 0; - top: 0; - pointer-events: none; + position: absolute; + bottom: 3em; + left: 0; + right: 0; + top: 0; + pointer-events: none; - display: flex; - justify-content: center; - align-items: center; + display: flex; + justify-content: center; + align-items: center; - span { - font-size: 6em; - } + span { + font-size: 6em; + } } \ No newline at end of file From 927f3e6b782d34e5ade945aaeb1f5a67db69100b Mon Sep 17 00:00:00 2001 From: Denis Mosolov Date: Wed, 8 Sep 2021 11:04:19 +0300 Subject: [PATCH 18/47] Add PR number --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 933af982d..eb1f0423c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,7 +3,7 @@ ## 4.6.0 - unreleased - Bump required version for videojs-wavesurfer (3.8.0 or newer) -- Add `countdown` option for displaying 3-2-1 countdown before the recording. +- Add `countdown` option for displaying 3-2-1 countdown before the recording (#604) ## 4.5.0 - 2021/06/14 From dec0c944ff736d229d3d5eb03eae9beaa95e3195 Mon Sep 17 00:00:00 2001 From: Denis Mosolov Date: Wed, 8 Sep 2021 11:36:35 +0300 Subject: [PATCH 19/47] Granular test cases --- test/controls/countdown-overlay.spec.js | 34 +++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/test/controls/countdown-overlay.spec.js b/test/controls/countdown-overlay.spec.js index 762535f23..ced05303d 100644 --- a/test/controls/countdown-overlay.spec.js +++ b/test/controls/countdown-overlay.spec.js @@ -45,10 +45,9 @@ describe('controls.CountdownOverlay', () => { player.one(Event.DEVICE_READY, () => { // start toggle.trigger('click'); - expect(player.record().isRecording()).toBeFalse(); setTimeout(() => { - // started after 2 seconds + // recording is started after the countdown expect(player.record().isRecording()).toBeTrue(); }, 3000); @@ -68,4 +67,35 @@ describe('controls.CountdownOverlay', () => { player.record().getDevice(); }); }); + + it('no record during the countdown', (done) => { + let toggle = new RecordToggle(player); + + player.one(Event.DEVICE_READY, () => { + // start + toggle.trigger('click'); + + expect(player.record().isRecording()).toBeFalse(); + + setTimeout(() => { + // recording is not started during the countdown + expect(player.record().isRecording()).toBeFalse(); + }, 1000); + + setTimeout(() => { + // stop recording + player.record().stop(); + }, 4000); + }); + + player.one(Event.FINISH_RECORD, () => { + // wait till it's loaded before destroying + // (XXX: create new event for this) + setTimeout(done, 1000); + }); + + player.one(Event.READY, () => { + player.record().getDevice(); + }); + }); }); \ No newline at end of file From ef70a8e2c973a71e637c380366889f2c06cf2688 Mon Sep 17 00:00:00 2001 From: Denis Mosolov Date: Fri, 10 Sep 2021 11:39:26 +0300 Subject: [PATCH 20/47] Move countdown to Record --- src/js/controls/record-toggle.js | 41 ++-------------- src/js/videojs.record.js | 62 +++++++++++++++++++++++-- test/controls/countdown-overlay.spec.js | 35 +++++++++++++- 3 files changed, 96 insertions(+), 42 deletions(-) diff --git a/src/js/controls/record-toggle.js b/src/js/controls/record-toggle.js index dd606d57b..afebadb35 100644 --- a/src/js/controls/record-toggle.js +++ b/src/js/controls/record-toggle.js @@ -6,7 +6,6 @@ import videojs from 'video.js'; import Event from '../event'; -import validateCountdownSteps from '../utils/validate-countdown-steps'; const Button = videojs.getComponent('Button'); const Component = videojs.getComponent('Component'); @@ -72,49 +71,17 @@ class RecordToggle extends Button { */ handleClick(event) { let recorder = this.player_.record(); - let countdownStepsAreValid = validateCountdownSteps(recorder.countdown); - if (!countdownStepsAreValid) { - window.console.log('videojs-record countdown option is not valid. Check out the reference https://collab-project.github.io/videojs-record/#/options'); + if (recorder.isPrerecording()) { + // @todo do not disable the record button? stop the countdown when click record button? + return; } if (!recorder.isRecording()) { - if (countdownStepsAreValid && recorder.countdown.length) { - // @todo move to recorder - this.startWithCountdown(recorder); - } else { - recorder.start(); - } + recorder.start(); } else { recorder.stop(); } } - /** - * Display the countdown and start the recording when the last countdown step is reached - * - * @param {Record} recorder - * Instance of the Record plugin. - */ - startWithCountdown(recorder) { - let countdownSteps = [...recorder.countdown]; - let startOrDown = () => { - if (countdownSteps.length === 0) { - this.player_.countdownOverlay.hide(); - this.enable(); - recorder.start(); - } else { - let value, time; - ({value, time} = countdownSteps.shift()); - this.player_.countdownOverlay.setCountdownValue(value); - setTimeout(startOrDown, time); - } - }; - - this.disable(); - this.player_.countdownOverlay.show(); - - startOrDown(); - } - /** * Add the vjs-icon-record-stop class to the element so it can change appearance. * diff --git a/src/js/videojs.record.js b/src/js/videojs.record.js index 9345d611d..c62ffc6ae 100644 --- a/src/js/videojs.record.js +++ b/src/js/videojs.record.js @@ -23,6 +23,7 @@ import formatTime from './utils/format-time'; import setSrcObject from './utils/browser-shim'; import compareVersion from './utils/compare-version'; import {detectBrowser} from './utils/detect-browser'; +import validateCountdownSteps from './utils/validate-countdown-steps'; import {getAudioEngine, isAudioPluginActive, getVideoEngine, getConvertEngine} from './engine/engine-loader'; import {IMAGE_ONLY, AUDIO_ONLY, VIDEO_ONLY, AUDIO_VIDEO, AUDIO_SCREEN, ANIMATION, SCREEN_ONLY, getRecorderMode} from './engine/record-mode'; @@ -239,7 +240,12 @@ class Record extends Plugin { this.animationQuality = recordOptions.animationQuality; // countdown settings - this.countdown = recordOptions.countdown; + if (validateCountdownSteps(recordOptions.countdown)) { + this.countdown = recordOptions.countdown; + } else { + this.countdown = []; + window.console.warn('videojs-record countdown option is not valid. Check out the reference https://collab-project.github.io/videojs-record/#/options'); + } } /** @@ -379,6 +385,15 @@ class Record extends Plugin { } } + /** + * Indicates whether the plugin is currently prerecording. Recording is not started yet. + * + * @return {boolean} Plugin currently prerecording. + */ + isPrerecording() { + return this._prerecording; + } + /** * Indicates whether the plugin is currently recording or not. * @@ -859,6 +874,7 @@ class Record extends Plugin { return; } this._recording = true; + this._prerecording = true; // hide play/pause control if (this.player.controlBar.playToggle !== undefined) { @@ -918,6 +934,7 @@ class Record extends Plugin { switch (this.getRecordType()) { case IMAGE_ONLY: // create snapshot + // @todo add countdown this.createSnapshot(); // notify UI @@ -932,18 +949,56 @@ class Record extends Plugin { // wait for media stream on video element to actually load this.player.one(Event.LOADEDMETADATA, () => { // start actually recording process - this.startRecording(); + this.showPrerecorder().then(() => { + this.startRecording(); + }); }); break; default: // all resources have already loaded, so we can start // recording right away - this.startRecording(); + this.showPrerecorder().then(() => { + this.startRecording(); + }); } } } + /** + * Show the countdown overlay + * @return {Promise} - promise is resolved when the last countdown step is reached + * @todo rename countdown to prerecorder, because the "countDown" term is used in the player + */ + showPrerecorder() { + return new Promise(resolve => { + if (this.countdown.length === 0) { + // resolve immediately if there are no countdown steps + this._prerecording = false; + resolve(); + } + let countdownSteps = [...this.countdown]; + let resolveOrDown = () => { + if (countdownSteps.length === 0) { + this.player.countdownOverlay.hide(); + this.player.recordToggle.enable(); + this._prerecording = false; + resolve(); + } else { + let value, time; + ({value, time} = countdownSteps.shift()); + this.player.countdownOverlay.setCountdownValue(value); + setTimeout(resolveOrDown, time); + } + }; + + this.player.recordToggle.disable(); + this.player.countdownOverlay.show(); + + resolveOrDown(); + }); + } + /** * Start recording. * @private @@ -1505,6 +1560,7 @@ class Record extends Plugin { */ resetState() { this._recording = false; + // @todo add _prerecording this._processing = false; this._deviceActive = false; this.devices = []; diff --git a/test/controls/countdown-overlay.spec.js b/test/controls/countdown-overlay.spec.js index ced05303d..9349260e8 100644 --- a/test/controls/countdown-overlay.spec.js +++ b/test/controls/countdown-overlay.spec.js @@ -72,14 +72,45 @@ describe('controls.CountdownOverlay', () => { let toggle = new RecordToggle(player); player.one(Event.DEVICE_READY, () => { + const onRecordStarted = () => { + expect(true).toBe(false); + }; + player.one(Event.START_RECORD, onRecordStarted); + setTimeout(() => { + // recording is not started during the countdown + player.off(Event.START_RECORD, onRecordStarted); + }, 2000); + // start toggle.trigger('click'); - expect(player.record().isRecording()).toBeFalse(); + setTimeout(() => { + // stop recording + player.record().stop(); + }, 4000); + }); + + player.one(Event.FINISH_RECORD, () => { + // wait till it's loaded before destroying + // (XXX: create new event for this) + setTimeout(done, 1000); + }); + + player.one(Event.READY, () => { + player.record().getDevice(); + }); + }); + + it('prerecording during the countdown', (done) => { + let toggle = new RecordToggle(player); + + player.one(Event.DEVICE_READY, () => { + // start + toggle.trigger('click'); setTimeout(() => { // recording is not started during the countdown - expect(player.record().isRecording()).toBeFalse(); + expect(player.record().isPrerecording()).toBeTrue(); }, 1000); setTimeout(() => { From d29e86ea7b0dcb77dbc83e5eb34e5a5d9bc6ac52 Mon Sep 17 00:00:00 2001 From: Denis Mosolov Date: Tue, 14 Sep 2021 12:45:57 +0300 Subject: [PATCH 21/47] Add countdown to image only record type --- examples/image-only.html | 9 +++++ src/js/controls/camera-button.js | 6 ++- src/js/controls/countdown-overlay.js | 55 ++++++++++++++++++++++++++++ src/js/controls/record-toggle.js | 55 +++++++++++++++++++++++++--- src/js/event.js | 4 ++ src/js/videojs.record.js | 44 +++++++++++++++++----- 6 files changed, 157 insertions(+), 16 deletions(-) diff --git a/examples/image-only.html b/examples/image-only.html index 0bc995dd0..c5f7cbda4 100644 --- a/examples/image-only.html +++ b/examples/image-only.html @@ -39,6 +39,15 @@ height: 480, plugins: { record: { + // @todo add this example sample to the countdown.html + countdown: [ + {value: 'Five!', time: 1000}, + {value: 'Four!', time: 1000}, + {value: 'Three', time: 1000}, + {value: '2', time: 1000}, + {value: '1', time: 500}, + {value: 'Go!', time: 500}, + ], debug: true, imageOutputType: 'dataURL', imageOutputFormat: 'image/png', diff --git a/src/js/controls/camera-button.js b/src/js/controls/camera-button.js index 19a1290a4..719d677fb 100644 --- a/src/js/controls/camera-button.js +++ b/src/js/controls/camera-button.js @@ -72,10 +72,14 @@ class CameraButton extends Button { handleClick(event) { let recorder = this.player_.record(); - if (!recorder.isProcessing()) { + if (!recorder.isProcessing() && !recorder.isPrerecording()) { // create snapshot recorder.start(); } else { + if (recorder.isPrerecording()) { + recorder.abortPrerecording(); + } + // retry recorder.retrySnapshot(); diff --git a/src/js/controls/countdown-overlay.js b/src/js/controls/countdown-overlay.js index 03ce52cb4..a40d4608d 100644 --- a/src/js/controls/countdown-overlay.js +++ b/src/js/controls/countdown-overlay.js @@ -4,6 +4,7 @@ */ import videojs from 'video.js'; +import Event from '../event'; const Component = videojs.getComponent('Component'); @@ -14,6 +15,22 @@ const Component = videojs.getComponent('Component'); * @augments videojs.Component */ class CountdownOverlay extends Component { + + /** + * The constructor function for the class. + * + * @private + * @param {(videojs.Player|Object)} player - Video.js player instance. + * @param {Object} options - Player options. + */ + constructor(player, options) { + super(player, options); + + this.on(this.player_, Event.PRERECORDER_START, this.onPrerecorderStart); + this.on(this.player_, Event.PRERECORDER_FINISH, this.onPrerecorderFinish); + this.on(this.player_, Event.PRERECORDER_ABORT, this.onPrerecorderAbort); + } + /** * Create the `Countdown`s DOM element. * @@ -50,6 +67,44 @@ class CountdownOverlay extends Component { window.console.error('videojs-record countdown overlay is missing'); } } + + /** + * Show prerecorder overlay + * + * @param {EventTarget~Event} [event] + * The event that caused this function to run. + * + * @todo unit test + */ + onPrerecorderStart(event) { + this.show(); + } + + /** + * Hide prerecorder overlay + * + * @param {EventTarget~Event} [event] + * The event that caused this function to run. + * + * @todo unit test + */ + onPrerecorderFinish(event) { + this.setCountdownValue(''); + this.hide(); + } + + /** + * Hide prerecorder overlay + * + * @param {EventTarget~Event} [event] + * The event that caused this function to run. + * + * @todo unit test + */ + onPrerecorderAbort(event) { + this.setCountdownValue(''); + this.hide(); + } } Component.registerComponent('CountdownOverlay', CountdownOverlay); diff --git a/src/js/controls/record-toggle.js b/src/js/controls/record-toggle.js index afebadb35..beaf523c6 100644 --- a/src/js/controls/record-toggle.js +++ b/src/js/controls/record-toggle.js @@ -35,6 +35,9 @@ class RecordToggle extends Button { this.on(this.player_, Event.START_RECORD, this.onStart); this.on(this.player_, Event.STOP_RECORD, this.onStop); + this.on(this.player_, Event.PRERECORDER_START, this.onPrerecorderStart); + this.on(this.player_, Event.PRERECORDER_FINISH, this.onPrerecorderFinish); + this.on(this.player_, Event.PRERECORDER_FINISH, this.onPrerecorderAbort); } /** @@ -45,6 +48,9 @@ class RecordToggle extends Button { this.off(this.player_, Event.START_RECORD, this.onStart); this.off(this.player_, Event.STOP_RECORD, this.onStop); + this.off(this.player_, Event.PRERECORDER_START, this.onPrerecorderStart); + this.off(this.player_, Event.PRERECORDER_FINISH, this.onPrerecorderFinish); + this.off(this.player_, Event.PRERECORDER_FINISH, this.onPrerecorderAbort); } /** @@ -71,13 +77,13 @@ class RecordToggle extends Button { */ handleClick(event) { let recorder = this.player_.record(); - if (recorder.isPrerecording()) { - // @todo do not disable the record button? stop the countdown when click record button? - return; - } - if (!recorder.isRecording()) { + if (!recorder.isProcessing() && !recorder.isPrerecording()) { recorder.start(); } else { + if (recorder.isPrerecording()) { + recorder.abortPrerecording(); + } + recorder.stop(); } } @@ -115,6 +121,45 @@ class RecordToggle extends Button { // change the button text this.controlText('Record'); } + + /** + * Show prerecorder overlay + * + * @param {EventTarget~Event} [event] + * The event that caused this function to run. + * + * @listens Player#prerecorderStart + * @todo unit test + */ + onPrerecorderStart(event) { + this.enable(); + } + + /** + * Hide prerecorder overlay + * + * @param {EventTarget~Event} [event] + * The event that caused this function to run. + * + * @listens Player#prerecorderFinish + * @todo unit test + */ + onPrerecorderFinish(event) { + this.disable(); + } + + /** + * Hide prerecorder overlay + * + * @param {EventTarget~Event} [event] + * The event that caused this function to run. + * + * @listens Player#prerecorderAbort + * @todo unit test + */ + onPrerecorderAbort(event) { + // @todo implement me + } } /** diff --git a/src/js/event.js b/src/js/event.js index c5387c9e7..6b6e54cd4 100644 --- a/src/js/event.js +++ b/src/js/event.js @@ -36,6 +36,10 @@ Event.FINISH_CONVERT = 'finishConvert'; Event.ENTER_PIP = 'enterPIP'; Event.LEAVE_PIP = 'leavePIP'; Event.RETRY = 'retry'; +// @todo docs +Event.PRERECORDER_START = 'prerecorderStart'; +Event.PRERECORDER_FINISH = 'prerecorderFinish'; +Event.PRERECORDER_ABORT = 'prerecorderAbort'; // dom Event.ENTERPICTUREINPICTURE = 'enterpictureinpicture'; diff --git a/src/js/videojs.record.js b/src/js/videojs.record.js index c62ffc6ae..1e5993d31 100644 --- a/src/js/videojs.record.js +++ b/src/js/videojs.record.js @@ -874,6 +874,7 @@ class Record extends Plugin { return; } this._recording = true; + // @todo what if we're in the prerecording state? stop the current prerecording? this._prerecording = true; // hide play/pause control @@ -934,8 +935,9 @@ class Record extends Plugin { switch (this.getRecordType()) { case IMAGE_ONLY: // create snapshot - // @todo add countdown - this.createSnapshot(); + this.showPrerecorder().then(() => { + this.createSnapshot(); + }); // notify UI this.player.trigger(Event.START_RECORD); @@ -977,24 +979,27 @@ class Record extends Plugin { this._prerecording = false; resolve(); } - let countdownSteps = [...this.countdown]; + + this.player.trigger(Event.PRERECORDER_START); + + let countdownSteps = [...this.countdown]; // @todo add to this that it can be cleared let resolveOrDown = () => { if (countdownSteps.length === 0) { - this.player.countdownOverlay.hide(); - this.player.recordToggle.enable(); + this.player.clearTimeout(this.prerecorderTimeoutID); this._prerecording = false; + + this.player.trigger(Event.PRERECORDER_FINISH); + resolve(); } else { let value, time; ({value, time} = countdownSteps.shift()); + // @todo trigger an event and pass current step as an event data this.player.countdownOverlay.setCountdownValue(value); - setTimeout(resolveOrDown, time); + this.prerecorderTimeoutID = this.player.setTimeout(resolveOrDown, time); } }; - this.player.recordToggle.disable(); - this.player.countdownOverlay.show(); - resolveOrDown(); }); } @@ -1059,6 +1064,24 @@ class Record extends Plugin { } } + /** + * Abort prerecording countdown + */ + abortPrerecording() { + // Stop prerecorder steps + this.player.clearTimeout(this.prerecorderTimeoutID); + + this._prerecording = false; + // @todo investigate and fix + // when a user clicks on the recording button (during prerecording count down) + // and we have to set up this property to false somewhere + // otherwise recorder thinks that we still processing something and does not allow us to start a new recording + this._processing = false; + + // Notify the UI + this.player.trigger(Event.PRERECORDER_ABORT); + } + /** * Stop device(s) and recording if active. */ @@ -1560,7 +1583,7 @@ class Record extends Plugin { */ resetState() { this._recording = false; - // @todo add _prerecording + this._prerecording = false; this._processing = false; this._deviceActive = false; this.devices = []; @@ -1694,6 +1717,7 @@ class Record extends Plugin { */ retrySnapshot() { this._processing = false; + this._prerecording = false; // retry: hide the snapshot this.player.recordCanvas.hide(); From 66d90e27fe3ff3675db841b8f85661f390d32f95 Mon Sep 17 00:00:00 2001 From: Denis Mosolov Date: Fri, 8 Oct 2021 13:20:04 +0300 Subject: [PATCH 22/47] Tweak CameraButton --- src/js/controls/camera-button.js | 37 ++++++++++++- test/controls/camera-button.spec.js | 84 +++++++++++++++++++++++++++++ test/test-helpers.js | 4 +- 3 files changed, 121 insertions(+), 4 deletions(-) diff --git a/src/js/controls/camera-button.js b/src/js/controls/camera-button.js index 719d677fb..f4d217fec 100644 --- a/src/js/controls/camera-button.js +++ b/src/js/controls/camera-button.js @@ -33,6 +33,8 @@ class CameraButton extends Button { enable() { super.enable(); + this.on(this.player_, Event.PRERECORDER_START, this.onStartPrerecorder); + this.on(this.player_, Event.PRERECORDER_FINISH, this.onFinishPrerecorder); this.on(this.player_, Event.START_RECORD, this.onStart); this.on(this.player_, Event.STOP_RECORD, this.onStop); } @@ -43,6 +45,8 @@ class CameraButton extends Button { disable() { super.disable(); + this.off(this.player_, Event.PRERECORDER_START, this.onStartPrerecorder); + this.off(this.player_, Event.PRERECORDER_FINISH, this.onFinishPrerecorder); this.off(this.player_, Event.START_RECORD, this.onStart); this.off(this.player_, Event.STOP_RECORD, this.onStop); } @@ -104,8 +108,11 @@ class CameraButton extends Button { this.removeClass('vjs-icon-photo-camera'); this.addClass('vjs-icon-replay'); - // change the button text - this.controlText('Retry'); + let recorder = this.player_.record(); + if (!recorder.isPrerecording()) { + // change the button text + this.controlText('Retry'); + } } /** @@ -124,6 +131,32 @@ class CameraButton extends Button { // change the button text this.controlText('Image'); } + + /** + * Change control text. + * + * @param {EventTarget~Event} [event] + * The event that caused this function to run. + * + * @listens Player#startRecord + */ + onStartPrerecorder(event) { + // change the button text + this.controlText('Reset'); + } + + /** + * Change control text. + * + * @param {EventTarget~Event} [event] + * The event that caused this function to run. + * + * @listens Player#startRecord + */ + onFinishPrerecorder(event) { + // change the button text + this.controlText('Retry'); + } } /** diff --git a/test/controls/camera-button.spec.js b/test/controls/camera-button.spec.js index bd5038b19..91da33229 100644 --- a/test/controls/camera-button.spec.js +++ b/test/controls/camera-button.spec.js @@ -11,6 +11,7 @@ import CameraButton from '../../src/js/controls/camera-button'; /** @test {camera-button} */ describe('controls.CameraButton', () => { let player; + let playerWithPrerecorder; let originalTimeout; beforeEach(() => { @@ -19,10 +20,26 @@ describe('controls.CameraButton', () => { // create new image-only player player = TestHelpers.makeImageOnlyPlayer(); + + // create new image-only player with a pre-recorder + playerWithPrerecorder = TestHelpers.makeImageOnlyPlayer({ + plugins: { + record: { + image: true, + countdown: [ + {value: '3', time: 1000}, + {value: '2', time: 1000}, + {value: '1', time: 1000}, + ], + debug: true + } + } + }, 'imageOnlyPrerecorder'); }); afterEach(() => { player.dispose(); + playerWithPrerecorder.dispose(); jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout; }); @@ -60,12 +77,14 @@ describe('controls.CameraButton', () => { setTimeout(() => { // stop recording + // not sure if it makes any sense here, because done() is called after the click button.trigger('click'); done(); }, 2000); }); player.one(Event.STOP_RECORD, () => { + // not sure when it's called, dead code? expect(button.hasClass('vjs-icon-replay')).toBeFalse(); expect(button.hasClass('vjs-icon-photo-camera')).toBeTrue(); expect(button.controlText_).toEqual('Image'); @@ -81,4 +100,69 @@ describe('controls.CameraButton', () => { }); }); + it('changes appearance when startRecord or stopRecord is triggered and pre-recorder is on', (done) => { + let button = new CameraButton(playerWithPrerecorder); + + expect(button.hasClass('vjs-icon-photo-camera')).toBeTrue(); + + playerWithPrerecorder.one(Event.PRERECORDER_START, () => { + setTimeout(() => { + expect(button.controlText_).toEqual('Reset'); + }, 2000); + }); + playerWithPrerecorder.one(Event.START_RECORD, () => { + expect(button.hasClass('vjs-icon-photo-camera')).toBeFalse(); + expect(button.hasClass('vjs-icon-replay')).toBeTrue(); + expect(button.controlText_).toEqual('Reset'); + + setTimeout(() => { + expect(button.hasClass('vjs-icon-photo-camera')).toBeFalse(); + expect(button.hasClass('vjs-icon-replay')).toBeTrue(); + // it took a photo after 3-2-1 countdown + expect(button.controlText_).toEqual('Retry'); + + done(); + }, 4000); + }); + + playerWithPrerecorder.one(Event.DEVICE_READY, () => { + button.trigger('click'); + }); + + playerWithPrerecorder.one(Event.READY, () => { + playerWithPrerecorder.record().getDevice(); + }); + }); + + it('changes appearance when stopRecord is triggered and pre-recorder is on', (done) => { + let button = new CameraButton(playerWithPrerecorder); + + expect(button.hasClass('vjs-icon-photo-camera')).toBeTrue(); + + playerWithPrerecorder.one(Event.PRERECORDER_START, () => { + setTimeout(() => { + expect(button.controlText_).toEqual('Reset'); + }, 500); + }); + playerWithPrerecorder.one(Event.RETRY, () => { + expect(button.hasClass('vjs-icon-replay')).toBeFalse(); + expect(button.hasClass('vjs-icon-photo-camera')).toBeTrue(); + expect(button.controlText_).toEqual('Image'); + + done(); + }); + playerWithPrerecorder.one(Event.PRERECORDER_START, () => { + setTimeout(() => { + // Stop the pre-recording + button.trigger('click'); + }, 1000); + }); + playerWithPrerecorder.one(Event.DEVICE_READY, () => { + button.trigger('click'); + }); + + playerWithPrerecorder.one(Event.READY, () => { + playerWithPrerecorder.record().getDevice(); + }); + }); }); \ No newline at end of file diff --git a/test/test-helpers.js b/test/test-helpers.js index 0e9f5c240..af67c07bb 100644 --- a/test/test-helpers.js +++ b/test/test-helpers.js @@ -313,8 +313,8 @@ const TestHelpers = { return this.makePlayer(tag, opts); }, - makeImageOnlyPlayer(newOptions) { - let tag = TestHelpers.makeTag('video', 'imageOnly'); + makeImageOnlyPlayer(newOptions, idName = 'imageOnly') { + let tag = TestHelpers.makeTag('video', idName); let opts = { controls: true, autoplay: false, From 60fc02899d7a87f4c1954ddb2ae71bccb04c2108 Mon Sep 17 00:00:00 2001 From: Denis Mosolov Date: Tue, 19 Oct 2021 14:52:45 +0300 Subject: [PATCH 23/47] Abort video pre-recorder --- src/js/controls/camera-button.js | 8 +++--- src/js/controls/countdown-overlay.js | 42 ---------------------------- src/js/controls/record-toggle.js | 16 +++++------ src/js/event.js | 6 ++-- src/js/videojs.record.js | 21 ++++++++------ test/controls/camera-button.spec.js | 6 ++-- 6 files changed, 31 insertions(+), 68 deletions(-) diff --git a/src/js/controls/camera-button.js b/src/js/controls/camera-button.js index f4d217fec..ede470995 100644 --- a/src/js/controls/camera-button.js +++ b/src/js/controls/camera-button.js @@ -33,8 +33,8 @@ class CameraButton extends Button { enable() { super.enable(); - this.on(this.player_, Event.PRERECORDER_START, this.onStartPrerecorder); - this.on(this.player_, Event.PRERECORDER_FINISH, this.onFinishPrerecorder); + this.on(this.player_, Event.START_PRERECORDER, this.onStartPrerecorder); + this.on(this.player_, Event.FINISH_PRERECORDER, this.onFinishPrerecorder); this.on(this.player_, Event.START_RECORD, this.onStart); this.on(this.player_, Event.STOP_RECORD, this.onStop); } @@ -45,8 +45,8 @@ class CameraButton extends Button { disable() { super.disable(); - this.off(this.player_, Event.PRERECORDER_START, this.onStartPrerecorder); - this.off(this.player_, Event.PRERECORDER_FINISH, this.onFinishPrerecorder); + this.off(this.player_, Event.START_PRERECORDER, this.onStartPrerecorder); + this.off(this.player_, Event.FINISH_PRERECORDER, this.onFinishPrerecorder); this.off(this.player_, Event.START_RECORD, this.onStart); this.off(this.player_, Event.STOP_RECORD, this.onStop); } diff --git a/src/js/controls/countdown-overlay.js b/src/js/controls/countdown-overlay.js index a40d4608d..34749ffa4 100644 --- a/src/js/controls/countdown-overlay.js +++ b/src/js/controls/countdown-overlay.js @@ -25,10 +25,6 @@ class CountdownOverlay extends Component { */ constructor(player, options) { super(player, options); - - this.on(this.player_, Event.PRERECORDER_START, this.onPrerecorderStart); - this.on(this.player_, Event.PRERECORDER_FINISH, this.onPrerecorderFinish); - this.on(this.player_, Event.PRERECORDER_ABORT, this.onPrerecorderAbort); } /** @@ -67,44 +63,6 @@ class CountdownOverlay extends Component { window.console.error('videojs-record countdown overlay is missing'); } } - - /** - * Show prerecorder overlay - * - * @param {EventTarget~Event} [event] - * The event that caused this function to run. - * - * @todo unit test - */ - onPrerecorderStart(event) { - this.show(); - } - - /** - * Hide prerecorder overlay - * - * @param {EventTarget~Event} [event] - * The event that caused this function to run. - * - * @todo unit test - */ - onPrerecorderFinish(event) { - this.setCountdownValue(''); - this.hide(); - } - - /** - * Hide prerecorder overlay - * - * @param {EventTarget~Event} [event] - * The event that caused this function to run. - * - * @todo unit test - */ - onPrerecorderAbort(event) { - this.setCountdownValue(''); - this.hide(); - } } Component.registerComponent('CountdownOverlay', CountdownOverlay); diff --git a/src/js/controls/record-toggle.js b/src/js/controls/record-toggle.js index beaf523c6..171b93bf4 100644 --- a/src/js/controls/record-toggle.js +++ b/src/js/controls/record-toggle.js @@ -35,9 +35,9 @@ class RecordToggle extends Button { this.on(this.player_, Event.START_RECORD, this.onStart); this.on(this.player_, Event.STOP_RECORD, this.onStop); - this.on(this.player_, Event.PRERECORDER_START, this.onPrerecorderStart); - this.on(this.player_, Event.PRERECORDER_FINISH, this.onPrerecorderFinish); - this.on(this.player_, Event.PRERECORDER_FINISH, this.onPrerecorderAbort); + this.on(this.player_, Event.START_PRERECORDER, this.onPrerecorderStart); + this.on(this.player_, Event.FINISH_PRERECORDER, this.onPrerecorderFinish); + this.on(this.player_, Event.FINISH_PRERECORDER, this.onPrerecorderAbort); } /** @@ -48,9 +48,9 @@ class RecordToggle extends Button { this.off(this.player_, Event.START_RECORD, this.onStart); this.off(this.player_, Event.STOP_RECORD, this.onStop); - this.off(this.player_, Event.PRERECORDER_START, this.onPrerecorderStart); - this.off(this.player_, Event.PRERECORDER_FINISH, this.onPrerecorderFinish); - this.off(this.player_, Event.PRERECORDER_FINISH, this.onPrerecorderAbort); + this.off(this.player_, Event.START_PRERECORDER, this.onPrerecorderStart); + this.off(this.player_, Event.FINISH_PRERECORDER, this.onPrerecorderFinish); + this.off(this.player_, Event.FINISH_PRERECORDER, this.onPrerecorderAbort); } /** @@ -82,9 +82,9 @@ class RecordToggle extends Button { } else { if (recorder.isPrerecording()) { recorder.abortPrerecording(); + } else { + recorder.stop(); } - - recorder.stop(); } } diff --git a/src/js/event.js b/src/js/event.js index 6b6e54cd4..e438a15ea 100644 --- a/src/js/event.js +++ b/src/js/event.js @@ -37,9 +37,9 @@ Event.ENTER_PIP = 'enterPIP'; Event.LEAVE_PIP = 'leavePIP'; Event.RETRY = 'retry'; // @todo docs -Event.PRERECORDER_START = 'prerecorderStart'; -Event.PRERECORDER_FINISH = 'prerecorderFinish'; -Event.PRERECORDER_ABORT = 'prerecorderAbort'; +Event.START_PRERECORDER = 'startPrerecorder'; +Event.FINISH_PRERECORDER = 'finishPrerecorder'; +Event.ABORT_PRERECORDER = 'abortPrerecorder'; // dom Event.ENTERPICTUREINPICTURE = 'enterpictureinpicture'; diff --git a/src/js/videojs.record.js b/src/js/videojs.record.js index 1e5993d31..a003878e8 100644 --- a/src/js/videojs.record.js +++ b/src/js/videojs.record.js @@ -968,7 +968,7 @@ class Record extends Plugin { } /** - * Show the countdown overlay + * Show the prerecorder overlay and start the countdown * @return {Promise} - promise is resolved when the last countdown step is reached * @todo rename countdown to prerecorder, because the "countDown" term is used in the player */ @@ -980,20 +980,22 @@ class Record extends Plugin { resolve(); } - this.player.trigger(Event.PRERECORDER_START); + this.player.trigger(Event.START_PRERECORDER); + this.player.countdownOverlay.setCountdownValue(''); + this.player.countdownOverlay.show(); - let countdownSteps = [...this.countdown]; // @todo add to this that it can be cleared + let steps = [...this.countdown]; let resolveOrDown = () => { - if (countdownSteps.length === 0) { + if (steps.length === 0) { this.player.clearTimeout(this.prerecorderTimeoutID); this._prerecording = false; - - this.player.trigger(Event.PRERECORDER_FINISH); + this.player.countdownOverlay.hide(); + this.player.trigger(Event.FINISH_PRERECORDER); resolve(); } else { let value, time; - ({value, time} = countdownSteps.shift()); + ({value, time} = steps.shift()); // @todo trigger an event and pass current step as an event data this.player.countdownOverlay.setCountdownValue(value); this.prerecorderTimeoutID = this.player.setTimeout(resolveOrDown, time); @@ -1071,6 +1073,7 @@ class Record extends Plugin { // Stop prerecorder steps this.player.clearTimeout(this.prerecorderTimeoutID); + this._recording = false; this._prerecording = false; // @todo investigate and fix // when a user clicks on the recording button (during prerecording count down) @@ -1078,8 +1081,10 @@ class Record extends Plugin { // otherwise recorder thinks that we still processing something and does not allow us to start a new recording this._processing = false; + this.player.countdownOverlay.hide(); + // Notify the UI - this.player.trigger(Event.PRERECORDER_ABORT); + this.player.trigger(Event.ABORT_PRERECORDER); } /** diff --git a/test/controls/camera-button.spec.js b/test/controls/camera-button.spec.js index 91da33229..ef62581fe 100644 --- a/test/controls/camera-button.spec.js +++ b/test/controls/camera-button.spec.js @@ -105,7 +105,7 @@ describe('controls.CameraButton', () => { expect(button.hasClass('vjs-icon-photo-camera')).toBeTrue(); - playerWithPrerecorder.one(Event.PRERECORDER_START, () => { + playerWithPrerecorder.one(Event.START_PRERECORDER, () => { setTimeout(() => { expect(button.controlText_).toEqual('Reset'); }, 2000); @@ -139,7 +139,7 @@ describe('controls.CameraButton', () => { expect(button.hasClass('vjs-icon-photo-camera')).toBeTrue(); - playerWithPrerecorder.one(Event.PRERECORDER_START, () => { + playerWithPrerecorder.one(Event.START_PRERECORDER, () => { setTimeout(() => { expect(button.controlText_).toEqual('Reset'); }, 500); @@ -151,7 +151,7 @@ describe('controls.CameraButton', () => { done(); }); - playerWithPrerecorder.one(Event.PRERECORDER_START, () => { + playerWithPrerecorder.one(Event.START_PRERECORDER, () => { setTimeout(() => { // Stop the pre-recording button.trigger('click'); From 3f239b8de694306c411114fc6edbe03b588eb3a3 Mon Sep 17 00:00:00 2001 From: Denis Mosolov Date: Thu, 21 Oct 2021 13:09:40 +0300 Subject: [PATCH 24/47] Update record-toggle test --- test/controls/record-toggle.spec.js | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/test/controls/record-toggle.spec.js b/test/controls/record-toggle.spec.js index 2084e016f..60f7fb1ba 100644 --- a/test/controls/record-toggle.spec.js +++ b/test/controls/record-toggle.spec.js @@ -85,8 +85,8 @@ describe('controls.RecordToggle', () => { }); }); - it('record button is locked while the countdown is running', (done) => { - // create an instance of a player with the countdown + it('accept interaction: pre-recorder', (done) => { + // create an instance of a player with the pre-recorder player.dispose(); player = TestHelpers.makeAudioVideoPlayer({ plugins: { @@ -104,22 +104,17 @@ describe('controls.RecordToggle', () => { player.one(Event.DEVICE_READY, () => { // start toggle.trigger('click'); + expect(player.record().isPrerecording()).toBeTrue(); + expect(player.record().isRecording()).toBeTrue(); setTimeout(() => { - // countdown is running, record button is locked - expect(toggle.el().hasAttribute('disabled')).toBeTrue(); - }, 1000); - - setTimeout(() => { - // stop recording - player.record().stop(); - }, 3000); - }); + // stop + toggle.trigger('click'); + expect(player.record().isPrerecording()).toBeFalse(); + expect(player.record().isRecording()).toBeFalse(); - player.one(Event.FINISH_RECORD, () => { - // wait till it's loaded before destroying - // (XXX: create new event for this) - setTimeout(done, 1000); + done(); + }, 1000); }); player.one(Event.READY, () => { From 01ac818ecef87a4a78fc9c128d5eea36bfb2276d Mon Sep 17 00:00:00 2001 From: Denis Mosolov Date: Thu, 21 Oct 2021 13:23:12 +0300 Subject: [PATCH 25/47] Update countdown-overlay test --- test/controls/countdown-overlay.spec.js | 26 +++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/test/controls/countdown-overlay.spec.js b/test/controls/countdown-overlay.spec.js index 9349260e8..9805dd6ad 100644 --- a/test/controls/countdown-overlay.spec.js +++ b/test/controls/countdown-overlay.spec.js @@ -39,6 +39,32 @@ describe('controls.CountdownOverlay', () => { expect(countdown.el().innerHTML).toEqual(''); }); + it('pre-recorder overlay appears', () => { + let countdown = new CountdownOverlay(player); + + player.one(Event.DEVICE_READY, () => { + // start + player.record().start(); + + setTimeout(() => { + // overlay is visible + expect(countdown.el().hasClass('vjs-hidden')).toBeFalse(); + // overlay is not empty + expect(countdown.el().innerHTML).not.toEqual(''); + + // stop + player.record().stop(); + + done(); + }, 1000); + }); + + player.one(Event.READY, () => { + player.record().getDevice(); + }); + }); + + // @todo-coachup move to videojs.record.spec.ts it('start record after the countdown', (done) => { let toggle = new RecordToggle(player); From 9e26caedd082d03bd8389d7814e4b9d37fbedf2a Mon Sep 17 00:00:00 2001 From: Denis Mosolov Date: Sun, 24 Oct 2021 12:15:37 +0300 Subject: [PATCH 26/47] Rename events --- docs/events.md | 3 +++ src/js/controls/camera-button.js | 8 ++++---- src/js/controls/record-toggle.js | 12 ++++++------ src/js/event.js | 7 +++---- src/js/videojs.record.js | 6 +++--- test/controls/camera-button.spec.js | 6 +++--- 6 files changed, 22 insertions(+), 20 deletions(-) diff --git a/docs/events.md b/docs/events.md index 157e13654..4fe265cf5 100644 --- a/docs/events.md +++ b/docs/events.md @@ -28,3 +28,6 @@ player.on('startRecord', function() { | `startConvert` | The convert plugin started processing the recorded data. | | `finishConvert` | The converted data is available. Inspect the [player.convertedData](recorded-data#convert-data) object for the converted data. | | `retry` | User clicked on `retry` to take another picture. Only available for image-only mode. | +| `startCountdown` | The countdown before the recording is about to start. | +| `finishCountdown` | The countdown before the recording is finished. | +| `abortCountdown` | The countdown is stopped by a user interaction. | diff --git a/src/js/controls/camera-button.js b/src/js/controls/camera-button.js index ede470995..7ada26531 100644 --- a/src/js/controls/camera-button.js +++ b/src/js/controls/camera-button.js @@ -33,8 +33,8 @@ class CameraButton extends Button { enable() { super.enable(); - this.on(this.player_, Event.START_PRERECORDER, this.onStartPrerecorder); - this.on(this.player_, Event.FINISH_PRERECORDER, this.onFinishPrerecorder); + this.on(this.player_, Event.START_COUNTDOWN, this.onStartPrerecorder); + this.on(this.player_, Event.FINISH_COUNTDOWN, this.onFinishPrerecorder); this.on(this.player_, Event.START_RECORD, this.onStart); this.on(this.player_, Event.STOP_RECORD, this.onStop); } @@ -45,8 +45,8 @@ class CameraButton extends Button { disable() { super.disable(); - this.off(this.player_, Event.START_PRERECORDER, this.onStartPrerecorder); - this.off(this.player_, Event.FINISH_PRERECORDER, this.onFinishPrerecorder); + this.off(this.player_, Event.START_COUNTDOWN, this.onStartPrerecorder); + this.off(this.player_, Event.FINISH_COUNTDOWN, this.onFinishPrerecorder); this.off(this.player_, Event.START_RECORD, this.onStart); this.off(this.player_, Event.STOP_RECORD, this.onStop); } diff --git a/src/js/controls/record-toggle.js b/src/js/controls/record-toggle.js index 171b93bf4..53ad8ee75 100644 --- a/src/js/controls/record-toggle.js +++ b/src/js/controls/record-toggle.js @@ -35,9 +35,9 @@ class RecordToggle extends Button { this.on(this.player_, Event.START_RECORD, this.onStart); this.on(this.player_, Event.STOP_RECORD, this.onStop); - this.on(this.player_, Event.START_PRERECORDER, this.onPrerecorderStart); - this.on(this.player_, Event.FINISH_PRERECORDER, this.onPrerecorderFinish); - this.on(this.player_, Event.FINISH_PRERECORDER, this.onPrerecorderAbort); + this.on(this.player_, Event.START_COUNTDOWN, this.onPrerecorderStart); + this.on(this.player_, Event.FINISH_COUNTDOWN, this.onPrerecorderFinish); + this.on(this.player_, Event.FINISH_COUNTDOWN, this.onPrerecorderAbort); } /** @@ -48,9 +48,9 @@ class RecordToggle extends Button { this.off(this.player_, Event.START_RECORD, this.onStart); this.off(this.player_, Event.STOP_RECORD, this.onStop); - this.off(this.player_, Event.START_PRERECORDER, this.onPrerecorderStart); - this.off(this.player_, Event.FINISH_PRERECORDER, this.onPrerecorderFinish); - this.off(this.player_, Event.FINISH_PRERECORDER, this.onPrerecorderAbort); + this.off(this.player_, Event.START_COUNTDOWN, this.onPrerecorderStart); + this.off(this.player_, Event.FINISH_COUNTDOWN, this.onPrerecorderFinish); + this.off(this.player_, Event.FINISH_COUNTDOWN, this.onPrerecorderAbort); } /** diff --git a/src/js/event.js b/src/js/event.js index e438a15ea..d1d6331ba 100644 --- a/src/js/event.js +++ b/src/js/event.js @@ -36,10 +36,9 @@ Event.FINISH_CONVERT = 'finishConvert'; Event.ENTER_PIP = 'enterPIP'; Event.LEAVE_PIP = 'leavePIP'; Event.RETRY = 'retry'; -// @todo docs -Event.START_PRERECORDER = 'startPrerecorder'; -Event.FINISH_PRERECORDER = 'finishPrerecorder'; -Event.ABORT_PRERECORDER = 'abortPrerecorder'; +Event.START_COUNTDOWN = 'startCountdown'; +Event.FINISH_COUNTDOWN = 'finishCountdown'; +Event.ABORT_COUNTDOWN = 'abortCountdown'; // dom Event.ENTERPICTUREINPICTURE = 'enterpictureinpicture'; diff --git a/src/js/videojs.record.js b/src/js/videojs.record.js index a003878e8..b8da9811f 100644 --- a/src/js/videojs.record.js +++ b/src/js/videojs.record.js @@ -980,7 +980,7 @@ class Record extends Plugin { resolve(); } - this.player.trigger(Event.START_PRERECORDER); + this.player.trigger(Event.START_COUNTDOWN); this.player.countdownOverlay.setCountdownValue(''); this.player.countdownOverlay.show(); @@ -990,7 +990,7 @@ class Record extends Plugin { this.player.clearTimeout(this.prerecorderTimeoutID); this._prerecording = false; this.player.countdownOverlay.hide(); - this.player.trigger(Event.FINISH_PRERECORDER); + this.player.trigger(Event.FINISH_COUNTDOWN); resolve(); } else { @@ -1084,7 +1084,7 @@ class Record extends Plugin { this.player.countdownOverlay.hide(); // Notify the UI - this.player.trigger(Event.ABORT_PRERECORDER); + this.player.trigger(Event.ABORT_COUNTDOWN); } /** diff --git a/test/controls/camera-button.spec.js b/test/controls/camera-button.spec.js index ef62581fe..614fbf823 100644 --- a/test/controls/camera-button.spec.js +++ b/test/controls/camera-button.spec.js @@ -105,7 +105,7 @@ describe('controls.CameraButton', () => { expect(button.hasClass('vjs-icon-photo-camera')).toBeTrue(); - playerWithPrerecorder.one(Event.START_PRERECORDER, () => { + playerWithPrerecorder.one(Event.START_COUNTDOWN, () => { setTimeout(() => { expect(button.controlText_).toEqual('Reset'); }, 2000); @@ -139,7 +139,7 @@ describe('controls.CameraButton', () => { expect(button.hasClass('vjs-icon-photo-camera')).toBeTrue(); - playerWithPrerecorder.one(Event.START_PRERECORDER, () => { + playerWithPrerecorder.one(Event.START_COUNTDOWN, () => { setTimeout(() => { expect(button.controlText_).toEqual('Reset'); }, 500); @@ -151,7 +151,7 @@ describe('controls.CameraButton', () => { done(); }); - playerWithPrerecorder.one(Event.START_PRERECORDER, () => { + playerWithPrerecorder.one(Event.START_COUNTDOWN, () => { setTimeout(() => { // Stop the pre-recording button.trigger('click'); From f4ad22c9b0666dbd3c42dea8653e82f6cf7c50fa Mon Sep 17 00:00:00 2001 From: Denis Mosolov Date: Sun, 24 Oct 2021 12:16:17 +0300 Subject: [PATCH 27/47] Restore image-only sample --- examples/image-only.html | 9 --------- 1 file changed, 9 deletions(-) diff --git a/examples/image-only.html b/examples/image-only.html index c5f7cbda4..0bc995dd0 100644 --- a/examples/image-only.html +++ b/examples/image-only.html @@ -39,15 +39,6 @@ height: 480, plugins: { record: { - // @todo add this example sample to the countdown.html - countdown: [ - {value: 'Five!', time: 1000}, - {value: 'Four!', time: 1000}, - {value: 'Three', time: 1000}, - {value: '2', time: 1000}, - {value: '1', time: 500}, - {value: 'Go!', time: 500}, - ], debug: true, imageOutputType: 'dataURL', imageOutputFormat: 'image/png', From 4bfb0ffccc575b6e3ea49d7c3bc54a11ac97f8c4 Mon Sep 17 00:00:00 2001 From: Denis Mosolov Date: Sun, 24 Oct 2021 12:21:16 +0300 Subject: [PATCH 28/47] Change handles names --- src/js/controls/camera-button.js | 12 ++++++------ src/js/controls/record-toggle.js | 27 ++++++--------------------- 2 files changed, 12 insertions(+), 27 deletions(-) diff --git a/src/js/controls/camera-button.js b/src/js/controls/camera-button.js index 7ada26531..1b1ea7131 100644 --- a/src/js/controls/camera-button.js +++ b/src/js/controls/camera-button.js @@ -33,8 +33,8 @@ class CameraButton extends Button { enable() { super.enable(); - this.on(this.player_, Event.START_COUNTDOWN, this.onStartPrerecorder); - this.on(this.player_, Event.FINISH_COUNTDOWN, this.onFinishPrerecorder); + this.on(this.player_, Event.START_COUNTDOWN, this.onStartCountdown); + this.on(this.player_, Event.FINISH_COUNTDOWN, this.onFinishCountdown); this.on(this.player_, Event.START_RECORD, this.onStart); this.on(this.player_, Event.STOP_RECORD, this.onStop); } @@ -45,8 +45,8 @@ class CameraButton extends Button { disable() { super.disable(); - this.off(this.player_, Event.START_COUNTDOWN, this.onStartPrerecorder); - this.off(this.player_, Event.FINISH_COUNTDOWN, this.onFinishPrerecorder); + this.off(this.player_, Event.START_COUNTDOWN, this.onStartCountdown); + this.off(this.player_, Event.FINISH_COUNTDOWN, this.onFinishCountdown); this.off(this.player_, Event.START_RECORD, this.onStart); this.off(this.player_, Event.STOP_RECORD, this.onStop); } @@ -140,7 +140,7 @@ class CameraButton extends Button { * * @listens Player#startRecord */ - onStartPrerecorder(event) { + onStartCountdown(event) { // change the button text this.controlText('Reset'); } @@ -153,7 +153,7 @@ class CameraButton extends Button { * * @listens Player#startRecord */ - onFinishPrerecorder(event) { + onFinishCountdown(event) { // change the button text this.controlText('Retry'); } diff --git a/src/js/controls/record-toggle.js b/src/js/controls/record-toggle.js index 53ad8ee75..f81ae5fb4 100644 --- a/src/js/controls/record-toggle.js +++ b/src/js/controls/record-toggle.js @@ -35,9 +35,8 @@ class RecordToggle extends Button { this.on(this.player_, Event.START_RECORD, this.onStart); this.on(this.player_, Event.STOP_RECORD, this.onStop); - this.on(this.player_, Event.START_COUNTDOWN, this.onPrerecorderStart); - this.on(this.player_, Event.FINISH_COUNTDOWN, this.onPrerecorderFinish); - this.on(this.player_, Event.FINISH_COUNTDOWN, this.onPrerecorderAbort); + this.on(this.player_, Event.START_COUNTDOWN, this.onCountdownStart); + this.on(this.player_, Event.FINISH_COUNTDOWN, this.onCountdownFinish); } /** @@ -48,9 +47,8 @@ class RecordToggle extends Button { this.off(this.player_, Event.START_RECORD, this.onStart); this.off(this.player_, Event.STOP_RECORD, this.onStop); - this.off(this.player_, Event.START_COUNTDOWN, this.onPrerecorderStart); - this.off(this.player_, Event.FINISH_COUNTDOWN, this.onPrerecorderFinish); - this.off(this.player_, Event.FINISH_COUNTDOWN, this.onPrerecorderAbort); + this.off(this.player_, Event.START_COUNTDOWN, this.onCountdownStart); + this.off(this.player_, Event.FINISH_COUNTDOWN, this.onCountdownFinish); } /** @@ -131,7 +129,7 @@ class RecordToggle extends Button { * @listens Player#prerecorderStart * @todo unit test */ - onPrerecorderStart(event) { + onCountdownStart(event) { this.enable(); } @@ -144,22 +142,9 @@ class RecordToggle extends Button { * @listens Player#prerecorderFinish * @todo unit test */ - onPrerecorderFinish(event) { + onCountdownFinish(event) { this.disable(); } - - /** - * Hide prerecorder overlay - * - * @param {EventTarget~Event} [event] - * The event that caused this function to run. - * - * @listens Player#prerecorderAbort - * @todo unit test - */ - onPrerecorderAbort(event) { - // @todo implement me - } } /** From 2b2dc98ae73442aa4273cd44f771f933bc9a7082 Mon Sep 17 00:00:00 2001 From: Denis Mosolov Date: Sun, 24 Oct 2021 12:27:37 +0300 Subject: [PATCH 29/47] isPreRecording -> isCountingDown --- src/js/controls/camera-button.js | 6 +++--- src/js/controls/record-toggle.js | 4 ++-- src/js/videojs.record.js | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/js/controls/camera-button.js b/src/js/controls/camera-button.js index 1b1ea7131..5c6815132 100644 --- a/src/js/controls/camera-button.js +++ b/src/js/controls/camera-button.js @@ -76,11 +76,11 @@ class CameraButton extends Button { handleClick(event) { let recorder = this.player_.record(); - if (!recorder.isProcessing() && !recorder.isPrerecording()) { + if (!recorder.isProcessing() && !recorder.isCountingDown()) { // create snapshot recorder.start(); } else { - if (recorder.isPrerecording()) { + if (recorder.isCountingDown()) { recorder.abortPrerecording(); } @@ -109,7 +109,7 @@ class CameraButton extends Button { this.addClass('vjs-icon-replay'); let recorder = this.player_.record(); - if (!recorder.isPrerecording()) { + if (!recorder.isCountingDown()) { // change the button text this.controlText('Retry'); } diff --git a/src/js/controls/record-toggle.js b/src/js/controls/record-toggle.js index f81ae5fb4..7db9cd44a 100644 --- a/src/js/controls/record-toggle.js +++ b/src/js/controls/record-toggle.js @@ -75,10 +75,10 @@ class RecordToggle extends Button { */ handleClick(event) { let recorder = this.player_.record(); - if (!recorder.isProcessing() && !recorder.isPrerecording()) { + if (!recorder.isProcessing() && !recorder.isCountingDown()) { recorder.start(); } else { - if (recorder.isPrerecording()) { + if (recorder.isCountingDown()) { recorder.abortPrerecording(); } else { recorder.stop(); diff --git a/src/js/videojs.record.js b/src/js/videojs.record.js index b8da9811f..7cc54663a 100644 --- a/src/js/videojs.record.js +++ b/src/js/videojs.record.js @@ -386,11 +386,11 @@ class Record extends Plugin { } /** - * Indicates whether the plugin is currently prerecording. Recording is not started yet. + * Indicates whether the plugin is currently running the countdown. Recording is not started yet. * - * @return {boolean} Plugin currently prerecording. + * @return {boolean} Plugin currently counting down or not. */ - isPrerecording() { + isCountingDown() { return this._prerecording; } From 81b0c2dbe0bd8014232c9e3ad4c49899a79082e4 Mon Sep 17 00:00:00 2001 From: Denis Mosolov Date: Sun, 24 Oct 2021 12:31:24 +0300 Subject: [PATCH 30/47] _prerecording -> _countingdown --- src/js/videojs.record.js | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/js/videojs.record.js b/src/js/videojs.record.js index 7cc54663a..e0579fa31 100644 --- a/src/js/videojs.record.js +++ b/src/js/videojs.record.js @@ -391,7 +391,7 @@ class Record extends Plugin { * @return {boolean} Plugin currently counting down or not. */ isCountingDown() { - return this._prerecording; + return this._countingdown; } /** @@ -874,8 +874,7 @@ class Record extends Plugin { return; } this._recording = true; - // @todo what if we're in the prerecording state? stop the current prerecording? - this._prerecording = true; + this._countingdown = true; // hide play/pause control if (this.player.controlBar.playToggle !== undefined) { @@ -976,7 +975,7 @@ class Record extends Plugin { return new Promise(resolve => { if (this.countdown.length === 0) { // resolve immediately if there are no countdown steps - this._prerecording = false; + this._countingdown = false; resolve(); } @@ -988,7 +987,7 @@ class Record extends Plugin { let resolveOrDown = () => { if (steps.length === 0) { this.player.clearTimeout(this.prerecorderTimeoutID); - this._prerecording = false; + this._countingdown = false; this.player.countdownOverlay.hide(); this.player.trigger(Event.FINISH_COUNTDOWN); @@ -1074,11 +1073,7 @@ class Record extends Plugin { this.player.clearTimeout(this.prerecorderTimeoutID); this._recording = false; - this._prerecording = false; - // @todo investigate and fix - // when a user clicks on the recording button (during prerecording count down) - // and we have to set up this property to false somewhere - // otherwise recorder thinks that we still processing something and does not allow us to start a new recording + this._countingdown = false; this._processing = false; this.player.countdownOverlay.hide(); @@ -1588,7 +1583,7 @@ class Record extends Plugin { */ resetState() { this._recording = false; - this._prerecording = false; + this._countingdown = false; this._processing = false; this._deviceActive = false; this.devices = []; @@ -1722,7 +1717,7 @@ class Record extends Plugin { */ retrySnapshot() { this._processing = false; - this._prerecording = false; + this._countingdown = false; // retry: hide the snapshot this.player.recordCanvas.hide(); From 87cf537e910803a95bedea3ca1fcaed3da76f4fa Mon Sep 17 00:00:00 2001 From: Denis Mosolov Date: Sun, 24 Oct 2021 12:33:36 +0300 Subject: [PATCH 31/47] showPrerecorder() -> showCountdown() --- src/js/videojs.record.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/js/videojs.record.js b/src/js/videojs.record.js index e0579fa31..455901f5f 100644 --- a/src/js/videojs.record.js +++ b/src/js/videojs.record.js @@ -934,7 +934,7 @@ class Record extends Plugin { switch (this.getRecordType()) { case IMAGE_ONLY: // create snapshot - this.showPrerecorder().then(() => { + this.showCountdown().then(() => { this.createSnapshot(); }); @@ -950,7 +950,7 @@ class Record extends Plugin { // wait for media stream on video element to actually load this.player.one(Event.LOADEDMETADATA, () => { // start actually recording process - this.showPrerecorder().then(() => { + this.showCountdown().then(() => { this.startRecording(); }); }); @@ -959,7 +959,7 @@ class Record extends Plugin { default: // all resources have already loaded, so we can start // recording right away - this.showPrerecorder().then(() => { + this.showCountdown().then(() => { this.startRecording(); }); } @@ -967,11 +967,10 @@ class Record extends Plugin { } /** - * Show the prerecorder overlay and start the countdown + * Show the countdown overlay and start the countdown * @return {Promise} - promise is resolved when the last countdown step is reached - * @todo rename countdown to prerecorder, because the "countDown" term is used in the player */ - showPrerecorder() { + showCountdown() { return new Promise(resolve => { if (this.countdown.length === 0) { // resolve immediately if there are no countdown steps From e17f4ed9223306b926d1d2a90fe5365722b52766 Mon Sep 17 00:00:00 2001 From: Denis Mosolov Date: Sun, 24 Oct 2021 12:35:27 +0300 Subject: [PATCH 32/47] abortPrerecording() -> abortCountdown() --- src/js/controls/camera-button.js | 2 +- src/js/controls/record-toggle.js | 2 +- src/js/videojs.record.js | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/js/controls/camera-button.js b/src/js/controls/camera-button.js index 5c6815132..af587c93e 100644 --- a/src/js/controls/camera-button.js +++ b/src/js/controls/camera-button.js @@ -81,7 +81,7 @@ class CameraButton extends Button { recorder.start(); } else { if (recorder.isCountingDown()) { - recorder.abortPrerecording(); + recorder.abortCountdown(); } // retry diff --git a/src/js/controls/record-toggle.js b/src/js/controls/record-toggle.js index 7db9cd44a..b8739f7e2 100644 --- a/src/js/controls/record-toggle.js +++ b/src/js/controls/record-toggle.js @@ -79,7 +79,7 @@ class RecordToggle extends Button { recorder.start(); } else { if (recorder.isCountingDown()) { - recorder.abortPrerecording(); + recorder.abortCountdown(); } else { recorder.stop(); } diff --git a/src/js/videojs.record.js b/src/js/videojs.record.js index 455901f5f..60bb20a42 100644 --- a/src/js/videojs.record.js +++ b/src/js/videojs.record.js @@ -1065,10 +1065,10 @@ class Record extends Plugin { } /** - * Abort prerecording countdown + * Abort the countdown */ - abortPrerecording() { - // Stop prerecorder steps + abortCountdown() { + // Stop the countdown this.player.clearTimeout(this.prerecorderTimeoutID); this._recording = false; From 4fb848e4e3ac105db5c87b87fa38b87998e530ac Mon Sep 17 00:00:00 2001 From: Denis Mosolov Date: Sun, 24 Oct 2021 12:38:19 +0300 Subject: [PATCH 33/47] prerecorderTimeoutID -> countdownTimeoutID --- src/js/videojs.record.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/js/videojs.record.js b/src/js/videojs.record.js index 60bb20a42..d127b250e 100644 --- a/src/js/videojs.record.js +++ b/src/js/videojs.record.js @@ -985,7 +985,7 @@ class Record extends Plugin { let steps = [...this.countdown]; let resolveOrDown = () => { if (steps.length === 0) { - this.player.clearTimeout(this.prerecorderTimeoutID); + this.player.clearTimeout(this.countdownTimeoutID); this._countingdown = false; this.player.countdownOverlay.hide(); this.player.trigger(Event.FINISH_COUNTDOWN); @@ -996,7 +996,7 @@ class Record extends Plugin { ({value, time} = steps.shift()); // @todo trigger an event and pass current step as an event data this.player.countdownOverlay.setCountdownValue(value); - this.prerecorderTimeoutID = this.player.setTimeout(resolveOrDown, time); + this.countdownTimeoutID = this.player.setTimeout(resolveOrDown, time); } }; @@ -1069,7 +1069,7 @@ class Record extends Plugin { */ abortCountdown() { // Stop the countdown - this.player.clearTimeout(this.prerecorderTimeoutID); + this.player.clearTimeout(this.countdownTimeoutID); this._recording = false; this._countingdown = false; From 7cbad0c1f699954992f190c1c9608a37fab98764 Mon Sep 17 00:00:00 2001 From: Denis Mosolov Date: Sun, 24 Oct 2021 12:41:36 +0300 Subject: [PATCH 34/47] Rename pre-recorder to countdown in tests --- test/controls/camera-button.spec.js | 38 ++++++++++++------------- test/controls/countdown-overlay.spec.js | 2 +- test/controls/record-toggle.spec.js | 4 +-- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/test/controls/camera-button.spec.js b/test/controls/camera-button.spec.js index 614fbf823..f0a6b74bf 100644 --- a/test/controls/camera-button.spec.js +++ b/test/controls/camera-button.spec.js @@ -11,7 +11,7 @@ import CameraButton from '../../src/js/controls/camera-button'; /** @test {camera-button} */ describe('controls.CameraButton', () => { let player; - let playerWithPrerecorder; + let playerWithCountdown; let originalTimeout; beforeEach(() => { @@ -21,8 +21,8 @@ describe('controls.CameraButton', () => { // create new image-only player player = TestHelpers.makeImageOnlyPlayer(); - // create new image-only player with a pre-recorder - playerWithPrerecorder = TestHelpers.makeImageOnlyPlayer({ + // create new image-only player with the countdown + playerWithCountdown = TestHelpers.makeImageOnlyPlayer({ plugins: { record: { image: true, @@ -39,7 +39,7 @@ describe('controls.CameraButton', () => { afterEach(() => { player.dispose(); - playerWithPrerecorder.dispose(); + playerWithCountdown.dispose(); jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout; }); @@ -100,17 +100,17 @@ describe('controls.CameraButton', () => { }); }); - it('changes appearance when startRecord or stopRecord is triggered and pre-recorder is on', (done) => { - let button = new CameraButton(playerWithPrerecorder); + it('changes appearance when startRecord or stopRecord is triggered and countdown is on', (done) => { + let button = new CameraButton(playerWithCountdown); expect(button.hasClass('vjs-icon-photo-camera')).toBeTrue(); - playerWithPrerecorder.one(Event.START_COUNTDOWN, () => { + playerWithCountdown.one(Event.START_COUNTDOWN, () => { setTimeout(() => { expect(button.controlText_).toEqual('Reset'); }, 2000); }); - playerWithPrerecorder.one(Event.START_RECORD, () => { + playerWithCountdown.one(Event.START_RECORD, () => { expect(button.hasClass('vjs-icon-photo-camera')).toBeFalse(); expect(button.hasClass('vjs-icon-replay')).toBeTrue(); expect(button.controlText_).toEqual('Reset'); @@ -125,44 +125,44 @@ describe('controls.CameraButton', () => { }, 4000); }); - playerWithPrerecorder.one(Event.DEVICE_READY, () => { + playerWithCountdown.one(Event.DEVICE_READY, () => { button.trigger('click'); }); - playerWithPrerecorder.one(Event.READY, () => { - playerWithPrerecorder.record().getDevice(); + playerWithCountdown.one(Event.READY, () => { + playerWithCountdown.record().getDevice(); }); }); - it('changes appearance when stopRecord is triggered and pre-recorder is on', (done) => { - let button = new CameraButton(playerWithPrerecorder); + it('changes appearance when stopRecord is triggered and the countdown is on', (done) => { + let button = new CameraButton(playerWithCountdown); expect(button.hasClass('vjs-icon-photo-camera')).toBeTrue(); - playerWithPrerecorder.one(Event.START_COUNTDOWN, () => { + playerWithCountdown.one(Event.START_COUNTDOWN, () => { setTimeout(() => { expect(button.controlText_).toEqual('Reset'); }, 500); }); - playerWithPrerecorder.one(Event.RETRY, () => { + playerWithCountdown.one(Event.RETRY, () => { expect(button.hasClass('vjs-icon-replay')).toBeFalse(); expect(button.hasClass('vjs-icon-photo-camera')).toBeTrue(); expect(button.controlText_).toEqual('Image'); done(); }); - playerWithPrerecorder.one(Event.START_COUNTDOWN, () => { + playerWithCountdown.one(Event.START_COUNTDOWN, () => { setTimeout(() => { // Stop the pre-recording button.trigger('click'); }, 1000); }); - playerWithPrerecorder.one(Event.DEVICE_READY, () => { + playerWithCountdown.one(Event.DEVICE_READY, () => { button.trigger('click'); }); - playerWithPrerecorder.one(Event.READY, () => { - playerWithPrerecorder.record().getDevice(); + playerWithCountdown.one(Event.READY, () => { + playerWithCountdown.record().getDevice(); }); }); }); \ No newline at end of file diff --git a/test/controls/countdown-overlay.spec.js b/test/controls/countdown-overlay.spec.js index 9805dd6ad..483d2bdb1 100644 --- a/test/controls/countdown-overlay.spec.js +++ b/test/controls/countdown-overlay.spec.js @@ -39,7 +39,7 @@ describe('controls.CountdownOverlay', () => { expect(countdown.el().innerHTML).toEqual(''); }); - it('pre-recorder overlay appears', () => { + it('countdown overlay appears', () => { let countdown = new CountdownOverlay(player); player.one(Event.DEVICE_READY, () => { diff --git a/test/controls/record-toggle.spec.js b/test/controls/record-toggle.spec.js index 60f7fb1ba..57986e488 100644 --- a/test/controls/record-toggle.spec.js +++ b/test/controls/record-toggle.spec.js @@ -85,8 +85,8 @@ describe('controls.RecordToggle', () => { }); }); - it('accept interaction: pre-recorder', (done) => { - // create an instance of a player with the pre-recorder + it('accept interaction: countdown', (done) => { + // create an instance of a player with the countdown player.dispose(); player = TestHelpers.makeAudioVideoPlayer({ plugins: { From 00f07850a970216fbb546e5678e319e5cb76bd9c Mon Sep 17 00:00:00 2001 From: Denis Mosolov Date: Sun, 24 Oct 2021 12:43:00 +0300 Subject: [PATCH 35/47] Rename pre-recorder to countdown in inline docs --- src/js/controls/record-toggle.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/js/controls/record-toggle.js b/src/js/controls/record-toggle.js index b8739f7e2..339af97b0 100644 --- a/src/js/controls/record-toggle.js +++ b/src/js/controls/record-toggle.js @@ -121,12 +121,12 @@ class RecordToggle extends Button { } /** - * Show prerecorder overlay + * Show countdown overlay * * @param {EventTarget~Event} [event] * The event that caused this function to run. * - * @listens Player#prerecorderStart + * @listens Player#countdownStart * @todo unit test */ onCountdownStart(event) { @@ -134,12 +134,12 @@ class RecordToggle extends Button { } /** - * Hide prerecorder overlay + * Hide countdown overlay * * @param {EventTarget~Event} [event] * The event that caused this function to run. * - * @listens Player#prerecorderFinish + * @listens Player#countdownFinish * @todo unit test */ onCountdownFinish(event) { From 4e265a8809427cd54e1e718ea7f04a53cac27814 Mon Sep 17 00:00:00 2001 From: Denis Mosolov Date: Sun, 24 Oct 2021 12:47:07 +0300 Subject: [PATCH 36/47] Update test specs --- test/controls/camera-button.spec.js | 2 +- test/controls/countdown-overlay.spec.js | 5 ++--- test/controls/record-toggle.spec.js | 4 ++-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/test/controls/camera-button.spec.js b/test/controls/camera-button.spec.js index f0a6b74bf..89ee69608 100644 --- a/test/controls/camera-button.spec.js +++ b/test/controls/camera-button.spec.js @@ -34,7 +34,7 @@ describe('controls.CameraButton', () => { debug: true } } - }, 'imageOnlyPrerecorder'); + }, 'imageOnlyCountdown'); }); afterEach(() => { diff --git a/test/controls/countdown-overlay.spec.js b/test/controls/countdown-overlay.spec.js index 483d2bdb1..1632bdf6f 100644 --- a/test/controls/countdown-overlay.spec.js +++ b/test/controls/countdown-overlay.spec.js @@ -127,7 +127,7 @@ describe('controls.CountdownOverlay', () => { }); }); - it('prerecording during the countdown', (done) => { + it('recording is not started during the countdown', (done) => { let toggle = new RecordToggle(player); player.one(Event.DEVICE_READY, () => { @@ -135,8 +135,7 @@ describe('controls.CountdownOverlay', () => { toggle.trigger('click'); setTimeout(() => { - // recording is not started during the countdown - expect(player.record().isPrerecording()).toBeTrue(); + expect(player.record().isCountingDown()).toBeTrue(); }, 1000); setTimeout(() => { diff --git a/test/controls/record-toggle.spec.js b/test/controls/record-toggle.spec.js index 57986e488..130c6632f 100644 --- a/test/controls/record-toggle.spec.js +++ b/test/controls/record-toggle.spec.js @@ -104,13 +104,13 @@ describe('controls.RecordToggle', () => { player.one(Event.DEVICE_READY, () => { // start toggle.trigger('click'); - expect(player.record().isPrerecording()).toBeTrue(); + expect(player.record().isCountingDown()).toBeTrue(); expect(player.record().isRecording()).toBeTrue(); setTimeout(() => { // stop toggle.trigger('click'); - expect(player.record().isPrerecording()).toBeFalse(); + expect(player.record().isCountingDown()).toBeFalse(); expect(player.record().isRecording()).toBeFalse(); done(); From eec7c44718f390f4d0defeab0a1c7cc4e79a0507 Mon Sep 17 00:00:00 2001 From: Denis Mosolov Date: Sun, 24 Oct 2021 13:50:55 +0300 Subject: [PATCH 37/47] Tweak tests --- src/js/controls/record-toggle.js | 30 ------ test/controls/countdown-overlay.spec.js | 92 ------------------ test/videojs.record.spec.js | 118 ++++++++++++++++++++++++ 3 files changed, 118 insertions(+), 122 deletions(-) diff --git a/src/js/controls/record-toggle.js b/src/js/controls/record-toggle.js index 339af97b0..87b198a7c 100644 --- a/src/js/controls/record-toggle.js +++ b/src/js/controls/record-toggle.js @@ -35,8 +35,6 @@ class RecordToggle extends Button { this.on(this.player_, Event.START_RECORD, this.onStart); this.on(this.player_, Event.STOP_RECORD, this.onStop); - this.on(this.player_, Event.START_COUNTDOWN, this.onCountdownStart); - this.on(this.player_, Event.FINISH_COUNTDOWN, this.onCountdownFinish); } /** @@ -47,8 +45,6 @@ class RecordToggle extends Button { this.off(this.player_, Event.START_RECORD, this.onStart); this.off(this.player_, Event.STOP_RECORD, this.onStop); - this.off(this.player_, Event.START_COUNTDOWN, this.onCountdownStart); - this.off(this.player_, Event.FINISH_COUNTDOWN, this.onCountdownFinish); } /** @@ -119,32 +115,6 @@ class RecordToggle extends Button { // change the button text this.controlText('Record'); } - - /** - * Show countdown overlay - * - * @param {EventTarget~Event} [event] - * The event that caused this function to run. - * - * @listens Player#countdownStart - * @todo unit test - */ - onCountdownStart(event) { - this.enable(); - } - - /** - * Hide countdown overlay - * - * @param {EventTarget~Event} [event] - * The event that caused this function to run. - * - * @listens Player#countdownFinish - * @todo unit test - */ - onCountdownFinish(event) { - this.disable(); - } } /** diff --git a/test/controls/countdown-overlay.spec.js b/test/controls/countdown-overlay.spec.js index 1632bdf6f..3f83740b2 100644 --- a/test/controls/countdown-overlay.spec.js +++ b/test/controls/countdown-overlay.spec.js @@ -5,7 +5,6 @@ import TestHelpers from '../test-helpers'; import CountdownOverlay from '../../src/js/controls/countdown-overlay'; -import RecordToggle from '../../src/js/controls/record-toggle'; import Event from '../../src/js/event'; @@ -63,95 +62,4 @@ describe('controls.CountdownOverlay', () => { player.record().getDevice(); }); }); - - // @todo-coachup move to videojs.record.spec.ts - it('start record after the countdown', (done) => { - let toggle = new RecordToggle(player); - - player.one(Event.DEVICE_READY, () => { - // start - toggle.trigger('click'); - - setTimeout(() => { - // recording is started after the countdown - expect(player.record().isRecording()).toBeTrue(); - }, 3000); - - setTimeout(() => { - // stop recording - player.record().stop(); - }, 4000); - }); - - player.one(Event.FINISH_RECORD, () => { - // wait till it's loaded before destroying - // (XXX: create new event for this) - setTimeout(done, 1000); - }); - - player.one(Event.READY, () => { - player.record().getDevice(); - }); - }); - - it('no record during the countdown', (done) => { - let toggle = new RecordToggle(player); - - player.one(Event.DEVICE_READY, () => { - const onRecordStarted = () => { - expect(true).toBe(false); - }; - player.one(Event.START_RECORD, onRecordStarted); - setTimeout(() => { - // recording is not started during the countdown - player.off(Event.START_RECORD, onRecordStarted); - }, 2000); - - // start - toggle.trigger('click'); - - setTimeout(() => { - // stop recording - player.record().stop(); - }, 4000); - }); - - player.one(Event.FINISH_RECORD, () => { - // wait till it's loaded before destroying - // (XXX: create new event for this) - setTimeout(done, 1000); - }); - - player.one(Event.READY, () => { - player.record().getDevice(); - }); - }); - - it('recording is not started during the countdown', (done) => { - let toggle = new RecordToggle(player); - - player.one(Event.DEVICE_READY, () => { - // start - toggle.trigger('click'); - - setTimeout(() => { - expect(player.record().isCountingDown()).toBeTrue(); - }, 1000); - - setTimeout(() => { - // stop recording - player.record().stop(); - }, 4000); - }); - - player.one(Event.FINISH_RECORD, () => { - // wait till it's loaded before destroying - // (XXX: create new event for this) - setTimeout(done, 1000); - }); - - player.one(Event.READY, () => { - player.record().getDevice(); - }); - }); }); \ No newline at end of file diff --git a/test/videojs.record.spec.js b/test/videojs.record.spec.js index 5e73d8c15..7bf3426a6 100644 --- a/test/videojs.record.spec.js +++ b/test/videojs.record.spec.js @@ -9,6 +9,7 @@ import {isFirefox, detectBrowser} from '../src/js/utils/detect-browser'; // registers the plugin import Record from '../src/js/videojs.record'; +import RecordToggle from "../src/js/controls/record-toggle"; jasmine.DEFAULT_TIMEOUT_INTERVAL = 30000; @@ -829,4 +830,121 @@ describe('Record', () => { }); }); + /** @test {Record} */ + it('no real recording during the countdown', (done) => { + player = TestHelpers.makeAudioVideoPlayer({ + plugins: { + record: { + countdown: [ + {value: '2', time: 1000}, + {value: '1', time: 1000}, + ], + } + } + }); + + player.one(Event.DEVICE_READY, () => { + const onRecordStarted = () => { + expect(true).toBe(false); + }; + player.one(Event.START_RECORD, onRecordStarted); + setTimeout(() => { + // recording is not started during the countdown + player.off(Event.START_RECORD, onRecordStarted); + }, 2000); + + // start record + player.record().start(); + + setTimeout(() => { + // stop recording + player.record().stop(); + }, 4000); + }); + + player.one(Event.FINISH_RECORD, () => { + // wait till it's loaded before destroying + // (XXX: create new event for this) + setTimeout(done, 1000); + }); + + player.one(Event.READY, () => { + player.record().getDevice(); + }); + }); + + /** @test {Record#isCountingDown} */ + it('counting down is started', (done) => { + player = TestHelpers.makeAudioVideoPlayer({ + plugins: { + record: { + countdown: [ + {value: '2', time: 1000}, + {value: '1', time: 1000}, + ], + } + } + }); + + player.one(Event.DEVICE_READY, () => { + // start record + player.record().start(); + + setTimeout(() => { + expect(player.record().isCountingDown()).toBeTrue(); + }, 1000); + + setTimeout(() => { + // stop recording + player.record().stop(); + }, 4000); + }); + + player.one(Event.FINISH_RECORD, () => { + // wait till it's loaded before destroying + // (XXX: create new event for this) + setTimeout(done, 1000); + }); + + player.one(Event.READY, () => { + player.record().getDevice(); + }); + }); + + /** @test {Record#isCountingDown} */ + it('counting down is finished', (done) => { + player = TestHelpers.makeAudioVideoPlayer({ + plugins: { + record: { + countdown: [ + {value: '2', time: 1000}, + {value: '1', time: 1000}, + ], + } + } + }); + + player.one(Event.DEVICE_READY, () => { + player.record().start(); + + setTimeout(() => { + expect(player.record().isCountingDown()).toBeFalse(); + }, 3000); + + setTimeout(() => { + // stop recording + player.record().stop(); + }, 4000); + }); + + player.one(Event.FINISH_RECORD, () => { + // wait till it's loaded before destroying + // (XXX: create new event for this) + setTimeout(done, 1000); + }); + + player.one(Event.READY, () => { + player.record().getDevice(); + }); + }); }); From 8240283cca568ddf32e408020bc1f72181d84601 Mon Sep 17 00:00:00 2001 From: Denis Mosolov Date: Sun, 24 Oct 2021 14:11:20 +0300 Subject: [PATCH 38/47] Abort countdown on stop and tweak tests --- src/js/videojs.record.js | 4 ++++ test/controls/record-toggle.spec.js | 37 ----------------------------- test/videojs.record.spec.js | 23 ++++++++---------- 3 files changed, 14 insertions(+), 50 deletions(-) diff --git a/src/js/videojs.record.js b/src/js/videojs.record.js index d127b250e..b434741cf 100644 --- a/src/js/videojs.record.js +++ b/src/js/videojs.record.js @@ -1039,6 +1039,10 @@ class Record extends Plugin { this._recording = false; this._processing = true; + if (this.isCountingDown()) { + this.abortCountdown(); + } + if (this.getRecordType() !== IMAGE_ONLY) { // notify UI this.player.trigger(Event.STOP_RECORD); diff --git a/test/controls/record-toggle.spec.js b/test/controls/record-toggle.spec.js index 130c6632f..b44fcc42c 100644 --- a/test/controls/record-toggle.spec.js +++ b/test/controls/record-toggle.spec.js @@ -84,41 +84,4 @@ describe('controls.RecordToggle', () => { player.record().getDevice(); }); }); - - it('accept interaction: countdown', (done) => { - // create an instance of a player with the countdown - player.dispose(); - player = TestHelpers.makeAudioVideoPlayer({ - plugins: { - record: { - countdown: [ - {value: '2', time: 1000}, - {value: '1', time: 1000}, - ] - } - } - }); - - let toggle = new RecordToggle(player); - - player.one(Event.DEVICE_READY, () => { - // start - toggle.trigger('click'); - expect(player.record().isCountingDown()).toBeTrue(); - expect(player.record().isRecording()).toBeTrue(); - - setTimeout(() => { - // stop - toggle.trigger('click'); - expect(player.record().isCountingDown()).toBeFalse(); - expect(player.record().isRecording()).toBeFalse(); - - done(); - }, 1000); - }); - - player.one(Event.READY, () => { - player.record().getDevice(); - }); - }); }); \ No newline at end of file diff --git a/test/videojs.record.spec.js b/test/videojs.record.spec.js index 7bf3426a6..a83a92796 100644 --- a/test/videojs.record.spec.js +++ b/test/videojs.record.spec.js @@ -874,7 +874,8 @@ describe('Record', () => { }); /** @test {Record#isCountingDown} */ - it('counting down is started', (done) => { + /** @test {Record#isRecording} */ + it('start/stop the countdown', (done) => { player = TestHelpers.makeAudioVideoPlayer({ plugins: { record: { @@ -887,23 +888,19 @@ describe('Record', () => { }); player.one(Event.DEVICE_READY, () => { - // start record + // start player.record().start(); + expect(player.record().isCountingDown()).toBeTrue(); + expect(player.record().isRecording()).toBeTrue(); setTimeout(() => { - expect(player.record().isCountingDown()).toBeTrue(); - }, 1000); - - setTimeout(() => { - // stop recording + // stop player.record().stop(); - }, 4000); - }); + expect(player.record().isCountingDown()).toBeFalse(); + expect(player.record().isRecording()).toBeFalse(); - player.one(Event.FINISH_RECORD, () => { - // wait till it's loaded before destroying - // (XXX: create new event for this) - setTimeout(done, 1000); + done(); + }, 1000); }); player.one(Event.READY, () => { From b1c4c1bb67a0ad1eff2c277c4b381e5f6c8b492e Mon Sep 17 00:00:00 2001 From: Denis Mosolov Date: Sun, 24 Oct 2021 14:57:39 +0300 Subject: [PATCH 39/47] Fix record toggle --- src/js/controls/record-toggle.js | 9 ++-- test/controls/record-toggle.spec.js | 82 +++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 6 deletions(-) diff --git a/src/js/controls/record-toggle.js b/src/js/controls/record-toggle.js index 87b198a7c..ec3808a8d 100644 --- a/src/js/controls/record-toggle.js +++ b/src/js/controls/record-toggle.js @@ -70,15 +70,12 @@ class RecordToggle extends Button { * @listens click */ handleClick(event) { + debugger; let recorder = this.player_.record(); - if (!recorder.isProcessing() && !recorder.isCountingDown()) { + if (!recorder.isRecording()) { recorder.start(); } else { - if (recorder.isCountingDown()) { - recorder.abortCountdown(); - } else { - recorder.stop(); - } + recorder.stop(); } } diff --git a/test/controls/record-toggle.spec.js b/test/controls/record-toggle.spec.js index b44fcc42c..c54abfc64 100644 --- a/test/controls/record-toggle.spec.js +++ b/test/controls/record-toggle.spec.js @@ -84,4 +84,86 @@ describe('controls.RecordToggle', () => { player.record().getDevice(); }); }); + + it('start then abort the countdown', (done) => { + // create an instance of a player with the countdown + player.dispose(); + player = TestHelpers.makeAudioVideoPlayer({ + plugins: { + record: { + countdown: [ + {value: '2', time: 1000}, + {value: '1', time: 1000}, + ] + } + } + }); + + let toggle = new RecordToggle(player); + + player.one(Event.DEVICE_READY, () => { + // start countdown + toggle.trigger('click'); + expect(player.record().isCountingDown()).toBeTrue(); + + setTimeout(() => { + // abort countdown + toggle.trigger('click'); + expect(player.record().isCountingDown()).toBeFalse(); + }, 1000); + + setTimeout(() => { + // no recording after aborting + expect(player.record().isCountingDown()).toBeFalse(); + expect(player.record().isRecording()).toBeFalse(); + setTimeout(done, 1000); + }, 3000); + }); + + player.one(Event.READY, () => { + player.record().getDevice(); + }); + }); + + it('stop the recording', (done) => { + // create an instance of a player with the countdown + player.dispose(); + player = TestHelpers.makeAudioVideoPlayer({ + plugins: { + record: { + countdown: [ + {value: '2', time: 1000}, + {value: '1', time: 1000}, + ] + } + } + }); + + let toggle = new RecordToggle(player); + + player.one(Event.DEVICE_READY, () => { + // start countdown + toggle.trigger('click'); + expect(player.record().isRecording()).toBeTrue(); + + setTimeout(() => { + // recording (countdown finished) + expect(player.record().isCountingDown()).toBeFalse(); + expect(player.record().isRecording()).toBeTrue(); + }, 3000); + + setTimeout(() => { + // stop recording + toggle.trigger('click'); + expect(player.record().isCountingDown()).toBeFalse(); + expect(player.record().isRecording()).toBeFalse(); + + setTimeout(done, 1000); + }, 4000); + }); + + player.one(Event.READY, () => { + player.record().getDevice(); + }); + }); }); \ No newline at end of file From d206c8f42fa348c2d34bc81d88e5cd19bb8dd03a Mon Sep 17 00:00:00 2001 From: Denis Mosolov Date: Sun, 31 Oct 2021 20:37:45 +0300 Subject: [PATCH 40/47] Rename CountdownOverlay methods --- examples/video-only.html | 8 ++++++++ src/js/controls/countdown-overlay.js | 6 +++++- src/js/controls/record-toggle.js | 1 - src/js/videojs.record.js | 9 ++++----- 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/examples/video-only.html b/examples/video-only.html index e676abca4..e918e266e 100644 --- a/examples/video-only.html +++ b/examples/video-only.html @@ -44,6 +44,14 @@ video: true, maxLength: 10, displayMilliseconds: false, + countdown: [ + {value: 'Five!', time: 1000}, + {value: 'Four!', time: 1000}, + {value: 'Three', time: 1000}, + {value: '2', time: 1000}, + {value: '1', time: 500}, + {value: 'Go!', time: 500}, + ], debug: true } } diff --git a/src/js/controls/countdown-overlay.js b/src/js/controls/countdown-overlay.js index 34749ffa4..4f4721ef7 100644 --- a/src/js/controls/countdown-overlay.js +++ b/src/js/controls/countdown-overlay.js @@ -56,7 +56,11 @@ class CountdownOverlay extends Component { super.show(); } - setCountdownValue(value) { + resetOverlayText() { + this.setOverlayText(''); + } + + setOverlayText(value) { if (this.el().firstChild) { this.el().firstChild.innerText = value; } else { diff --git a/src/js/controls/record-toggle.js b/src/js/controls/record-toggle.js index ec3808a8d..d5759d481 100644 --- a/src/js/controls/record-toggle.js +++ b/src/js/controls/record-toggle.js @@ -70,7 +70,6 @@ class RecordToggle extends Button { * @listens click */ handleClick(event) { - debugger; let recorder = this.player_.record(); if (!recorder.isRecording()) { recorder.start(); diff --git a/src/js/videojs.record.js b/src/js/videojs.record.js index b434741cf..c31f28f7a 100644 --- a/src/js/videojs.record.js +++ b/src/js/videojs.record.js @@ -979,7 +979,7 @@ class Record extends Plugin { } this.player.trigger(Event.START_COUNTDOWN); - this.player.countdownOverlay.setCountdownValue(''); + this.player.countdownOverlay.resetOverlayText(); this.player.countdownOverlay.show(); let steps = [...this.countdown]; @@ -992,10 +992,9 @@ class Record extends Plugin { resolve(); } else { - let value, time; - ({value, time} = steps.shift()); - // @todo trigger an event and pass current step as an event data - this.player.countdownOverlay.setCountdownValue(value); + let text, time; + ({text, time} = steps.shift()); + this.player.countdownOverlay.setOverlayText(text); this.countdownTimeoutID = this.player.setTimeout(resolveOrDown, time); } }; From fd1922a67908a83c88f613ab68f317a92ec6ad5d Mon Sep 17 00:00:00 2001 From: Denis Mosolov Date: Sun, 31 Oct 2021 20:45:24 +0300 Subject: [PATCH 41/47] Tweak docs --- docs/options.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/options.md b/docs/options.md index ee569a8b2..2ae4d61dd 100644 --- a/docs/options.md +++ b/docs/options.md @@ -54,5 +54,5 @@ Additional options for this plugin are: | `convertOptions` | array | `[]` | List of string options to pass to the convert engine. | | `convertAuto` | boolean | `true` | By default the converter automatically starts once recording completed. Use `false` to disable this behavior, allowing you to start the converter manually instead. | | `hotKeys` | boolean or function | `false` | Enable [keyboard hotkeys](hotkeys.md). Disabled by default. | -| `countdown` | array | `[]` | List of steps to display as a countdown after clicking on the record button (before the recording starts). Each step has a value (string) to display and time interval (in milliseconds). For example `[{time: 500, value: '3'}, {time: 500, value: '2'}, {time: 500, value: '1'}]` will display "3" for 0.5 sec, then "2" for 0.5 sec, then "1" for 0.5 sec and after that start the recording. | +| `countdown` | array | `[]` | List of steps to display as a countdown after clicking on the record button (before the recording starts). Each step has a value (string) to display and time interval (in milliseconds). For example `[{time: 500, value: 'Three'}, {time: 500, value: 'Two'}, {time: 500, value: 'One'}]` will display "Three" for 0.5 sec, then "Two" for 0.5 sec, then "One" for 0.5 sec and after that start the recording. | | `pluginLibraryOptions` | object | `{}` | Use this object to specify additional settings for the library used by the plugin. Currently only used for the ffmpeg.wasm, ffmpeg.js, opus-recorder and vmsg plugins. | From daba1fe47e2931b19da1d760d092681915387834 Mon Sep 17 00:00:00 2001 From: Denis Mosolov Date: Sun, 31 Oct 2021 20:45:41 +0300 Subject: [PATCH 42/47] Tweak inline comment --- test/controls/camera-button.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/controls/camera-button.spec.js b/test/controls/camera-button.spec.js index 89ee69608..f536d4933 100644 --- a/test/controls/camera-button.spec.js +++ b/test/controls/camera-button.spec.js @@ -153,7 +153,7 @@ describe('controls.CameraButton', () => { }); playerWithCountdown.one(Event.START_COUNTDOWN, () => { setTimeout(() => { - // Stop the pre-recording + // Stop the countdown button.trigger('click'); }, 1000); }); From a09912bd63f6502d75d38238391a1a9cdee171bb Mon Sep 17 00:00:00 2001 From: Denis Mosolov Date: Sun, 7 Apr 2024 18:35:50 +0400 Subject: [PATCH 43/47] Bump version --- src/js/controls/countdown-overlay.js | 2 +- src/js/utils/validate-countdown-steps.js | 2 +- test/controls/countdown-overlay.spec.js | 2 +- test/utils/validate-countdown-steps.spec.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/js/controls/countdown-overlay.js b/src/js/controls/countdown-overlay.js index 4f4721ef7..2eff99432 100644 --- a/src/js/controls/countdown-overlay.js +++ b/src/js/controls/countdown-overlay.js @@ -1,6 +1,6 @@ /** * @file countdown-overlay.js - * @since 4.6.0 + * @since 4.9.0 */ import videojs from 'video.js'; diff --git a/src/js/utils/validate-countdown-steps.js b/src/js/utils/validate-countdown-steps.js index 7a3ff4165..084dcfbe5 100644 --- a/src/js/utils/validate-countdown-steps.js +++ b/src/js/utils/validate-countdown-steps.js @@ -1,6 +1,6 @@ /** * @file validate-countdown-steps.js - * @since 4.6.0 + * @since 4.9.0 */ /** diff --git a/test/controls/countdown-overlay.spec.js b/test/controls/countdown-overlay.spec.js index 3f83740b2..2ac497067 100644 --- a/test/controls/countdown-overlay.spec.js +++ b/test/controls/countdown-overlay.spec.js @@ -1,5 +1,5 @@ /** - * @since 4.6.0 + * @since 4.9.0 */ import TestHelpers from '../test-helpers'; diff --git a/test/utils/validate-countdown-steps.spec.js b/test/utils/validate-countdown-steps.spec.js index 3ae0767f1..543d26650 100644 --- a/test/utils/validate-countdown-steps.spec.js +++ b/test/utils/validate-countdown-steps.spec.js @@ -1,5 +1,5 @@ /** - * @since 4.6.0 + * @since 4.9.0 */ import validateCountdownSteps from '../../src/js/utils/validate-countdown-steps'; From 12221c213be2476c87868e6bf7980fd5d0b0fc68 Mon Sep 17 00:00:00 2001 From: Denis Mosolov Date: Sun, 7 Apr 2024 18:40:46 +0400 Subject: [PATCH 44/47] Dead code --- src/js/controls/countdown-overlay.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/js/controls/countdown-overlay.js b/src/js/controls/countdown-overlay.js index 2eff99432..6484792db 100644 --- a/src/js/controls/countdown-overlay.js +++ b/src/js/controls/countdown-overlay.js @@ -4,7 +4,6 @@ */ import videojs from 'video.js'; -import Event from '../event'; const Component = videojs.getComponent('Component'); From 25dcafb972eb161729f82fa621cafab5c1178a20 Mon Sep 17 00:00:00 2001 From: Denis Mosolov Date: Sun, 7 Apr 2024 19:41:06 +0400 Subject: [PATCH 45/47] Fix object destructuring --- src/js/videojs.record.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/js/videojs.record.js b/src/js/videojs.record.js index 47358db1e..feffd3c2f 100644 --- a/src/js/videojs.record.js +++ b/src/js/videojs.record.js @@ -999,8 +999,7 @@ class Record extends Plugin { resolve(); } else { - let text, time; - ({text, time} = steps.shift()); + const {value: text, time: time} = steps.shift(); this.player.countdownOverlay.setOverlayText(text); this.countdownTimeoutID = this.player.setTimeout(resolveOrDown, time); } From dcf85494c6320489633775f834da8d9958668101 Mon Sep 17 00:00:00 2001 From: Denis Mosolov Date: Sun, 7 Apr 2024 19:41:16 +0400 Subject: [PATCH 46/47] CSS tweaks --- src/css/components/countdown-overlay.scss | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/css/components/countdown-overlay.scss b/src/css/components/countdown-overlay.scss index f0f5f9cf4..35d3e35b4 100644 --- a/src/css/components/countdown-overlay.scss +++ b/src/css/components/countdown-overlay.scss @@ -13,8 +13,15 @@ display: flex; justify-content: center; align-items: center; + filter: grayscale(1); span { - font-size: 6em; + font-size: 12em; } -} \ No newline at end of file +} +.vjs-countdown { + + video { + filter: grayscale(1); + } +} From ab1c7f43093444151ca83787b9fa9cdd65846f98 Mon Sep 17 00:00:00 2001 From: Denis Mosolov Date: Mon, 22 Apr 2024 17:25:23 +0400 Subject: [PATCH 47/47] Do not add countdown to the default video demo --- examples/video-only.html | 8 -------- 1 file changed, 8 deletions(-) diff --git a/examples/video-only.html b/examples/video-only.html index e918e266e..e676abca4 100644 --- a/examples/video-only.html +++ b/examples/video-only.html @@ -44,14 +44,6 @@ video: true, maxLength: 10, displayMilliseconds: false, - countdown: [ - {value: 'Five!', time: 1000}, - {value: 'Four!', time: 1000}, - {value: 'Three', time: 1000}, - {value: '2', time: 1000}, - {value: '1', time: 500}, - {value: 'Go!', time: 500}, - ], debug: true } }