diff --git a/.eslintrc b/.eslintrc index 3990ce906197..9f774f708d97 100644 --- a/.eslintrc +++ b/.eslintrc @@ -8,6 +8,7 @@ "alerts" : false, "Apps" : false, "Assets" : false, + "AudioRecorder" : false, "ChatMessage" : false, "ChatMessages" : false, "chatMessages" : false, diff --git a/packages/rocketchat-ui-message/client/messageBox.js b/packages/rocketchat-ui-message/client/messageBox.js index 9f5b68d6926c..9f2d58919cc4 100644 --- a/packages/rocketchat-ui-message/client/messageBox.js +++ b/packages/rocketchat-ui-message/client/messageBox.js @@ -282,7 +282,7 @@ Template.messageBox.helpers({ return Template.instance().dataReply.get(); }, isAudioMessageAllowed() { - return (navigator.getUserMedia || navigator.webkitGetUserMedia || + return (navigator.mediaDevices || navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia) && RocketChat.settings.get('FileUpload_Enabled') && RocketChat.settings.get('Message_AudioRecorderEnabled') && diff --git a/packages/rocketchat-ui-message/client/startup/messageBoxActions.js b/packages/rocketchat-ui-message/client/startup/messageBoxActions.js index d99a94614863..015236ed767f 100644 --- a/packages/rocketchat-ui-message/client/startup/messageBoxActions.js +++ b/packages/rocketchat-ui-message/client/startup/messageBoxActions.js @@ -9,10 +9,14 @@ import { t, modal, fileUpload } from 'meteor/rocketchat:ui'; RocketChat.messageBox.actions.add('Create_new', 'Video_message', { id: 'video-message', icon: 'video', - condition: () => (navigator.getUserMedia || navigator.webkitGetUserMedia) && RocketChat.settings.get('FileUpload_Enabled') && RocketChat.settings.get('Message_VideoRecorderEnabled') && (!RocketChat.settings.get('FileUpload_MediaTypeWhiteList') || RocketChat.settings.get('FileUpload_MediaTypeWhiteList').match(/video\/webm|video\/\*/i)), - action({ messageBox }) { - return VRecDialog.opened ? VRecDialog.close() : VRecDialog.open(messageBox); - }, + condition: () => (navigator.mediaDevices || navigator.getUserMedia || navigator.webkitGetUserMedia || + navigator.mozGetUserMedia || navigator.msGetUserMedia) && + window.MediaRecorder && + RocketChat.settings.get('FileUpload_Enabled') && + RocketChat.settings.get('Message_VideoRecorderEnabled') && + (!RocketChat.settings.get('FileUpload_MediaTypeWhiteList') || + RocketChat.settings.get('FileUpload_MediaTypeWhiteList').match(/video\/webm|video\/\*/i)), + action: ({ messageBox }) => (VRecDialog.opened ? VRecDialog.close() : VRecDialog.open(messageBox)), }); RocketChat.messageBox.actions.add('Add_files_from', 'Computer', { diff --git a/packages/rocketchat-ui/client/lib/recorderjs/audioRecorder.js b/packages/rocketchat-ui/client/lib/recorderjs/audioRecorder.js index 24b3955da678..ee239c0e778b 100644 --- a/packages/rocketchat-ui/client/lib/recorderjs/audioRecorder.js +++ b/packages/rocketchat-ui/client/lib/recorderjs/audioRecorder.js @@ -1,26 +1,33 @@ /* globals Recorder */ -AudioRecorder = new class { //eslint-disable-line +// TODO: embed Recorder class here +// TODO: create the worker for mp3 encoding on-the-fly +AudioRecorder = new (class AudioRecorder { start(cb) { - window.AudioContext = window.AudioContext || window.webkitAudioContext; + window.audioContext = new (window.AudioContext || window.webkitAudioContext); - navigator.getUserMedia = navigator.getUserMedia || - navigator.webkitGetUserMedia || - navigator.mozGetUserMedia || - navigator.msGetUserMedia; - - window.URL = window.URL || window.webkitURL; - window.audioContext = new AudioContext; - - const ok = (stream) => { + const handleSuccess = (stream) => { this.startUserMedia(stream); - return (cb != null ? cb.call(this) : undefined); + cb && cb.call(this, true); }; - if ((navigator.getUserMedia == null)) { - return cb(false); + const handleError = (error) => { + console.error(error); + cb && cb.call(this, false); + }; + + const oldGetUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || + navigator.msGetUserMedia; + + if (navigator.mediaDevices) { + navigator.mediaDevices.getUserMedia({ audio: true }) + .then(handleSuccess, handleError); + return; + } else if (oldGetUserMedia) { + oldGetUserMedia.call(navigator, { audio: true }, handleSuccess, handleError); + return; } - return navigator.getUserMedia({ audio: true }, ok, (e) => console.log(`No live audio input: ${ e }`)); + cb && cb.call(this, false); } startUserMedia(stream) { @@ -36,19 +43,14 @@ AudioRecorder = new class { //eslint-disable-line stop(cb) { this.recorder.stop(); - if (cb != null) { - this.getBlob(cb); - } + cb && this.recorder.exportMP3(cb); this.stream.getAudioTracks()[0].stop(); window.audioContext.close(); + delete window.audioContext; delete this.recorder; - return delete this.stream; - } - - getBlob(cb) { - return this.recorder.exportMP3(cb); + delete this.stream; } -}; +}); diff --git a/packages/rocketchat-ui/client/lib/recorderjs/recorder.js b/packages/rocketchat-ui/client/lib/recorderjs/recorder.js index 3c9969f01b3a..1dee04ea2745 100644 --- a/packages/rocketchat-ui/client/lib/recorderjs/recorder.js +++ b/packages/rocketchat-ui/client/lib/recorderjs/recorder.js @@ -78,16 +78,6 @@ this.node.connect(this.context.destination); //this should not be necessary }; - Recorder.forceDownload = function(blob, filename){ - var url = (window.URL || window.webkitURL).createObjectURL(blob); - var link = window.document.createElement('a'); - link.href = url; - link.download = filename || 'audio-message.mp3'; - var click = document.createEvent("Event"); - click.initEvent("click", true, true); - link.dispatchEvent(click); - } - window.Recorder = Recorder; })(window); diff --git a/packages/rocketchat-ui/client/lib/recorderjs/videoRecorder.js b/packages/rocketchat-ui/client/lib/recorderjs/videoRecorder.js index 146082b1959a..9005b905cbb9 100644 --- a/packages/rocketchat-ui/client/lib/recorderjs/videoRecorder.js +++ b/packages/rocketchat-ui/client/lib/recorderjs/videoRecorder.js @@ -1,6 +1,6 @@ import { ReactiveVar } from 'meteor/reactive-var'; -VideoRecorder = new class { //eslint-disable-line +VideoRecorder = new (class VideoRecorder { constructor() { this.started = false; this.cameraStarted = new ReactiveVar(false); @@ -9,20 +9,31 @@ VideoRecorder = new class { //eslint-disable-line } start(videoel, cb) { - navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia; - window.URL = window.URL || window.webkitURL; - this.videoel = videoel; - const ok = (stream) => { + + const handleSuccess = (stream) => { this.startUserMedia(stream); - return (cb != null ? cb.call(this) : undefined); + cb && cb.call(this, true); + }; + + const handleError = (error) => { + console.error(error); + cb && cb.call(this, false); }; - if (navigator.getUserMedia == null) { - return cb(false); + const oldGetUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || + navigator.msGetUserMedia; + + if (navigator.mediaDevices) { + navigator.mediaDevices.getUserMedia({ audio: true, video: true }) + .then(handleSuccess, handleError); + return; + } else if (oldGetUserMedia) { + oldGetUserMedia.call(navigator, { audio: true, video: true }, handleSuccess, handleError); + return; } - return navigator.getUserMedia({ audio: true, video: true }, ok, (e) => console.log(`No live video input: ${ e }`)); + cb && cb.call(this, false); } record() { @@ -30,9 +41,7 @@ VideoRecorder = new class { //eslint-disable-line if (this.stream == null) { return; } - this.mediaRecorder = new MediaRecorder(this.stream); - this.mediaRecorder.stream = this.stream; - this.mediaRecorder.mimeType = 'video/webm'; + this.mediaRecorder = new MediaRecorder(this.stream, { type: 'video/webm' }); this.mediaRecorder.ondataavailable = (blobev) => { this.chunks.push(blobev.data); if (!this.recordingAvailable.get()) { @@ -45,58 +54,67 @@ VideoRecorder = new class { //eslint-disable-line startUserMedia(stream) { this.stream = stream; + try { this.videoel.srcObject = stream; - } catch (_e) { + } catch (error) { + const URL = window.URL || window.webkitURL; this.videoel.src = URL.createObjectURL(stream); } - this.videoel.onloadedmetadata = () => this.videoel.play(); + + this.videoel.onloadedmetadata = () => { + this.videoel && this.videoel.play(); + }; this.started = true; return this.cameraStarted.set(true); } stop(cb) { - if (this.started) { - this.stopRecording(); - - if (this.stream) { - const vtracks = this.stream.getVideoTracks(); - for (const vtrack of Array.from(vtracks)) { - vtrack.stop(); - } - - const atracks = this.stream.getAudioTracks(); - for (const atrack of Array.from(atracks)) { - atrack.stop(); - } + if (!this.started) { + return; + } + + this.stopRecording(); + + if (this.stream) { + const vtracks = this.stream.getVideoTracks(); + for (const vtrack of Array.from(vtracks)) { + vtrack.stop(); } - if (this.videoel) { - this.videoel.pause; - this.videoel.src = ''; + const atracks = this.stream.getAudioTracks(); + for (const atrack of Array.from(atracks)) { + atrack.stop(); } + } - this.started = false; - this.cameraStarted.set(false); - this.recordingAvailable.set(false); + if (this.videoel) { + this.videoel.pause; + this.videoel.src = ''; + } - if (cb && this.chunks) { - const blob = new Blob(this.chunks, { type : 'video/webm' }); - cb(blob); - } + this.started = false; + this.cameraStarted.set(false); + this.recordingAvailable.set(false); - delete this.recorder; - delete this.stream; - return delete this.videoel; + if (cb && this.chunks) { + const blob = new Blob(this.chunks, { type: 'video/webm' }); + cb(blob); } + + delete this.recorder; + delete this.stream; + delete this.videoel; } stopRecording() { - if (this.started && this.recording && this.mediaRecorder) { - this.mediaRecorder.stop(); - this.recording.set(false); - return delete this.mediaRecorder; + if (!this.started || !this.recording || !this.mediaRecorder) { + return; } + + this.mediaRecorder.stop(); + this.recording.set(false); + delete this.mediaRecorder; } -}; +});