parent
c7d8a8165c
commit
7cbb7daabc
@ -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' } |
||||
@ -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 |
||||
Loading…
Reference in new issue