add on the fly video recording and uploading

pull/3550/head
Flavio Grossi 10 years ago
parent c7d8a8165c
commit 7cbb7daabc
  1. 1
      .meteor/packages
  2. 1
      .meteor/versions
  3. 4
      packages/rocketchat-lib/i18n/en.i18n.json
  4. 6
      packages/rocketchat-theme/assets/stylesheets/base.less
  5. 23
      packages/rocketchat-ui-message/message/messageBox.coffee
  6. 5
      packages/rocketchat-ui-message/message/messageBox.html
  7. 0
      packages/rocketchat-ui-vrecord/README.md
  8. 47
      packages/rocketchat-ui-vrecord/client/VRecDialog.coffee
  9. 36
      packages/rocketchat-ui-vrecord/client/vrecord.coffee
  10. 20
      packages/rocketchat-ui-vrecord/client/vrecord.html
  11. 54
      packages/rocketchat-ui-vrecord/client/vrecord.less
  12. 2
      packages/rocketchat-ui-vrecord/loadStylesheet.coffee
  13. 31
      packages/rocketchat-ui-vrecord/package.js
  14. 2
      packages/rocketchat-ui-vrecord/server/settings.coffee
  15. 10
      packages/rocketchat-ui/lib/fileUpload.coffee
  16. 72
      packages/rocketchat-ui/lib/recorderjs/videoRecorder.coffee
  17. 1
      packages/rocketchat-ui/package.js

@ -103,6 +103,7 @@ rocketchat:ui-login
rocketchat:ui-master
rocketchat:ui-message
rocketchat:ui-sidenav
rocketchat:ui-vrecord
rocketchat:version
rocketchat:webrtc
rocketchat:wordpress

@ -195,6 +195,7 @@ rocketchat:tutum@0.0.1
rocketchat:ui@0.1.0
rocketchat:ui-account@0.1.0
rocketchat:ui-admin@0.1.0
rocketchat:ui-vrecord@0.0.1
rocketchat:ui-flextab@0.1.0
rocketchat:ui-login@0.1.0
rocketchat:ui-master@0.1.0

@ -1237,5 +1237,7 @@
"Your_file_has_been_deleted" : "Your file has been deleted.",
"Your_mail_was_sent_to_s" : "Your mail was sent to %s",
"Your_password_is_wrong" : "Your password is wrong!",
"Your_push_was_sent_to_s_devices" : "Your push was sent to %s devices"
"Your_push_was_sent_to_s_devices" : "Your push was sent to %s devices",
"Message_VideoRecorderEnabled" : "Video Recorder Enabled",
"Message_VideoRecorderEnabledDescription" : "Requires 'video/webm' files to be an accepted media type within 'File Upload' settings."
}

