Merge pull request #564 from jitsi/recording-related-work-in-progress

Recording related UI modifications.
This commit is contained in:
damencho 2016-03-29 13:52:24 -05:00
commit 24896634f6
10 changed files with 364 additions and 145 deletions

View File

@ -567,7 +567,7 @@ export default {
_getConferenceOptions() {
let options = config;
if(config.enableRecording) {
if(config.enableRecording && !config.recordingType) {
options.recordingType = (config.hosts &&
(typeof config.hosts.jirecon != "undefined"))?
"jirecon" : "colibri";
@ -848,7 +848,8 @@ export default {
APP.UI.changeDisplayName(id, displayName);
});
room.on(ConferenceEvents.RECORDING_STATE_CHANGED, (status, error) => {
room.on(ConferenceEvents.RECORDER_STATE_CHANGED, (status, error) => {
console.log("Received recorder status change: ", status, error);
if(status == "error") {
console.error(error);
return;
@ -1008,15 +1009,8 @@ export default {
// Starts or stops the recording for the conference.
APP.UI.addListener(UIEvents.RECORDING_TOGGLE, (predefinedToken) => {
if (predefinedToken) {
room.toggleRecording({token: predefinedToken});
return;
}
APP.UI.requestRecordingToken().then((token) => {
room.toggleRecording({token: token});
});
APP.UI.addListener(UIEvents.RECORDING_TOGGLED, (options) => {
room.toggleRecording(options);
});
APP.UI.addListener(UIEvents.SUBJECT_CHANGED, (topic) => {

View File

@ -129,21 +129,6 @@ html, body{
-moz-transition: all .5s ease-in-out;
transition: all .5s ease-in-out;
}
/*#ffde00*/
#toolbar_button_record.active {
-webkit-text-shadow: -1px 0 10px #00ccff,
0 1px 10px #00ccff,
1px 0 10px #00ccff,
0 -1px 10px #00ccff;
-moz-text-shadow: 1px 0 10px #00ccff,
0 1px 10px #00ccff,
1px 0 10px #00ccff,
0 -1px 10px #00ccff;
text-shadow: -1px 0 10px #00ccff,
0 1px 10px #00ccff,
1px 0 10px #00ccff,
0 -1px 10px #00ccff;
}
a.button:hover,
a.bottomToolbarButton:hover {
@ -298,7 +283,7 @@ div.feedbackButton:hover {
}
.active {
color: #00ccff;
background-color: #00ccff;
}
.bottomToolbar_span>span {

View File

@ -488,4 +488,34 @@
padding: 10px;
color: rgba(255,255,255,.5);
z-index: 10000;
}
.centeredVideoLabel {
display: none;
position: absolute;
bottom: 45%;
top: auto;
right: auto;
left: auto;
line-height: 28px;
height: 28px;
width: auto;
padding: 5px;
margin-right: auto;
margin-left: auto;
background: rgba(0,0,0,.5);
color: #FFF;
z-index: 10000;
border-radius: 4px;
-webkit-transition: all 2s 2s linear;
transition: all 2s 2s linear;
}
.moveToCorner {
top: 5px;
right: 5px;
margin-right: 0px;
margin-left: auto;
background: rgba(0,0,0,.3);
color: rgba(255,255,255,.5);
}

View File

@ -116,7 +116,7 @@
</span>
<a class="button icon-microphone" id="toolbar_button_mute" data-container="body" data-toggle="popover" data-placement="bottom" shortcut="mutePopover" data-i18n="[content]toolbar.mute" content="Mute / Unmute"></a>
<a class="button icon-camera" id="toolbar_button_camera" data-container="body" data-toggle="popover" data-placement="bottom" shortcut="toggleVideoPopover" data-i18n="[content]toolbar.videomute" content="Start / stop camera"></a>
<a class="button icon-recEnable" id="toolbar_button_record" data-container="body" data-toggle="popover" data-placement="bottom" data-i18n="[content]toolbar.record" content="Record" style="display: none"></a>
<a class="button" id="toolbar_button_record" data-container="body" data-toggle="popover" data-placement="bottom" data-i18n="[content]toolbar.record" content="Record" style="display: none"></a>
<a class="button icon-security" id="toolbar_button_security" data-container="body" data-toggle="popover" data-placement="bottom" data-i18n="[content]toolbar.lock" content="Lock / unlock room"></a>
<a class="button icon-link" id="toolbar_button_link" data-container="body" data-toggle="popover" data-placement="bottom" data-i18n="[content]toolbar.invite" content="Invite others"></a>
<a class="button icon-chat" id="toolbar_button_chat" data-container="body" data-toggle="popover" shortcut="toggleChatPopover" data-placement="bottom" data-i18n="[content]toolbar.chat" content="Open / close chat">
@ -154,6 +154,7 @@
</div>
<span id="videoConnectionMessage"></span>
<span id="videoResolutionLabel">HD</span>
<span id="recordingLabel" class="centeredVideoLabel"></span>
</div>
<div id="remoteVideos">

View File

@ -51,7 +51,7 @@
"mute": "Mute / Unmute",
"videomute": "Start / stop camera",
"authenticate": "Authenticate",
"record": "Record",
"record": "Toggle recording",
"lock": "Lock / unlock room",
"invite": "Invite others",
"chat": "Open / close chat",
@ -179,6 +179,7 @@
"joinAgain": "Join again",
"Share": "Share",
"Save": "Save",
"recording": "Recording",
"recordingToken": "Enter recording token",
"Dial": "Dial",
"sipMsg": "Enter SIP number",
@ -206,7 +207,14 @@
"firefoxExtensionPrompt": "You need to install a Firefox extension in order to use screen sharing. Please try again after you <a href='__url__'>get it from here</a>!",
"feedbackQuestion": "How was your call?",
"thankYou": "Thank you for using __appName__!",
"sorryFeedback": "We're sorry to hear that. Would you like to tell us more?"
"sorryFeedback": "We're sorry to hear that. Would you like to tell us more?",
"liveStreaming": "Live Streaming",
"streamKey": "Stream name/key",
"startLiveStreaming": "Start live streaming",
"stopStreamingWarning": "Are you sure you would like to stop the live streaming?",
"stopRecordingWarning": "Are you sure you would like to stop the recording?",
"stopLiveStreaming": "Stop live streaming",
"stopRecording": "Stop recording"
},
"email":
{
@ -254,8 +262,17 @@
},
"recording":
{
"toaster": "Currently recording!",
"pending": "Your recording will start as soon as another participant joins",
"on": "Recording has been started"
"pending": "Recording waiting for a participant to join...",
"on": "Recording",
"off": "Recording stopped",
"failedToStart": "Recording failed to start"
},
"liveStreaming":
{
"pending": "Starting Live Stream...",
"on": "Live Streaming",
"off": "Live Streaming Stopped",
"unavailable": "The live streaming service is currently unavailable. Please try again later.",
"failedToStart": "Live streaming failed to start"
}
}

View File

@ -14,6 +14,7 @@ import UIEvents from "../../service/UI/UIEvents";
import CQEvents from '../../service/connectionquality/CQEvents';
import EtherpadManager from './etherpad/Etherpad';
import SharedVideoManager from './shared_video/SharedVideo';
import Recording from "./recording/Recording";
import VideoLayout from "./videolayout/VideoLayout";
import FilmStrip from "./videolayout/FilmStrip";
@ -363,12 +364,16 @@ UI.start = function () {
bindEvents();
sharedVideoManager = new SharedVideoManager(eventEmitter);
if (!interfaceConfig.filmStripOnly) {
$("#videospace").mousemove(function () {
return ToolbarToggler.showToolbar();
});
setupToolbars();
setupChat();
// Initialise the recording module.
if (config.enableRecording)
Recording.init(eventEmitter, config.recordingType);
// Display notice message at the top of the toolbar
if (config.noticeMessage) {
$('#noticeText').text(config.noticeMessage);
@ -566,15 +571,15 @@ UI.updateLocalRole = function (isModerator) {
VideoLayout.showModeratorIndicator();
Toolbar.showSipCallButton(isModerator);
Toolbar.showRecordingButton(isModerator);
Toolbar.showSharedVideoButton(isModerator);
Recording.showRecordingButton(isModerator);
SettingsMenu.showStartMutedOptions(isModerator);
SettingsMenu.showFollowMeOptions(isModerator);
if (isModerator) {
messageHandler.notify(null, "notify.me", 'connected', "notify.moderator");
Toolbar.checkAutoRecord();
Recording.checkAutoRecord();
}
};
@ -977,37 +982,8 @@ UI.requestFeedback = function () {
});
};
/**
* Request recording token from the user.
* @returns {Promise}
*/
UI.requestRecordingToken = function () {
let msg = APP.translation.generateTranslationHTML("dialog.recordingToken");
let token = APP.translation.translateString("dialog.token");
return new Promise(function (resolve, reject) {
messageHandler.openTwoButtonDialog(
null, null, null,
`<h2>${msg}</h2>
<input name="recordingToken" type="text"
data-i18n="[placeholder]dialog.token"
placeholder="${token}" autofocus>`,
false, "dialog.Save",
function (e, v, m, f) {
if (v && f.recordingToken) {
resolve(UIUtil.escapeHtml(f.recordingToken));
} else {
reject();
}
},
null,
function () { },
':input:first'
);
});
};
UI.updateRecordingState = function (state) {
Toolbar.updateRecordingState(state);
Recording.updateRecordingState(state);
};
UI.notifyTokenAuthFailed = function () {

View File

@ -0,0 +1,289 @@
/* global APP, $, config, interfaceConfig */
/*
* Copyright @ 2015 Atlassian Pty Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import UIEvents from "../../../service/UI/UIEvents";
import UIUtil from '../util/UIUtil';
/**
* Recording.
*/
let recordingToaster = null;
function _isRecordingButtonEnabled() {
return interfaceConfig.TOOLBAR_BUTTONS.indexOf("recording") !== -1
&& config.enableRecording;
}
/**
* Request live stream token from the user.
* @returns {Promise}
*/
function _requestLiveStreamId() {
let msg = APP.translation.generateTranslationHTML("dialog.liveStreaming");
let token = APP.translation.translateString("dialog.streamKey");
return new Promise(function (resolve, reject) {
APP.UI.messageHandler.openTwoButtonDialog(
null, null, null,
`<h2>${msg}</h2>
<input name="streamId" type="text"
data-i18n="[placeholder]dialog.streamKey"
placeholder="${token}" autofocus>`,
false, "dialog.startLiveStreaming",
function (e, v, m, f) {
if (v && f.streamId) {
resolve(UIUtil.escapeHtml(f.streamId));
} else {
reject();
}
},
null,
function () { },
':input:first'
);
});
}
/**
* Request recording token from the user.
* @returns {Promise}
*/
function _requestRecordingToken () {
let msg = APP.translation.generateTranslationHTML("dialog.recordingToken");
let token = APP.translation.translateString("dialog.token");
return new Promise(function (resolve, reject) {
APP.UI.messageHandler.openTwoButtonDialog(
null, null, null,
`<h2>${msg}</h2>
<input name="recordingToken" type="text"
data-i18n="[placeholder]dialog.token"
placeholder="${token}" autofocus>`,
false, "dialog.Save",
function (e, v, m, f) {
if (v && f.recordingToken) {
resolve(UIUtil.escapeHtml(f.recordingToken));
} else {
reject();
}
},
null,
function () { },
':input:first'
);
});
}
function _showStopRecordingPrompt (recordingType) {
var title;
var message;
var buttonKey;
if (recordingType === "jibri") {
title = "dialog.liveStreaming";
message = "dialog.stopStreamingWarning";
buttonKey = "dialog.stopLiveStreaming";
}
else {
title = "dialog.recording";
message = "dialog.stopRecordingWarning";
buttonKey = "dialog.stopRecording";
}
return new Promise(function (resolve, reject) {
APP.UI.messageHandler.openTwoButtonDialog(
title,
null,
message,
null,
false,
buttonKey,
function(e,v,m,f) {
if (v) {
resolve();
} else {
reject();
}
}
);
});
}
function moveToCorner(selector, move) {
let moveToCornerClass = "moveToCorner";
if (move && !selector.hasClass(moveToCornerClass))
selector.addClass(moveToCornerClass);
else
selector.removeClass(moveToCornerClass);
}
var Recording = {
/**
* Initializes the recording UI.
*/
init (emitter, recordingType) {
this.eventEmitter = emitter;
this.initRecordingButton(recordingType);
},
initRecordingButton(recordingType) {
let selector = $('#toolbar_button_record');
if (recordingType === 'jibri') {
this.baseClass = "fa fa-play-circle";
this.recordingOnKey = "liveStreaming.on";
this.recordingOffKey = "liveStreaming.off";
this.recordingPendingKey = "liveStreaming.pending";
this.failedToStartKey = "liveStreaming.failedToStart";
}
else {
this.baseClass = "icon-recEnable";
this.recordingOnKey = "recording.on";
this.recordingOffKey = "recording.off";
this.recordingPendingKey = "recording.pending";
this.failedToStartKey = "recording.failedToStart";
}
selector.addClass(this.baseClass);
var self = this;
selector.click(function () {
console.log("BUTTON CLICKED", self.currentState);
switch (self.currentState) {
case "on": {
_showStopRecordingPrompt(recordingType).then(() =>
self.eventEmitter.emit(UIEvents.RECORDING_TOGGLED));
}
break;
case "available":
case "off": {
if (recordingType === 'jibri')
_requestLiveStreamId().then((streamId) => {
self.eventEmitter.emit( UIEvents.RECORDING_TOGGLED,
{streamId: streamId});
});
else {
if (self.predefinedToken) {
self.eventEmitter.emit( UIEvents.RECORDING_TOGGLED,
{token: self.predefinedToken});
return;
}
_requestRecordingToken().then((token) => {
self.eventEmitter.emit( UIEvents.RECORDING_TOGGLED,
{token: token});
});
}
}
break;
default: {
APP.UI.messageHandler.openMessageDialog(
"dialog.liveStreaming",
"liveStreaming.unavailable"
);
}
}
});
},
// Shows or hides the 'recording' button.
showRecordingButton (show) {
if (_isRecordingButtonEnabled() && show) {
$('#toolbar_button_record').css({display: "inline-block"});
} else {
$('#toolbar_button_record').css({display: "none"});
}
},
updateRecordingState(recordingState) {
// If there's no state change, we ignore the update.
if (this.currentState === recordingState)
return;
this.setRecordingButtonState(recordingState);
},
// Sets the state of the recording button
setRecordingButtonState (recordingState) {
let buttonSelector = $('#toolbar_button_record');
let labelSelector = $('#recordingLabel');
// TODO: handle recording state=available
if (recordingState === 'on') {
buttonSelector.removeClass(this.baseClass);
buttonSelector.addClass(this.baseClass + " active");
labelSelector.attr("data-i18n", this.recordingOnKey);
moveToCorner(labelSelector, true, 3000);
labelSelector
.text(APP.translation.translateString(this.recordingOnKey));
} else if (recordingState === 'off'
|| recordingState === 'unavailable') {
// We don't want to do any changes if this is
// an availability change.
if (this.currentState === "available"
|| this.currentState === "unavailable")
return;
buttonSelector.removeClass(this.baseClass + " active");
buttonSelector.addClass(this.baseClass);
moveToCorner(labelSelector, false);
let messageKey;
if (this.currentState === "pending")
messageKey = this.failedToStartKey;
else
messageKey = this.recordingOffKey;
labelSelector.attr("data-i18n", messageKey);
labelSelector.text(APP.translation.translateString(messageKey));
setTimeout(function(){
$('#recordingLabel').css({display: "none"});
}, 5000);
}
else if (recordingState === 'pending') {
buttonSelector.removeClass(this.baseClass + " active");
buttonSelector.addClass(this.baseClass);
moveToCorner(labelSelector, false);
labelSelector
.attr("data-i18n", this.recordingPendingKey);
labelSelector
.text(APP.translation.translateString(
this.recordingPendingKey));
}
this.currentState = recordingState;
if (!labelSelector.is(":visible"))
labelSelector.css({display: "inline-block"});
},
// checks whether recording is enabled and whether we have params
// to start automatically recording
checkAutoRecord () {
if (_isRecordingButtonEnabled && config.autoRecord) {
this.predefinedToken = UIUtil.escapeHtml(config.autoRecordToken);
this.eventEmitter.emit(UIEvents.RECORDING_TOGGLED,
this.predefinedToken);
}
}
};
export default Recording;

View File

@ -6,7 +6,6 @@ import AnalyticsAdapter from '../../statistics/AnalyticsAdapter';
import UIEvents from '../../../service/UI/UIEvents';
let roomUrl = null;
let recordingToaster = null;
let emitter = null;
@ -43,51 +42,6 @@ function openLinkDialog () {
);
}
// Sets the state of the recording button
function setRecordingButtonState (recordingState) {
let selector = $('#toolbar_button_record');
if (recordingState === 'on') {
selector.removeClass("icon-recEnable");
selector.addClass("icon-recEnable active");
$("#largeVideo").toggleClass("videoMessageFilter", true);
let recordOnKey = "recording.on";
$('#videoConnectionMessage').attr("data-i18n", recordOnKey);
$('#videoConnectionMessage').text(APP.translation.translateString(recordOnKey));
setTimeout(function(){
$("#largeVideo").toggleClass("videoMessageFilter", false);
$('#videoConnectionMessage').css({display: "none"});
}, 1500);
recordingToaster = messageHandler.notify(
null, "recording.toaster", null,
null, null,
{timeOut: 0, closeButton: null, tapToDismiss: false}
);
} else if (recordingState === 'off') {
selector.removeClass("icon-recEnable active");
selector.addClass("icon-recEnable");
$("#largeVideo").toggleClass("videoMessageFilter", false);
$('#videoConnectionMessage').css({display: "none"});
if (recordingToaster) {
messageHandler.remove(recordingToaster);
}
} else if (recordingState === 'pending') {
selector.removeClass("icon-recEnable active");
selector.addClass("icon-recEnable");
$("#largeVideo").toggleClass("videoMessageFilter", true);
let recordPendingKey = "recording.pending";
$('#videoConnectionMessage').attr("data-i18n", recordPendingKey);
$('#videoConnectionMessage').text(APP.translation.translateString(recordPendingKey));
$('#videoConnectionMessage').css({display: "block"});
}
}
const buttonHandlers = {
"toolbar_button_mute": function () {
if (APP.conference.audioMuted) {
@ -107,10 +61,6 @@ const buttonHandlers = {
emitter.emit(UIEvents.VIDEO_MUTED, true);
}
},
"toolbar_button_record": function () {
AnalyticsAdapter.sendEvent('toolbar.recording.toggled');
emitter.emit(UIEvents.RECORDING_TOGGLE);
},
"toolbar_button_security": function () {
emitter.emit(UIEvents.ROOM_LOCK_CLICKED);
},
@ -250,7 +200,8 @@ const Toolbar = {
*/
unlockLockButton () {
if ($("#toolbar_button_security").hasClass("icon-security-locked"))
UIUtil.buttonClick("#toolbar_button_security", "icon-security icon-security-locked");
UIUtil.buttonClick("#toolbar_button_security",
"icon-security icon-security-locked");
},
/**
@ -258,7 +209,8 @@ const Toolbar = {
*/
lockLockButton () {
if ($("#toolbar_button_security").hasClass("icon-security"))
UIUtil.buttonClick("#toolbar_button_security", "icon-security icon-security-locked");
UIUtil.buttonClick("#toolbar_button_security",
"icon-security icon-security-locked");
},
/**
@ -279,15 +231,6 @@ const Toolbar = {
}
},
// Shows or hides the 'recording' button.
showRecordingButton (show) {
if (UIUtil.isButtonEnabled('recording') && show) {
$('#toolbar_button_record').css({display: "inline-block"});
} else {
$('#toolbar_button_record').css({display: "none"});
}
},
// Shows or hides the 'shared video' button.
showSharedVideoButton () {
if (UIUtil.isButtonEnabled('sharedvideo')
@ -298,14 +241,6 @@ const Toolbar = {
}
},
// checks whether recording is enabled and whether we have params
// to start automatically recording
checkAutoRecord () {
if (UIUtil.isButtonEnabled('recording') && config.autoRecord) {
emitter.emit(UIEvents.RECORDING_TOGGLE, UIUtil.escapeHtml(config.autoRecordToken));
}
},
// checks whether desktop sharing is enabled and whether
// we have params to start automatically sharing
checkAutoEnableDesktopSharing () {
@ -383,10 +318,6 @@ const Toolbar = {
}
},
updateRecordingState (state) {
setRecordingButtonState(state);
},
/**
* Marks video icon as muted or not.
* @param {boolean} muted if icon should look like muted or not

View File

@ -123,11 +123,7 @@
},
isButtonEnabled: function (name) {
var isEnabled = interfaceConfig.TOOLBAR_BUTTONS.indexOf(name) !== -1;
if (name === 'recording') {
return isEnabled && config.enableRecording;
}
return isEnabled;
return interfaceConfig.TOOLBAR_BUTTONS.indexOf(name) !== -1;
},
hideDisabledButtons: function (mappings) {

View File

@ -62,7 +62,7 @@ export default {
CONTACT_CLICKED: "UI.contact_clicked",
HANGUP: "UI.hangup",
LOGOUT: "UI.logout",
RECORDING_TOGGLE: "UI.recording_toggle",
RECORDING_TOGGLED: "UI.recording_toggled",
SIP_DIAL: "UI.sip_dial",
SUBJECT_CHANGED: "UI.subject_changed",
VIDEO_DEVICE_CHANGED: "UI.video_device_changed",