@ -2267,6 +2267,12 @@ a.github-fork {
&.show-mic .stop-mic {
display: inline-block;
}
&.hide-vrec {
display: none;
}
&.show-vrec .video-button {
display: inline-block;
}
&.show-send .send-button {
display: inline-block;
}

@ -69,6 +69,15 @@ Template.messageBox.helpers
if Template.instance().showMicButton.get()
return 'show-mic'
showVRec: ->
if not Template.instance().isMessageFieldEmpty.get()
return
if Template.instance().showVideoRec.get()
return ''
else
return 'hide-vrec'
showSend: ->
if not Template.instance().isMessageFieldEmpty.get() or not Template.instance().showMicButton.get()
return 'show-send'
@ -140,6 +149,12 @@ Template.messageBox.events
t.$('.stop-mic').removeClass('hidden')
t.$('.mic').addClass('hidden')
'click .message-form .video-button': (e, t) ->
if VRecDialog.opened
VRecDialog.close()
else
VRecDialog.open(e.currentTarget)
'click .message-form .stop-mic': (e, t) ->
AudioRecorder.stop (blob) ->
fileUpload [{
@ -154,8 +169,16 @@ Template.messageBox.events
Template.messageBox.onCreated ->
@isMessageFieldEmpty = new ReactiveVar true
@showMicButton = new ReactiveVar false
@showVideoRec = new ReactiveVar false
@autorun =>
videoRegex = /video\/webm/i
videoEnabled = !RocketChat.settings.get("FileUpload_MediaTypeWhiteList") || RocketChat.settings.get("FileUpload_MediaTypeWhiteList").match(wavRegex)
if RocketChat.settings.get('Message_VideoRecorderEnabled') and (navigator.getUserMedia? or navigator.webkitGetUserMedia?) and videoEnabled and RocketChat.settings.get('FileUpload_Enabled')
@showVideoRec.set true
else
@showVideoRec.set false
wavRegex = /audio\/wav|audio\/\*/i
wavEnabled = !RocketChat.settings.get("FileUpload_MediaTypeWhiteList") || RocketChat.settings.get("FileUpload_MediaTypeWhiteList").match(wavRegex)
if RocketChat.settings.get('Message_AudioRecorderEnabled') and (navigator.getUserMedia? or navigator.webkitGetUserMedia?) and wavEnabled and RocketChat.settings.get('FileUpload_Enabled')

@ -26,6 +26,11 @@
<i class="icon-paper-plane" aria-label="{{_ "Send"}}"></i>
</div>
</div>
<div class="message-buttons {{showVRec}}">
<div class="video-button">
<i class="icon-videocam" aria-label="{{_ "Record"}}"></i>
</div>
</div>
</div>
<div class="users-typing">
{{#with usersTyping}}

@ -0,0 +1,47 @@
@VRecDialog = new class
opened: false
initiated: false
width: 400
height: 280
init: ->
if @initiated
return
@initiated = true
Blaze.render(Template.vrecDialog, document.body)
open: (source) ->
if not @initiated
@init()
@source = source
dialog = $('.vrec-dialog')
@setPosition(dialog, source)
dialog.addClass('show')
@opened = true
@initializeCamera()
close: ->
$('.vrec-dialog').removeClass('show')
@opened = false
if @video?
VideoRecorder.stop()
setPosition: (dialog, source) ->
sourcePos = $(source).offset()
left = sourcePos.left - @width + 100
top = sourcePos.top - @height - 40
left = 10 if left < 0
top = 10 if top < 0
dialog.css({ top: top + 'px', left: left + 'px' })
initializeCamera: ->
@video = $('.vrec-dialog video').get('0')
if not @video
return
VideoRecorder.start @video

@ -0,0 +1,36 @@
Template.vrecDialog.helpers
recordIcon: ->
if VideoRecorder.cameraStarted.get() and VideoRecorder.recording.get()
return 'icon-stop'
else
return 'icon-circle'
okDisabled: ->
if VideoRecorder.cameraStarted.get() and VideoRecorder.recordingAvailable.get()
return ''
else
return 'disabled'
recordDisabled: ->
if VideoRecorder.cameraStarted.get()
return ''
else
return 'disabled'
Template.vrecDialog.events
'click .vrec-dialog .cancel': (e, t) ->
VideoRecorder.stop()
VRecDialog.close()
'click .vrec-dialog .record': (e, t) ->
if VideoRecorder.recording.get()
VideoRecorder.stopRecording()
else
VideoRecorder.record()
'click .vrec-dialog .ok': (e, t) ->
cb = (blob) =>
fileUpload [{ file: blob, type: 'video', name: TAPi18n.__('Video record') + '.webm' }]
VRecDialog.close()
VideoRecorder.stop(cb)

@ -0,0 +1,20 @@
<template name="vrecDialog">
<div class="vrec-dialog">
<div class="video-container">
<video width="320" height="240" src=""></video>
</div>
<div class="buttons">
<ul>
<li class="left-aligned">
<button class="record button primary {{recordDisabled}}" {{recordDisabled}}>
<i class="{{recordIcon}}"></i>
</button>
</li>
<li class="right-aligned">
<button class="cancel button secondary">{{_ "Cancel"}}</button>
<button class="ok button primary {{okDisabled}}" {{okDisabled}}>{{_ "Ok"}}</button>
</li>
</ul>
</div>
</div>
</template>

@ -0,0 +1,54 @@
.vrec-dialog {
opacity: 0;
visibility: hidden;
position: absolute;
background-color: @secondary-background-color;
border-radius: 5px;
box-shadow: 0px 1px 1px 0 rgba(0,0,0,0.2), 0 2px 10px 0 rgba(0,0,0,.16);
&.show {
opacity: 1;
display: block;
visibility: visible;
}
.buttons {
.disabled {
background-color: desaturate(#02acec, 80%);
}
> ul {
display: table;
width: 100%;
> li {
display: table-cell;
margin: 0 2px;
padding: 6px 0;
border-bottom: 2px solid @secondary-background-color;
&.right-aligned {
text-align: right;
}
&.left-aligned {
text-align: left;
}
}
}
}
.video-container {
padding: 5px 5px 5px 5px;
}
}
.vrec-dialog button.hide {
opacity: 0;
visibility: hidden;
}
.vrec-dialog button.show {
opacity: 1;
display: block;
visibility: visible;
}

@ -0,0 +1,2 @@
RocketChat.theme.addPackageAsset ->
return Assets.getText 'client/vrecord.less'

@ -0,0 +1,31 @@
Package.describe({
"name": "rocketchat:ui-vrecord",
"version": "0.0.1",
"description": "Video upload with on the fly recording",
"documentation": "README.md"
});
Package.onUse(function(api) {
api.versionsFrom('1.2.1');
api.use([
'mongo',
'ecmascript',
'templating',
'coffeescript',
'underscore',
'tracker',
'rocketchat:lib',
'less@2.5.1'
]);
api.addAssets('client/vrecord.less', 'server');
api.addFiles('client/vrecord.html', 'client');
api.addFiles('client/vrecord.coffee', 'client');
api.addFiles('client/VRecDialog.coffee', 'client');
api.addFiles('server/settings.coffee', 'server');
api.addFiles('loadStylesheet.coffee', 'server');
});

@ -0,0 +1,2 @@
RocketChat.settings.addGroup 'Message', ->
@add 'Message_VideoRecorderEnabled', true, { type: 'boolean', public: true, i18nDescription: 'Message_VideoRecorderEnabledDescription' }

@ -50,6 +50,16 @@ readAsArrayBuffer = (file, callback) ->
</div>
<div class='upload-preview-title'>#{Handlebars._escape(file.name)}</div>
"""
else if file.type is 'video'
text = """
<div class='upload-preview'>
<video style="width: 100%;" controls="controls">
<source src="#{fileContent}" type="video/webm">
Your browser does not support the video element.
</video>
</div>
<div class='upload-preview-title'>#{Handlebars._escape(file.name)}</div>
"""
else
text = """
<div class='upload-preview'>

@ -0,0 +1,72 @@
@VideoRecorder = new class
started: false
cameraStarted: new ReactiveVar false
recording: new ReactiveVar false
recordingAvailable: new ReactiveVar false
start: (videoel, cb) ->
navigator.getUserMedia = navigator.getUserMedia or navigator.webkitGetUserMedia or navigator.mozGetUserMedia
window.URL = window.URL or window.webkitURL
@videoel = videoel
ok = (stream) =>
@startUserMedia(stream)
cb?.call(@)
if not navigator.getUserMedia?
return cb false
navigator.getUserMedia {audio: true, video: true}, ok, (e) ->
console.log('No live video input: ' + e)
record: ->
@chunks = []
if not @stream?
return
@mediaRecorder = new MediaRecorder(@stream)
@mediaRecorder.stream = @stream
@mediaRecorder.mimeType = 'video/webm'
@mediaRecorder.ondataavailable = (blobev) =>
@chunks.push(blobev.data)
if not @recordingAvailable.get()
@recordingAvailable.set true
@mediaRecorder.start()
@recording.set true
startUserMedia: (stream) ->
@stream = stream
@videoel.src = URL.createObjectURL(stream)
@videoel.onloadedmetadata = (e) =>
@videoel.play()
@started = true
@cameraStarted.set true
stop: (cb) ->
if @started
@stopRecording()
if @stream?
@stream.getVideoTracks()[0].stop()
if @videoel?
@videoel.pause
@videoel.src = ''
@started = false
@cameraStarted.set false
@recordingAvailable.set false
if cb? and @chunks?
blob = new Blob(@chunks, { 'type' : 'video/webm' })
cb(blob)
delete @recorder
delete @stream
delete @videoel
stopRecording: ->
if @started and @recording and @mediaRecorder?
@mediaRecorder.stop()
@recording.set false
delete @mediaRecorder

@ -67,6 +67,7 @@ Package.onUse(function(api) {
// LIB RECORDERJS
api.addFiles('lib/recorderjs/audioRecorder.coffee', 'client');
api.addFiles('lib/recorderjs/videoRecorder.coffee', 'client');
api.addFiles('lib/recorderjs/recorder.js', 'client');
// LIB CLIPBOARDJS

Loading…
Cancel
Save