Merge remote-tracking branch 'upstream/master'

This commit is contained in:
luciash 2016-05-03 12:11:26 +02:00
commit 652daab30f
24 changed files with 944 additions and 307 deletions

View File

@ -46,7 +46,7 @@ uglify:
source-package:
mkdir -p source_package/jitsi-meet/css && \
cp -r analytics.js external_api.js favicon.ico fonts images index.html interface_config.js libs plugin.*html sounds title.html unsupported_browser.html LICENSE config.js lang source_package/jitsi-meet && \
cp -r *.js connection_optimization favicon.ico fonts images index.html libs plugin.*html sounds title.html unsupported_browser.html LICENSE lang source_package/jitsi-meet && \
cp css/all.css source_package/jitsi-meet/css && \
cp css/unsupported_browser.css source_package/jitsi-meet/css && \
(cd source_package ; tar cjf ../jitsi-meet.tar.bz2 jitsi-meet) && \

View File

@ -126,15 +126,14 @@ function muteLocalVideo (muted) {
* @param {boolean} [requestFeedback=false] if user feedback should be requested
*/
function hangup (requestFeedback = false) {
let promise = Promise.resolve();
if (requestFeedback) {
promise = APP.UI.requestFeedback();
}
promise.then(function () {
APP.conference._room.leave().then(() => {
connection.disconnect();
if (requestFeedback) {
return APP.UI.requestFeedback();
} else {
return Promise.resolve();
}
}).then(function () {
if (!config.enableWelcomePage) {
return;
}
@ -475,20 +474,13 @@ export default {
return this.audioLevelsMap[id];
},
/**
* Will check for number of remote particiapnts that have at least one
* remote track.
* @return {boolean} whether we have enough participants with remote streams
* @return {number} the number of participants in the conference with at
* least one track.
*/
checkEnoughParticipants (number) {
var participants = this._room.getParticipants();
var foundParticipants = 0;
for (var i = 0; i < participants.length; i += 1) {
if (participants[i].getTracks().length > 0) {
foundParticipants++;
}
}
return foundParticipants >= number;
getNumberOfParticipantsWithTracks() {
return this._room.getParticipants()
.filter((p) => p.getTracks().length > 0)
.length;
},
/**
* Returns the stats.
@ -565,6 +557,9 @@ export default {
this.useAudioStream(track);
} else if (track.isVideoTrack()) {
this.useVideoStream(track);
} else {
console.error(
"Ignored not an audio nor a video track: ", track);
}
});
roomLocker = createRoomLocker(room);
@ -753,18 +748,21 @@ export default {
room.on(ConferenceEvents.USER_JOINED, (id, user) => {
if (user.isHidden())
return;
console.log('USER %s connnected', id, user);
APP.API.notifyUserJoined(id);
APP.UI.addUser(id, user.getDisplayName());
// chek the roles for the new user and reflect them
// check the roles for the new user and reflect them
APP.UI.updateUserRole(user);
});
room.on(ConferenceEvents.USER_LEFT, (id, user) => {
console.log('USER %s LEFT', id, user);
APP.API.notifyUserLeft(id);
APP.UI.removeUser(id, user.getDisplayName());
APP.UI.stopSharedVideo(id);
APP.UI.onSharedVideoStop(id);
});
@ -1098,7 +1096,7 @@ export default {
);
APP.UI.addListener(UIEvents.UPDATE_SHARED_VIDEO,
(url, state, time, volume) => {
(url, state, time, isMuted, volume) => {
// send start and stop commands once, and remove any updates
// that had left
if (state === 'stop' || state === 'start' || state === 'playing') {
@ -1108,6 +1106,7 @@ export default {
attributes: {
state: state,
time: time,
muted: isMuted,
volume: volume
}
});
@ -1121,6 +1120,7 @@ export default {
attributes: {
state: state,
time: time,
muted: isMuted,
volume: volume
}
});
@ -1130,13 +1130,23 @@ export default {
this.commands.defaults.SHARED_VIDEO, ({value, attributes}, id) => {
if (attributes.state === 'stop') {
APP.UI.stopSharedVideo(id, attributes);
} else if (attributes.state === 'start') {
APP.UI.showSharedVideo(id, value, attributes);
} else if (attributes.state === 'playing'
APP.UI.onSharedVideoStop(id, attributes);
}
else if (attributes.state === 'start') {
APP.UI.onSharedVideoStart(id, value, attributes);
}
else if (attributes.state === 'playing'
|| attributes.state === 'pause') {
APP.UI.updateSharedVideo(id, value, attributes);
APP.UI.onSharedVideoUpdate(id, value, attributes);
}
});
},
/**
* Adss any room listener.
* @param eventName one of the ConferenceEvents
* @param callBack the function to be called when the event occurs
*/
addConferenceListener(eventName, callBack) {
room.on(eventName, callBack);
}
};

View File

@ -22,7 +22,7 @@ function checkForAttachParametersAndConnect(id, password, connection) {
// When connection optimization is not deployed or enabled the default
// value will be window.XMPPAttachInfo.status = "error"
// If the connection optimization is deployed and enabled and there is
// a failure the value will be window.XMPPAttachInfo.status = "error"
// a failure the value will be window.XMPPAttachInfo.status = "error"
if(window.XMPPAttachInfo.status === "error") {
connection.connect({id, password});
return;
@ -31,6 +31,7 @@ function checkForAttachParametersAndConnect(id, password, connection) {
var attachOptions = window.XMPPAttachInfo.data;
if(attachOptions) {
connection.attach(attachOptions);
delete window.XMPPAttachInfo.data;
} else {
connection.connect({id, password});
}
@ -51,11 +52,11 @@ function checkForAttachParametersAndConnect(id, password, connection) {
*/
function connect(id, password, roomName) {
let connectionConfig = config;
let connectionConfig = Object.assign({}, config);
connectionConfig.bosh += '?room=' + roomName;
let connection
= new JitsiMeetJS.JitsiConnection(null, config.token, config);
= new JitsiMeetJS.JitsiConnection(null, config.token, connectionConfig);
return new Promise(function (resolve, reject) {
connection.addEventListener(
@ -95,13 +96,14 @@ function connect(id, password, roomName) {
* Show Authentication Dialog and try to connect with new credentials.
* If failed to connect because of PASSWORD_REQUIRED error
* then ask for password again.
* @param {string} [roomName]
* @returns {Promise<JitsiConnection>}
*/
function requestAuth() {
function requestAuth(roomName) {
return new Promise(function (resolve, reject) {
let authDialog = LoginDialog.showAuthDialog(
function (id, password) {
connect(id, password).then(function (connection) {
connect(id, password, roomName).then(function (connection) {
authDialog.close();
resolve(connection);
}, function (err) {
@ -131,6 +133,20 @@ function requestAuth() {
* @returns {Promise<JitsiConnection>}
*/
export function openConnection({id, password, retry, roomName}) {
let usernameOverride
= window.localStorage.getItem("xmpp_username_override");
let passwordOverride
= window.localStorage.getItem("xmpp_password_override");
if (usernameOverride && usernameOverride.length > 0) {
id = usernameOverride;
}
if (passwordOverride && passwordOverride.length > 0) {
password = passwordOverride;
}
return connect(id, password, roomName).catch(function (err) {
if (!retry) {
throw err;
@ -141,7 +157,7 @@ export function openConnection({id, password, retry, roomName}) {
if (config.token) {
throw err;
} else {
return requestAuth();
return requestAuth(roomName);
}
} else {
throw err;

View File

@ -12,23 +12,16 @@
* exrnal_connect.js.
*/
/**
* Gets the token from the URL.
*/
function buildToken(){
var params = getConfigParamsFromUrl();
return params["config.token"] || config.token;
}
/**
* Executes createConnectionExternally function.
*/
(function () {
// FIXME: Add implementation for changing that config from the url for
// consistency
var params = getConfigParamsFromUrl();
//Url params have higher proirity than config params
var url = config.externalConnectUrl;
if(params.hasOwnProperty('config.externalConnectUrl'))
url = params["config.externalConnectUrl"];
/**
* Check if connect from connection.js was executed and executes the handler
@ -64,7 +57,7 @@ function buildToken(){
url += "?room=" + room_name;
var token = buildToken();
var token = params["config.token"] || config.token;
if(token)
url += "&token=" + token;

View File

@ -3,5 +3,8 @@
"bg": "Български",
"de": "Немски",
"tr": "Турски",
"it": "Италиански"
"it": "Италиански",
"fr": "Френски",
"sl": "Словенски",
"sk": "Словашки"
}

View File

@ -6,5 +6,6 @@
"it": "Italienisch",
"fr": "Französisch",
"sl": "Slowenisch",
"sk": "Slowakisch"
"sk": "Slowakisch",
"sv": "Schwedisch"
}

10
lang/languages-es.json Normal file
View File

@ -0,0 +1,10 @@
{
"en": "Inglés",
"bg": "Búlgaro",
"de": "Alemán",
"tr": "Turco",
"it": "Italiano",
"fr": "Francés",
"sl": "Esloveno",
"sk": "Eslovaco"
}

10
lang/languages-oc.json Normal file
View File

@ -0,0 +1,10 @@
{
"en": "",
"bg": "",
"de": "",
"tr": "",
"it": "",
"fr": "",
"sl": "",
"sk": ""
}

View File

@ -3,6 +3,7 @@
"connectionsettings": "Настройки на връзката",
"poweredby": "powered by",
"downloadlogs": "Изтегли логовете",
"feedback": "Кажете ни как върви",
"roomUrlDefaultMsg": "Конференцията се създава...",
"participant": "Участник",
"me": "аз",
@ -18,12 +19,12 @@
"content": "Не е необходимо да сваляте нищо. _app_ работи директно във вашия браузър. Просто споделете адреса на вашата конференция с другите за да започнете."
},
"feature2": {
"title": "",
"title": "Нисък дебит",
"content": "Видео конференциите могат да работят с по-малко от 128Kbps, а аудио конференциите и конференциите с споделен екран дори с по-малко."
},
"feature3": {
"title": "Отворен код",
"content": "__app__ е лицензиран под MIT лиценз. Можете свободно да го изтеглите, използвате, променяте и споделяте според тези лицензи."
"content": "__app__ се разпространява под лицензе Apache. Свободни сте да го сваляте ползвате, променяте и споделяте спрямо правилата на лиценза."
},
"feature4": {
"title": "Неограничен брой потребители",
@ -48,21 +49,22 @@
},
"toolbar": {
"mute": "Включи / Изключи микрофона",
"videomute": "Спри / пусни камерата",
"authenticate": "",
"videomute": "Включи / Изключи камерата",
"authenticate": "Идентификация",
"record": "Запис",
"lock": "Заключи / отключи стаята",
"invite": "Поканване на други",
"chat": "",
"invite": "Покани други",
"chat": "Отвори / затвори чат",
"prezi": "Сподели Prezi",
"etherpad": "Споделяне на документ",
"sharescreen": "Споделяне на екрана",
"etherpad": "Покажи споделения документ",
"sharescreen": "Сподели екрана",
"fullscreen": "Влез / Излез от Пълен екран",
"sip": "Обади се на SIP номер",
"Settings": "Настройки",
"hangup": "Затвори",
"login": "Влез",
"logout": ""
"logout": "Изход",
"dialpad": "Цифров панел"
},
"bottomtoolbar": {
"chat": "Отвори / затвори чат",
@ -79,7 +81,9 @@
"settings": {
"title": "НАСТРОЙКИ",
"update": "Актуализиране",
"name": "Име"
"name": "Име",
"startAudioMuted": "започни без аудио",
"startVideoMuted": "започни без видео"
},
"videothumbnail": {
"editnickname": "Натиснете за да<br/>промените името",
@ -91,7 +95,7 @@
"domute": "Изключи микрофона"
},
"connectionindicator": {
"bitrate": "",
"bitrate": "Дебит:",
"packetloss": "Загуба на пакети:",
"resolution": "Резолюция:",
"less": "Скрий",
@ -106,7 +110,7 @@
"remoteaddress": "Отдалечен адрес:",
"remoteaddress_plural": "Отдалечени адреси:",
"transport": "Транспорт:",
"bandwidth": "",
"bandwidth": "Оценка макс. дебит:",
"na": "Върнете се тук за информацията относно вашата връзка, когато започне конференцията"
},
"notify": {
@ -118,17 +122,19 @@
"focus": "Конферентен фокус",
"focusFail": "__component__ не е на раположения - следващ опит след __ms__ секунди",
"grantedTo": "Даване на роля модератор на __to__!",
"grantedToUnknown": "Даване на роля модератор на $t(somebody)!"
"grantedToUnknown": "Даване на роля модератор на $t(somebody)!",
"muted": "Започвате разговора без звук.",
"mutedTitle": "Звукът ви е спрян!"
},
"dialog": {
"kickMessage": "Бяхте изгонен от срещата!",
"popupError": "Вашия браузър блокира попъп прозорците от този сайт. Моля позволете попъп прозорците от настройките на браузъра и опитайте пак.",
"passwordError": "Тази конференция е защитена с парола. Само създателя и може да промени паролата.",
"passwordError2": "Тази конференция не е защитена с парола. Само създателя и може да сложи парола.",
"joinError": "Не можете да се присъедините към конференцията. Може би имате проблем с конфигурацията на сифурността. Моля обърнете се към администратора на услугата.",
"popupError": "Навигаторът ви блокира попъите. Молим ви да премахнете забраната и да опитате отново.",
"passwordError": "Стаята е защитена с парола. Само собственикът може да я променя или премахва.",
"passwordError2": "Стаята ви не е защитена с парола. Собственикът би могъл да добави парола.",
"connectError": "Опа! Нещо се обърка и не успяхме да се свържем с конференцията.",
"connecting": "",
"error": "",
"connectErrorWithMsg": "Опа! Нещо се обърка и не успяхме да се свържем с конференцията: __msg__",
"connecting": "Свързване",
"error": "Грешка",
"detectext": "Възникна грешка при опит да бъде намерено разширението за споделяне на екран.",
"failtoinstall": "Неуспешна инсталация на разширението за споделяне на екрана.",
"failedpermissions": "Неуспешен опит за получаване на права за използване на микрофон и/или камера.",
@ -149,18 +155,18 @@
"removePreziTitle": "Премахни Prezi",
"removePreziMsg": "Сигурни ли сте, че искате да премахнете Prezi?",
"sharePreziTitle": "Сподели Prezi",
"sharePreziMsg": "Друг участник вече е споделил Prezi. Тази конференция позволява само да се споделя само едно Prezi.",
"Remove": "Премахване",
"Stop": "Спиране",
"AuthMsg": "Нужна е идентификация, за да създадете стая:<br/><b>__room__</b></br> Може да се идентифицирате, за да създадете стая или да изчакате някой друг да го направи.",
"Authenticate": "Идентификация",
"sharePreziMsg": "Prezi string delete",
"Remove": "DELETE DELETE DELETE DELETE DELETE DELETE DELETE DELETE DELETE DELETE ",
"WaitingForHost": "Чакаме домакина ...",
"WaitForHostMsg": "<b>__room__ </b> още не е започнал. Ако вие сте домакина моля идентифицирайте се. В противен случай ще изчакаме домакина.",
"IamHost": "Аз съм домакина",
"Cancel": "Отказ",
"retry": "Повторен опит",
"logoutTitle": "Изход",
"logoutQuestion": "Сигурни ли сте, че искате да излезете и да прекъснете конференцията?",
"sessTerminated": "Сесията е прекъсната.",
"hungUp": "Вие затворихте обаждането.",
"joinAgain": есъединете се отново",
"hungUp": "Затворихте",
"joinAgain": рисъединете се отново",
"Share": "Споделяне",
"preziLinkError": "Моля въведете правилен Prezi линк.",
"Save": "Запазване",
@ -183,11 +189,18 @@
"reservationErrorMsg": "Грешка номер: __code__, съобщение: __msg__",
"password": "парола",
"userPassword": "потребителска парола",
"token": "код за достъп"
"token": "код за достъп",
"tokenAuthFailed": "Неуспешна XMPP автентикация: невалиден тоукън",
"displayNameRequired": "Въведете името си:",
"extensionRequired": "Изисква се разширение: ",
"firefoxExtensionPrompt": "За да споделите екрана си, трябва да инсталирате Firefox разширение. <a href='__url__'>свалете го тук</a> и пробвайте пак!",
"feedbackQuestion": "Как мина разговора?",
"thankYou": "Благодарим, че използвахте __appName__!",
"sorryFeedback": "Много съжаляваме! Бихте ли ни казали повече?"
},
"email": {
"sharedKey": [
"Тази конференция е защитена с парола. Моля използвайте следния код за да се присъедините:",
"Стаята е защитена с парола. Моля използвайте следния пин при влизане:",
"",
"",
"__sharedKey__",
@ -213,6 +226,7 @@
"connection": {
"ERROR": "Грешка",
"CONNECTING": "Свързване",
"RECONNECTING": "Появи се проблем с мрежата. Връзваме се наново...",
"CONNFAIL": "Връзката е неуспешна",
"AUTHENTICATING": "Идентификация",
"AUTHFAIL": "Неуспешна идентификация",
@ -220,5 +234,10 @@
"DISCONNECTED": "Изключен",
"DISCONNECTING": "Прекъсване на връзката",
"ATTACHED": "Прикрепен"
},
"recording": {
"toaster": "В момента записваме!",
"pending": "Записът ви ще започне щом влезе втори участник",
"on": "Записът започна"
}
}

View File

@ -8,12 +8,12 @@
"participant": "Teilnehmer",
"me": "ich",
"speaker": "Sprecher",
"defaultNickname": "Bsp.: __name__",
"defaultPreziLink": "Bsp.: __url__",
"defaultNickname": "Bsp: Heidi Blau",
"defaultLink": "Bsp.: __url__",
"welcomepage": {
"go": "Los",
"roomname": "Raumnamen eingeben",
"disable": "Diese Seite beim nächsten Betreten nicht mehr anzeigen",
"disable": "Diesen Hinweis nicht mehr anzeigen",
"feature1": {
"title": "Einfach zu benutzen",
"content": "Kein Download nötig. __app__ läuft direkt im Browser. Einfach die Konferenzadresse teilen und los geht's."
@ -24,7 +24,7 @@
},
"feature3": {
"title": "Open Source",
"content": "__app__ steht unter der Apache Lizenz. Es steht ihnen frei __app__ gemäss dieser Lizenz herunterzuladen, zu verändern oder zu verbreiten."
"content": "__app__ steht unter der Apache Lizenz. Es steht ihnen frei __app__ gemäß dieser Lizenz herunterzuladen, zu verändern oder zu verbreiten."
},
"feature4": {
"title": "Unbegrenzte Anzahl Benutzer",
@ -40,7 +40,7 @@
},
"feature7": {
"title": "Freigegebene Notizen",
"content": "__app__ verwendent Etherpad, ein Editor zur kollaborativen Bearbeitung von Texten."
"content": "__app__ verwendet Etherpad, ein Editor zur kollaborativen Bearbeitung von Texten."
},
"feature8": {
"title": "Benutzungsstatistiken",
@ -51,12 +51,11 @@
"mute": "Stummschaltung aktivieren / deaktivieren",
"videomute": "Kamera starten / stoppen",
"authenticate": "Anmelden",
"record": "Aufnehmen",
"lock": "Raum schützen / Schutz aufheben",
"invite": "Andere einladen",
"chat": "Chat öffnen / schliessen",
"prezi": "Prezi freigeben",
"chat": "Chat öffnen / schließen",
"etherpad": "Geteiltes Dokument",
"sharedvideo": "Ein YouTube-Video teilen",
"sharescreen": "Bildschirm freigeben",
"fullscreen": "Vollbildmodus aktivieren / deaktivieren",
"sip": "SIP Nummer anrufen",
@ -64,12 +63,15 @@
"hangup": "Auflegen",
"login": "Anmelden",
"logout": "Abmelden",
"dialpad": "Tastenblock anzeigen"
"dialpad": "Tastenblock anzeigen",
"sharedVideoMutedPopup": "Das geteilte Video wurde stumm geschaltet damit mit <br/>den anderen Teilnehmern gesprochen werden kann.",
"micMutedPopup": "Ihr Mikrofon wurde stumm geschaltet damit das<br/>geteilte Video genossen werden kann.",
"unableToUnmutePopup": "Die Stummschaltung kann nicht aufgehoben werden während das geteilte Video abgespielt wird."
},
"bottomtoolbar": {
"chat": "Chat öffnen / schliessen",
"filmstrip": "Videovorschauen anzeigen / verstecken",
"contactlist": "Kontaktliste öffnen / schliessen"
"chat": "Chat öffnen / schließen",
"filmstrip": "Videovorschau anzeigen / verstecken",
"contactlist": "Kontaktliste öffnen / schließen"
},
"chat": {
"nickname": {
@ -83,10 +85,13 @@
"update": "Aktualisieren",
"name": "Name",
"startAudioMuted": "Stumm beitreten",
"startVideoMuted": "Ohne Video beitreten"
"startVideoMuted": "Ohne Video beitreten",
"selectCamera": "Kamera auswählen",
"selectMic": "Mikrofon auswählen",
"followMe": "Follow-me aktivieren"
},
"videothumbnail": {
"editnickname": "Klicken um den Anzeigenamen zu bearbeiten",
"editnickname": "Klicken, um den Anzeigenamen zu bearbeiten",
"moderator": "Besitzer dieser Konferenz",
"videomute": "Teilnehmer hat die Kamera pausiert.",
"mute": "Teilnehmer ist stumm geschaltet",
@ -124,7 +129,7 @@
"grantedTo": "Moderatorenrechte an __to__ vergeben.",
"grantedToUnknown": "Moderatorenrechte an $t(somebody) vergeben.",
"muted": "Der Konferenz wurde stumm beigetreten.",
"mutedTitle": "Stummschaltung aktiv."
"mutedTitle": "Stummschaltung aktiv!"
},
"dialog": {
"kickMessage": "Oh! Sie wurden aus der Konferenz ausgeschlossen.",
@ -137,8 +142,10 @@
"error": "Fehler",
"detectext": "Fehler bei der Erkennung der Bildschirmfreigabeerweiterung.",
"failtoinstall": "Die Bildschirmfreigabeerweiterung konnte nicht installiert werden.",
"failedpermissions": "Die Zugriffsberechtigungen auf das Mikrofon und/oder die Kamera konnte nicht eingeholt werden.",
"failedpermissions": "Die Zugriffsberechtigungen auf das Mikrofon und/oder die Kamera konnten nicht eingeholt werden.",
"bridgeUnavailable": "Die Jitsi Videobridge ist momentan nicht erreichbar. Bitte versuchen Sie es später noch einmal.",
"jicofoUnavailable": "Jicofo ist momentan nicht erreichbar. Bitte versuchen Sie es später noch einmal.",
"maxUsersLimitReached": "Die maximale Teilnehmerzahl dieser Konferenz ist erreicht. Die Konferenz ist voll. Bitte versuchen Sie es später noch einmal.",
"lockTitle": "Sperren fehlgeschlagen",
"lockMessage": "Die Konferenz konnte nicht gesperrt werden.",
"warning": "Warnung",
@ -152,34 +159,35 @@
"defaultError": "Es ist ein Fehler aufgetreten",
"passwordRequired": "Passwort erforderlich",
"Ok": "OK",
"removePreziTitle": "Prezi entfernen",
"removePreziMsg": "Sind Sie sich sicher dass sie Prezi entfernen möchten?",
"sharePreziTitle": "Ein Prezi teilen",
"sharePreziMsg": "Ein anderer Teilnehmer teilt bereits ein Prezi. Diese Konferenz kann nur eine Prezi auf einmal anzeigen.",
"Remove": "Entfernen",
"shareVideoTitle": "Video teilen",
"shareVideoLinkError": "Bitte einen gültigen YouTube-Link angeben.",
"removeSharedVideoTitle": "Freigegebenes Video entfernen",
"removeSharedVideoMsg": "Sind Sie sicher dass Sie das geteilte Video entfernen möchten?",
"alreadySharedVideoMsg": "Ein anderer Teilnehmer teilt bereits ein Video. Diese Konferenz ermöglicht nur ein freigegebenes Video auf einmal.",
"WaitingForHost": "Warten auf den Organisator...",
"WaitForHostMsg": "Die Konferenz <b>__room__</b> hat noch nicht begonnen. Wenn Sie der Organisator sind, melden Sie sich bitte an. Anderenfalls warten Sie bitte bis der Organisator beigetreten ist.",
"WaitForHostMsg": "Die Konferenz <b>__room__</b> hat noch nicht begonnen. Wenn Sie der Organisator sind, melden Sie sich bitte an. Anderenfalls warten Sie bitte, bis der Organisator beigetreten ist.",
"IamHost": "Ich bin der Organisator",
"Cancel": "Abbrechen",
"retry": "Wiederholen",
"logoutTitle": "Abmelden",
"logoutQuestion": "Sind Sie sicher dass Sie sich abmelden und die Konferenz verlassen möchten?",
"logoutQuestion": "Sind Sie sicher, dass Sie sich abmelden und die Konferenz verlassen möchten?",
"sessTerminated": "Sitzung beendet",
"hungUp": "Anruf beendet",
"joinAgain": "Erneut beitreten",
"Share": "Teilen",
"preziLinkError": "Bitte einen gültigen Prezi-Link angeben.",
"Save": "Speichern",
"recording": "",
"recordingToken": "Aufnahme-Token eingeben",
"Dial": "Wählen",
"sipMsg": "Geben Sie eine SIP Nummer ein",
"passwordCheck": "Sind Sie sicher dass Sie das Passwort entfernen möchten?",
"passwordMsg": "Passwort setzen um den Raum zu schützen",
"passwordCheck": "Sind Sie sicher, dass Sie das Passwort entfernen möchten?",
"passwordMsg": "Passwort setzen, um den Raum zu schützen",
"Invite": "Einladen",
"shareLink": "Teilen Sie diesen Link mit jedem den Sie einladen möchten",
"settings1": "Konferenz einrichten",
"settings2": "Teilnehmer treten stummgeschaltet bei",
"settings3": "Nickname erforderlich<br/><br/>Setzen Sie ein Passwort um den Raum zu schützen:",
"settings3": "Nickname erforderlich<br/><br/>Setzen Sie ein Passwort, um den Raum zu schützen:",
"yourPassword": "Ihr Passwort",
"Back": "Zurück",
"serviceUnavailable": "Dienst nicht verfügbar",
@ -191,16 +199,24 @@
"userPassword": "Benutzerpasswort",
"token": "Token",
"tokenAuthFailed": "Anmeldung am XMPP-Server fehlgeschlagen: ungültiges Token",
"displayNameRequired": "Geben Sie Ihren Anzeigenamen ein:",
"displayNameRequired": "Geben Sie Ihren Anzeigenamen ein",
"extensionRequired": "Erweiterung erforderlich:",
"firefoxExtensionPrompt": "Um die Bildschirmfreigabe nutzen zu können, muss eine Firefox-Erweiterung installiert werden. Bitte versuchen Sie es erneut nachdem die <a href='__url__'>Erweiterung installiert</a> wurde.",
"feedbackQuestion": "Wie war der Anruf?",
"thankYou": "Danke für die Verwendung von __appName__!",
"sorryFeedback": "Tut uns leid. Möchten Sie uns mehr mitteilen?"
"sorryFeedback": "Tut uns leid. Möchten Sie uns mehr mitteilen?",
"liveStreaming": "",
"streamKey": "Streamname/-schlüssel",
"startLiveStreaming": "Live-Streaming starten",
"stopStreamingWarning": "Sind Sie sicher dass Sie das Live-Streaming stoppen möchten?",
"stopRecordingWarning": "Sind Sie sicher dass Sie die Aufnahme stoppen möchten?",
"stopLiveStreaming": "Live-Streaming stoppen",
"stopRecording": "Aufnahme stoppen"
},
"\u0005dialog": {},
"email": {
"sharedKey": [
"Diese Konferenz ist Passwortgeschützt. Bitte verwenden Sie diese PIN zum Beitreten:",
"Diese Konferenz ist passwortgeschützt. Bitte verwenden Sie diese PIN zum Beitreten:",
"",
"",
"__sharedKey__",
@ -215,7 +231,7 @@
"Ich möchte dich zu einer eben erstellten __appName__-Konferenz einladen.",
"",
"",
"Bitte klicke auf den folgenden Link um der Konferenz ebenfalls beizutreten:",
"Bitte klicke auf den folgenden Link, um der Konferenz ebenfalls beizutreten:",
"",
"",
"__roomUrl__",
@ -242,8 +258,19 @@
"ATTACHED": "Angehängt"
},
"recording": {
"toaster": "Wird aufgezeichnet",
"pending": "Die Aufzeichnung wird gestartet sobald ein weiterer Teilnehmer beitritt",
"on": "Aufzeichnung wurde gestartet"
"pending": "Die Aufnahme wartet auf den Beitritt eines Teilnehmers...",
"on": "Aufnahme",
"off": "Aufnahme gestoppt",
"failedToStart": "Die Aufnahme konnte nicht gestartet werden",
"buttonTooltip": "Aufnahme starten / stoppen"
},
"liveStreaming": {
"pending": "Live-Stream wird gestartet...",
"on": "Live-Streaming",
"off": "Live-Streaming gestoppt",
"unavailable": "Der Live-Streaming Dienst ist momentan nicht verfügbar. Bitte versuchen Sie es später noch einmal.",
"failedToStart": "Live-Streaming konnte nicht gestartet werden",
"buttonTooltip": "Live-Stream starten / stoppen",
"streamIdRequired": "Bitte Stream-ID eingeben um das Live-Streaming zu starten."
}
}

246
lang/main-es.json Normal file
View File

@ -0,0 +1,246 @@
{
"contactlist": "LISTA DE CONTACTOS",
"connectionsettings": "Ajustes de la conexión",
"poweredby": "impulsado por",
"downloadlogs": "Descargar bitácora",
"feedback": "Danos tu opinión",
"roomUrlDefaultMsg": "Tu conferencia está siendo creada...",
"participant": "Participante",
"me": "yo",
"speaker": "Orador",
"defaultNickname": "ej. __name__",
"defaultPreziLink": "ej. __url__",
"welcomepage": {
"go": "IR",
"roomname": "Introduzca un nombre de sala:",
"disable": "No mostrar esta página en el próximo acceso ",
"feature1": {
"title": "Sencillo de usar",
"content": "No se requiere descargar nada. __app__ funciona directamente con tu navegador. Solo comparte el URL de tu conferencia con otros para comenzar."
},
"feature2": {
"title": "Bajo Ancho de Banda",
"content": "Videoconferencias con múltiples participantes con tan poco como 128 Kbps. Conferencias compartiendo pantalla y audio con mucho menos."
},
"feature3": {
"title": "Código abierto",
"content": "__app__ está licenciado bajo la licencia Apache. Eres libre de descargar, usar, modificar, y compartirlo conforme a esta licencia libre."
},
"feature4": {
"title": "Usuarios ilimitados",
"content": "No hay restricciones artificiales en el número de usuarios o participantes de la conferencia. La capacidad del servidor y ancho de banda son los únicos factores limitantes."
},
"feature5": {
"title": "Compartir pantalla",
"content": "Es fácil de compartir su pantalla con otros. __app__ es ideal para presentaciones en línea, conferencias y sesiones de soporte técnico. "
},
"feature6": {
"title": "Salas seguras",
"content": "¿Necesita un poco de privacidad? Las salas de conferencias __app__ se pueden asegurar con una contraseña con el fin de excluir a los invitados no deseados y evitar interrupciones."
},
"feature7": {
"title": "Notas compartidas",
"content": "__app__ incluye Etherpad, un editor de texto colaborativo, en tiempo real, que es genial para minutas de junta, redactar artículos, y más."
},
"feature8": {
"title": "Estadísticas de uso",
"content": "Conozca acerca de sus usuarios a través de una fácil integración con Piwik, Google Analytics, y otros sistemas de seguimiento y análisis de uso."
}
},
"toolbar": {
"mute": "Silenciar / Activar",
"videomute": "Iniciar / detener cámara",
"authenticate": "Autenticar",
"record": "Grabar",
"lock": "Bloquear / desbloquear sala",
"invite": "Invitar a otros",
"chat": "",
"prezi": "Compartir Prezi",
"etherpad": "Compartir Documento",
"sharescreen": "Compartir pantalla",
"fullscreen": "Entrar / Salir de Pantalla completa",
"sip": "Llamar a un número SIP",
"Settings": "Ajustes",
"hangup": "Colgar",
"login": "Inicio de sesión",
"logout": "",
"dialpad": "Mostrar teclado de llamada"
},
"bottomtoolbar": {
"chat": "Abrir / cerrar chat",
"filmstrip": "Mostrar / ocultar film",
"contactlist": "Abrir / cerrar lista de contactos"
},
"chat": {
"nickname": {
"title": "Indique un nombre en la caja inferior",
"popover": "Seleccione un nombre"
},
"messagebox": "Introduzca texto..."
},
"settings": {
"title": "CONFIGURAR",
"update": "Actualizar",
"name": "Nombre",
"startAudioMuted": "iniciar sin audio",
"startVideoMuted": "iniciar sin video"
},
"videothumbnail": {
"editnickname": "Pica para editar tu <br/>nombre",
"moderator": "El dueño de<br/>esta conferencia",
"videomute": "Participante ha<br/>detenido la cámara.",
"mute": "Participante está silenciado",
"kick": "Expulsar",
"muted": "Silenciado",
"domute": "Silenciar"
},
"connectionindicator": {
"bitrate": "Frecuencia de bits:",
"packetloss": "Pérdida de paquetes:",
"resolution": "Resolución:",
"less": "Mostrar menos",
"more": "Ver más",
"address": "Dirección:",
"remoteport": "Puerto remoto:",
"remoteport_plural": "Puertos remotos:",
"localport": "Puerto local:",
"localport_plural": "Puertos locales:",
"localaddress": "Dirección local:",
"localaddress_plural": "Direcciones locales:",
"remoteaddress": "Dirección remota:",
"remoteaddress_plural": "Direcciones remotas:",
"transport": "Transporte:",
"bandwidth": "Ancho de banda estimado:",
"na": "Volver aquí por información de la conexión, una vez que se inicia la conferencia"
},
"notify": {
"disconnected": "desconectado",
"moderator": "¡Otorgados derechos de moderador! ",
"connected": "conectado",
"somebody": "Alguien",
"me": "Yo",
"focus": "Foco de conferencia",
"focusFail": "__component__ not available - retry en __ms__ sec",
"grantedTo": "¡Derechos de moderador otorgados a __to__!",
"grantedToUnknown": "¡Derechos de moderador otorgados a $t(somebody)!",
"muted": "Haz iniciado la conversación silenciado.",
"mutedTitle": "¡Estás silenciado!"
},
"dialog": {
"kickMessage": "¡Auch! ¡Haz sido expulsado de la reunión!",
"popupError": "Tu navegador está bloqueando las ventanas emergentes de este sitio. Por favor, activa las ventanas emergentes en la configuración de seguridad de su navegador y vuelva a intentarlo.",
"passwordError": "Esta conversación está protegido con una contraseña. Sólo el propietario de la conferencia puede establecer una contraseña.",
"passwordError2": "Esta conversación está protegido con una contraseña. Sólo el propietario de la conferencia puede establecer una contraseña.",
"connectError": "¡Uups! Algo salió mal y no pudimos conectarnos a la conferencia.",
"connectErrorWithMsg": "¡Uups! Algo salió mal y no pudimos conectarnos a la conferencia: __msg__",
"connecting": "",
"error": "",
"detectext": "Error al tratar de detectar la extensión de compartir escritorio.",
"failtoinstall": "Fallo al instalar la extensión de compartir escritorio",
"failedpermissions": "Fallo al obtener permisos para utilizar el micrófono y/o cámara de tu equipo.",
"bridgeUnavailable": "Videobridge Jitsi no está actualmente disponible. ¡Por favor intente después!",
"lockTitle": "Fallo al bloquear",
"lockMessage": "No se pudo bloquear la conferencia.",
"warning": "Advertencia",
"passwordNotSupported": "No se soportan contraseñas para la sala. ",
"sorry": "¡Perdón!",
"internalError": "Error interno de la aplicación [setRemoteDescription]",
"unableToSwitch": "No se puede activar transmisión de video",
"SLDFailure": "¡Ups! Algo salió mal y no se logró silenciar! (Falla de SLD)",
"SRDFailure": "¡Ups! ¡Algo salió mal y no se logró detener el video! (Falla SRD)",
"oops": "¡Ups!",
"defaultError": "Hubo algún tipo de error",
"passwordRequired": "Se necesita contraseña",
"Ok": "Aceptar",
"removePreziTitle": "Eliminar Prezi",
"removePreziMsg": "¿Estas seguro que deseas eliminar tu Prezi?",
"sharePreziTitle": "Compartir un Prezi",
"sharePreziMsg": "Otro participante ya está compartiendo un Prezi. Esta conferencia permite sólo un Prezi a la vez.",
"Remove": "Eliminar",
"WaitingForHost": "Esperando al anfitrión...",
"WaitForHostMsg": "La conferencia <b>__room__ </b> aún no inicia. Si usted es el anfitrión, por favor autentíquese. De lo contrario, por favor espere a que el anfitrión llegue.",
"IamHost": "Yo soy el anfitrión",
"Cancel": "Cancelar",
"retry": "Reintentar",
"logoutTitle": "Cerrar sesión",
"logoutQuestion": "¿Está seguro que quiere salir y detener la conferencia?",
"sessTerminated": "Sesión finalizada",
"hungUp": "Colgaste",
"joinAgain": "Unirse de nuevo",
"Share": "Compartir",
"preziLinkError": "Por favor indique una liga Prezi correcta. ",
"Save": "Guardar",
"recordingToken": "Introduzca el token de grabación",
"Dial": "Marcar",
"sipMsg": "Introduzca número SIP",
"passwordCheck": "¿Realmente quieres eliminar tu contraseña?",
"passwordMsg": "Indica una contraseña para bloquear tu sala",
"Invite": "Invitar",
"shareLink": "Comparte este enlace con las personas que deseas invitar",
"settings1": "Configure su conferencia",
"settings2": "Los participantes se unieron silenciados",
"settings3": "Solicita nombres <br/> <br/>Establecer una contraseña para bloquear la sala:",
"yourPassword": "tu contraseña",
"Back": "Atrás",
"serviceUnavailable": "Servicio no disponible",
"gracefulShutdown": "Nuestro servicio está detenido por mantenimiento. Por favor, inténtelo de nuevo más tarde.",
"Yes": "Sí",
"reservationError": "Error de sistema de reservación",
"reservationErrorMsg": "Código de error: __code__, message: __msg__",
"password": "contraseña",
"userPassword": "contraseña de usuario",
"token": "token",
"tokenAuthFailed": "Error al autenticar con el servidor XMPP: token inválido",
"displayNameRequired": "Por favor escriba su nombre",
"extensionRequired": "Extensión requerida",
"firefoxExtensionPrompt": "Es necesario instalar una extensión para Firefox con el fin de utilizar la pantalla compartida. Por favor, inténtelo de nuevo después de que lo obtenga de aquí <a href='__url__'> </a>!",
"feedbackQuestion": "¿Como fue su llamada?",
"thankYou": "Gracias por usar __appName__!",
"sorryFeedback": " Sentimos escuchar eso. ¿Quieres decirnos algo más?"
},
"email": {
"sharedKey": [
"Esta conferencia está protegida con contraseña. Utiliza el siguiente pin cuando te unas: ",
"",
"",
"__sharedKey__",
"",
""
],
"subject": "Invitación a a __appName__ (__conferenceName__)",
"body": [
"Ey allí, quiero invitarte a una a conferencia __appName__ que acabo de crear.",
"",
"",
"Por favor pica en la liga siguiente para unirte a la conferencia.",
"",
"",
"__roomUrl__",
"",
"",
"__sharedKeyText__",
"Nota que __appName__ es soportada solo por __supportedBrowsers__, por lo que debes usar uno de esos navegadores.",
"",
"",
"Talk to you in a sec!"
],
"and": "y"
},
"connection": {
"ERROR": "Error",
"CONNECTING": "Conectando",
"RECONNECTING": "Ocurrió un problema en la red. Reconectando ...",
"CONNFAIL": "Conexión fallida",
"AUTHENTICATING": "Autenticando",
"AUTHFAIL": "Falló la autenticación",
"CONNECTED": "Conectado",
"DISCONNECTED": "Desconectado",
"DISCONNECTING": "Desconectando",
"ATTACHED": "Adjunto"
},
"recording": {
"toaster": "¡Actualmente Grabando! ",
"pending": "Tu grabación se iniciará tan pronto como otro participante se una",
"on": "La grabación ha iniciado"
}
}

224
lang/main-oc.json Normal file
View File

@ -0,0 +1,224 @@
{
"contactlist": "",
"connectionsettings": "",
"poweredby": "",
"downloadlogs": "",
"feedback": "",
"roomUrlDefaultMsg": "",
"participant": "",
"me": "",
"speaker": "",
"defaultNickname": "",
"defaultPreziLink": "",
"welcomepage": {
"go": "",
"roomname": "",
"disable": "",
"feature1": {
"title": "",
"content": ""
},
"feature2": {
"title": "",
"content": ""
},
"feature3": {
"title": "",
"content": ""
},
"feature4": {
"title": "",
"content": ""
},
"feature5": {
"title": "",
"content": ""
},
"feature6": {
"title": "",
"content": ""
},
"feature7": {
"title": "",
"content": ""
},
"feature8": {
"title": "",
"content": ""
}
},
"toolbar": {
"mute": "",
"videomute": "",
"authenticate": "",
"record": "",
"lock": "",
"invite": "",
"chat": "",
"prezi": "",
"etherpad": "",
"sharescreen": "",
"fullscreen": "",
"sip": "",
"Settings": "",
"hangup": "",
"login": "",
"logout": "",
"dialpad": ""
},
"bottomtoolbar": {
"chat": "",
"filmstrip": "",
"contactlist": ""
},
"chat": {
"nickname": {
"title": "",
"popover": ""
},
"messagebox": ""
},
"settings": {
"title": "",
"update": "",
"name": "",
"startAudioMuted": "",
"startVideoMuted": ""
},
"videothumbnail": {
"editnickname": "",
"moderator": "",
"videomute": "",
"mute": "",
"kick": "",
"muted": "",
"domute": ""
},
"connectionindicator": {
"bitrate": "",
"packetloss": "",
"resolution": "",
"less": "",
"more": "",
"address": "",
"remoteport": "",
"remoteport_plural": "",
"localport": "",
"localport_plural": "",
"localaddress": "",
"localaddress_plural": "",
"remoteaddress": "",
"remoteaddress_plural": "",
"transport": "",
"bandwidth": "",
"na": ""
},
"notify": {
"disconnected": "",
"moderator": "",
"connected": "",
"somebody": "",
"me": "",
"focus": "",
"focusFail": "",
"grantedTo": "",
"grantedToUnknown": "",
"muted": "",
"mutedTitle": ""
},
"dialog": {
"kickMessage": "",
"popupError": "",
"passwordError": "",
"passwordError2": "",
"connectError": "",
"connectErrorWithMsg": "",
"connecting": "",
"error": "",
"detectext": "",
"failtoinstall": "",
"failedpermissions": "",
"bridgeUnavailable": "",
"lockTitle": "",
"lockMessage": "",
"warning": "",
"passwordNotSupported": "",
"sorry": "",
"internalError": "",
"unableToSwitch": "",
"SLDFailure": "",
"SRDFailure": "",
"oops": "",
"defaultError": "",
"passwordRequired": "",
"Ok": "",
"removePreziTitle": "",
"removePreziMsg": "",
"sharePreziTitle": "",
"sharePreziMsg": "",
"Remove": "",
"WaitingForHost": "",
"WaitForHostMsg": "",
"IamHost": "",
"Cancel": "",
"retry": "",
"logoutTitle": "",
"logoutQuestion": "",
"sessTerminated": "",
"hungUp": "",
"joinAgain": "",
"Share": "",
"preziLinkError": "",
"Save": "",
"recordingToken": "",
"Dial": "",
"sipMsg": "",
"passwordCheck": "",
"passwordMsg": "",
"Invite": "",
"shareLink": "",
"settings1": "",
"settings2": "",
"settings3": "",
"yourPassword": "",
"Back": "",
"serviceUnavailable": "",
"gracefulShutdown": "",
"Yes": "",
"reservationError": "",
"reservationErrorMsg": "",
"password": "",
"userPassword": "",
"token": "",
"tokenAuthFailed": "",
"displayNameRequired": "",
"extensionRequired": "",
"firefoxExtensionPrompt": "",
"feedbackQuestion": "",
"thankYou": "",
"sorryFeedback": ""
},
"email": {
"sharedKey": "",
"subject": "",
"body": "",
"and": ""
},
"connection": {
"ERROR": "",
"CONNECTING": "",
"RECONNECTING": "",
"CONNFAIL": "",
"AUTHENTICATING": "",
"AUTHFAIL": "",
"CONNECTED": "",
"DISCONNECTED": "",
"DISCONNECTING": "",
"ATTACHED": ""
},
"recording": {
"toaster": "",
"pending": "",
"on": ""
}
}

View File

@ -130,7 +130,7 @@ function processMessage(event) {
*/
function isEnabled () {
let hash = location.hash;
return hash && hash.indexOf("external") > -1 && window.postMessage;
return hash && hash.indexOf("external=true") > -1 && window.postMessage;
}
/**

View File

@ -29,7 +29,6 @@ var JitsiPopover = require("./util/JitsiPopover");
var Feedback = require("./Feedback");
import FollowMe from "../FollowMe";
import Recorder from "../recorder/Recorder";
var eventEmitter = new EventEmitter();
UI.eventEmitter = eventEmitter;
@ -242,11 +241,6 @@ UI.initConference = function () {
//if local role changes buttons state will be again updated
UI.updateLocalRole(false);
// Initialise the recorder handler. We're doing this explicitly before
// calling showToolbar, because the recorder may want to disable all
// toolbars.
new Recorder(APP.conference);
// Once we've joined the muc show the toolbar
ToolbarToggler.showToolbar();
@ -1122,9 +1116,9 @@ UI.updateDevicesAvailability = function (id, devices) {
* @param {string} url video url
* @param {string} attributes
*/
UI.showSharedVideo = function (id, url, attributes) {
UI.onSharedVideoStart = function (id, url, attributes) {
if (sharedVideoManager)
sharedVideoManager.showSharedVideo(id, url, attributes);
sharedVideoManager.onSharedVideoStart(id, url, attributes);
};
/**
@ -1133,9 +1127,9 @@ UI.showSharedVideo = function (id, url, attributes) {
* @param {string} url video url
* @param {string} attributes
*/
UI.updateSharedVideo = function (id, url, attributes) {
UI.onSharedVideoUpdate = function (id, url, attributes) {
if (sharedVideoManager)
sharedVideoManager.updateSharedVideo(id, url, attributes);
sharedVideoManager.onSharedVideoUpdate(id, url, attributes);
};
/**
@ -1143,9 +1137,9 @@ UI.updateSharedVideo = function (id, url, attributes) {
* @param {string} id the id of the sender of the command
* @param {string} attributes
*/
UI.stopSharedVideo = function (id, attributes) {
UI.onSharedVideoStop = function (id, attributes) {
if (sharedVideoManager)
sharedVideoManager.stopSharedVideo(id, attributes);
sharedVideoManager.onSharedVideoStop(id, attributes);
};
module.exports = UI;

View File

@ -16,6 +16,11 @@
*/
import UIEvents from "../../../service/UI/UIEvents";
import UIUtil from '../util/UIUtil';
import VideoLayout from '../videolayout/VideoLayout';
import Feedback from '../Feedback.js';
import Toolbar from '../toolbars/Toolbar';
import BottomToolbar from '../toolbars/BottomToolbar';
/**
* Indicates if the recording button should be enabled.
@ -218,6 +223,17 @@ var Recording = {
this.currentState = Status.UNAVAILABLE;
this.initRecordingButton(recordingType);
// If I am a recorder then I publish my recorder custom role to notify
// everyone.
if (config.iAmRecorder) {
VideoLayout.enableDeviceAvailabilityIcons(
APP.conference.localId, false);
VideoLayout.setLocalVideoVisible(false);
Feedback.enableFeedback(false);
Toolbar.enable(false);
BottomToolbar.enable(false);
}
},
/**

View File

@ -30,12 +30,18 @@ export default class SharedVideoManager {
}
/**
* Indicates if the player volume is currently on.
* Indicates if the player volume is currently on. This will return true if
* we have an available player, which is currently in a PLAYING state,
* which isn't muted and has it's volume greater than 0.
*
* @returns {*|player|boolean}
* @returns {boolean} indicating if the volume of the shared video is
* currently on.
*/
isSharedVideoVolumeOn() {
return (this.player && this.player.getVolume() > 0);
return (this.player
&& this.player.getPlayerState() === YT.PlayerState.PLAYING
&& !this.player.isMuted()
&& this.player.getVolume() > 0);
}
/**
@ -73,13 +79,14 @@ export default class SharedVideoManager {
}
/**
* Shows the player component and starts the checking function
* that will be sending updates, if we are the one shared the video
* Shows the player component and starts the process that will be sending
* updates, if we are the one shared the video.
*
* @param id the id of the sender of the command
* @param url the video url
* @param attributes
*/
showSharedVideo (id, url, attributes) {
onSharedVideoStart (id, url, attributes) {
if (this.isSharedVideoShown)
return;
@ -91,8 +98,10 @@ export default class SharedVideoManager {
// the owner of the video
this.from = id;
this.mutedWithUserInteraction = APP.conference.isLocalAudioMuted();
//listen for local audio mute events
this.localAudioMutedListener = this.localAudioMuted.bind(this);
this.localAudioMutedListener = this.onLocalAudioMuted.bind(this);
this.emitter.on(UIEvents.AUDIO_MUTED, this.localAudioMutedListener);
// This code loads the IFrame Player API code asynchronously.
@ -147,24 +156,30 @@ export default class SharedVideoManager {
}
};
/**
* Indicates that a change in state has occurred for the shared video.
* @param event the event notifying us of the change
*/
window.onPlayerStateChange = function(event) {
if (event.data == YT.PlayerState.PLAYING) {
self.playerPaused = false;
self.player = event.target;
if(self.initialAttributes)
{
self.processAttributes(
self.player, self.initialAttributes, self.playerPaused);
// If a network update has occurred already now is the
// time to process it.
self.processVideoUpdate(
self.player,
self.initialAttributes);
self.initialAttributes = null;
}
self.updateCheck();
self.smartAudioMute();
} else if (event.data == YT.PlayerState.PAUSED) {
self.playerPaused = true;
self.updateCheck(true);
self.smartAudioUnmute();
}
self.fireSharedVideoEvent(event.data == YT.PlayerState.PAUSED);
};
/**
@ -174,7 +189,7 @@ export default class SharedVideoManager {
window.onVideoProgress = function (event) {
let state = event.target.getPlayerState();
if (state == YT.PlayerState.PAUSED) {
self.updateCheck(true);
self.fireSharedVideoEvent(true);
}
};
@ -183,18 +198,14 @@ export default class SharedVideoManager {
* @param event
*/
window.onVolumeChange = function (event) {
self.updateCheck();
self.fireSharedVideoEvent();
// let's check, if player is not muted lets mute locally
if(event.data.volume > 0 && !event.data.muted
&& !APP.conference.isLocalAudioMuted()) {
self.emitter.emit(UIEvents.AUDIO_MUTED, true, false);
self.showMicMutedPopup(true);
if(event.data.volume > 0 && !event.data.muted) {
self.smartAudioMute();
}
else if (!self.mutedWithUserInteraction
&& (event.data.volume <=0 || event.data.muted)
&& APP.conference.isLocalAudioMuted()) {
self.emitter.emit(UIEvents.AUDIO_MUTED, false, false);
else if (event.data.volume <=0 || event.data.muted) {
self.smartAudioUnmute();
}
};
@ -226,7 +237,7 @@ export default class SharedVideoManager {
// we need to continuously send the player current time position
if(APP.conference.isLocalId(self.from)) {
self.intervalId = setInterval(
self.updateCheck.bind(self),
self.fireSharedVideoEvent.bind(self),
updateInterval);
}
};
@ -242,29 +253,37 @@ export default class SharedVideoManager {
* Process attributes, whether player needs to be paused or seek.
* @param player the player to operate over
* @param attributes the attributes with the player state we want
* @param playerPaused current saved state for the player
*/
processAttributes (player, attributes, playerPaused)
processVideoUpdate (player, attributes)
{
if(!attributes)
return;
if (attributes.state == 'playing') {
this.processTime(player, attributes, playerPaused);
let isPlayerPaused
= (this.player.getPlayerState() === YT.PlayerState.PAUSED);
// lets check the volume
if (attributes.volume !== undefined
&& player.getVolume() != attributes.volume
&& (APP.conference.isLocalAudioMuted()
|| !this.mutedWithUserInteraction)) {
// If our player is currently paused force the seek.
this.processTime(player, attributes, isPlayerPaused);
// Process mute.
let isAttrMuted = (attributes.muted === "true");
if (player.isMuted() !== isAttrMuted) {
this.smartPlayerMute(isAttrMuted, true);
}
// Process volume
if (!isAttrMuted
&& attributes.volume !== undefined
&& player.getVolume() != attributes.volume) {
player.setVolume(attributes.volume);
console.info("Player change of volume:" + attributes.volume);
this.showSharedVideoMutedPopup(false);
}
if(playerPaused)
if (isPlayerPaused)
player.playVideo();
} else if (attributes.state == 'pause') {
@ -272,8 +291,6 @@ export default class SharedVideoManager {
player.pauseVideo();
this.processTime(player, attributes, true);
} else if (attributes.state == 'stop') {
this.stopSharedVideo(this.from);
}
}
@ -307,7 +324,7 @@ export default class SharedVideoManager {
/**
* Checks current state of the player and fire an event with the values.
*/
updateCheck(sendPauseEvent)
fireSharedVideoEvent(sendPauseEvent)
{
// ignore update checks if we are not the owner of the video
// or there is still no player defined or we are stopped
@ -328,7 +345,8 @@ export default class SharedVideoManager {
this.emitter.emit(UIEvents.UPDATE_SHARED_VIDEO,
this.url, 'playing',
this.player.getCurrentTime(),
this.player.isMuted() ? 0 : this.player.getVolume());
this.player.isMuted(),
this.player.getVolume());
}
}
@ -339,21 +357,21 @@ export default class SharedVideoManager {
* @param url the video url
* @param attributes
*/
updateSharedVideo (id, url, attributes) {
onSharedVideoUpdate (id, url, attributes) {
// if we are sending the event ignore
if(APP.conference.isLocalId(this.from)) {
return;
}
if(!this.isSharedVideoShown) {
this.showSharedVideo(id, url, attributes);
this.onSharedVideoStart(id, url, attributes);
return;
}
if(!this.player)
this.initialAttributes = attributes;
else {
this.processAttributes(this.player, attributes, this.playerPaused);
this.processVideoUpdate(this.player, attributes);
}
}
@ -363,14 +381,14 @@ export default class SharedVideoManager {
* left and we want to remove video if the user sharing it left).
* @param id the id of the sender of the command
*/
stopSharedVideo (id, attributes) {
onSharedVideoStop (id, attributes) {
if (!this.isSharedVideoShown)
return;
if(this.from !== id)
return;
if(!this.player){
if(!this.player) {
// if there is no error in the player till now,
// store the initial attributes
if (!this.errorInPlayer) {
@ -388,8 +406,6 @@ export default class SharedVideoManager {
this.localAudioMutedListener);
this.localAudioMutedListener = null;
this.showSharedVideoMutedPopup(false);
VideoLayout.removeParticipantContainer(this.url);
VideoLayout.showLargeVideoContainer(SHARED_VIDEO_CONTAINER_TYPE, false)
@ -405,6 +421,7 @@ export default class SharedVideoManager {
this.errorInPlayer.destroy();
this.errorInPlayer = null;
}
this.smartAudioUnmute();
// revert to original behavior (prevents pausing
// for participants not sharing the video to pause it)
$("#sharedVideo").css("pointer-events","auto");
@ -421,20 +438,67 @@ export default class SharedVideoManager {
* @param {boolean} indicates if this mute was a result of user interaction,
* i.e. pressing the mute button or it was programatically triggerred
*/
localAudioMuted (muted, userInteraction) {
onLocalAudioMuted (muted, userInteraction) {
if(!this.player)
return;
if (muted) {
this.mutedWithUserInteraction = userInteraction;
return;
}
else if (this.player.getPlayerState() !== YT.PlayerState.PAUSED) {
this.smartPlayerMute(true, false);
// Check if we need to update other participants
this.fireSharedVideoEvent();
}
}
/**
* Mutes / unmutes the player.
* @param mute true to mute the shared video, false - otherwise.
* @param {boolean} Indicates if this mute is a consequence of a network
* video update or is called locally.
*/
smartPlayerMute(mute, isVideoUpdate) {
if (!this.player.isMuted() && mute) {
this.player.mute();
if (isVideoUpdate)
this.smartAudioUnmute();
}
else if (this.player.isMuted() && !mute) {
this.player.unMute();
if (isVideoUpdate)
this.smartAudioMute();
}
// if we are un-muting and player is not muted, lets muted
// to not pollute the conference
if (this.player.getVolume() > 0 || !this.player.isMuted()) {
this.player.setVolume(0);
this.showSharedVideoMutedPopup(true);
this.showSharedVideoMutedPopup(mute);
}
/**
* Smart mike unmute. If the mike is currently muted and it wasn't muted
* by the user via the mike button and the volume of the shared video is on
* we're unmuting the mike automatically.
*/
smartAudioUnmute() {
if (APP.conference.isLocalAudioMuted()
&& !this.mutedWithUserInteraction
&& !this.isSharedVideoVolumeOn()) {
this.emitter.emit(UIEvents.AUDIO_MUTED, false, false);
this.showMicMutedPopup(false);
}
}
/**
* Smart mike mute. If the mike isn't currently muted and the shared video
* volume is on we mute the mike.
*/
smartAudioMute() {
if (!APP.conference.isLocalAudioMuted()
&& this.isSharedVideoVolumeOn()) {
this.emitter.emit(UIEvents.AUDIO_MUTED, true, false);
this.showMicMutedPopup(true);
}
}

View File

@ -196,6 +196,15 @@
() => {selector.css({opacity: 0});}
);
}
},
/**
* Parses the given cssValue as an Integer. If the value is not a number
* we return 0 instead of NaN.
* @param cssValue the string value we obtain when querying css properties
*/
parseCssInt(cssValue) {
return parseInt(cssValue) || 0;
}
};

View File

@ -85,20 +85,32 @@ const FilmStrip = {
*/
let videoAreaAvailableWidth
= UIUtil.getAvailableVideoWidth(isSideBarVisible)
- parseInt(this.filmStrip.css('right'), 10)
- parseInt(this.filmStrip.css('paddingLeft'), 10)
- parseInt(this.filmStrip.css('paddingRight'), 10)
- parseInt(this.filmStrip.css('borderLeftWidth'), 10)
- parseInt(this.filmStrip.css('borderRightWidth'), 10) - 5;
- UIUtil.parseCssInt(this.filmStrip.css('right'), 10)
- UIUtil.parseCssInt(this.filmStrip.css('paddingLeft'), 10)
- UIUtil.parseCssInt(this.filmStrip.css('paddingRight'), 10)
- UIUtil.parseCssInt(this.filmStrip.css('borderLeftWidth'), 10)
- UIUtil.parseCssInt(this.filmStrip.css('borderRightWidth'), 10)
- 5;
let availableWidth = Math.floor(
let availableWidth = videoAreaAvailableWidth;
// If the number of videos is 0 or undefined we don't need to calculate
// further.
if (numvids)
availableWidth = Math.floor(
(videoAreaAvailableWidth - numvids * (
parseInt(localVideoContainer.css('borderLeftWidth'), 10)
+ parseInt(localVideoContainer.css('borderRightWidth'), 10)
+ parseInt(localVideoContainer.css('paddingLeft'), 10)
+ parseInt(localVideoContainer.css('paddingRight'), 10)
+ parseInt(localVideoContainer.css('marginLeft'), 10)
+ parseInt(localVideoContainer.css('marginRight'), 10)))
UIUtil.parseCssInt(
localVideoContainer.css('borderLeftWidth'), 10)
+ UIUtil.parseCssInt(
localVideoContainer.css('borderRightWidth'), 10)
+ UIUtil.parseCssInt(
localVideoContainer.css('paddingLeft'), 10)
+ UIUtil.parseCssInt(
localVideoContainer.css('paddingRight'), 10)
+ UIUtil.parseCssInt(
localVideoContainer.css('marginLeft'), 10)
+ UIUtil.parseCssInt(
localVideoContainer.css('marginRight'), 10)))
/ numvids);
let maxHeight
@ -155,7 +167,12 @@ const FilmStrip = {
selector += ':visible';
}
return this.filmStrip.children(selector);
// Exclude the local video container if it has been hidden.
if ($("#localVideoContainer").hasClass("hidden"))
return this.filmStrip.children(selector)
.not("#localVideoContainer");
else
return this.filmStrip.children(selector);
}
};

View File

@ -446,7 +446,15 @@ export default class LargeVideoManager {
let container = this.getContainer(this.state);
container.hide().then(() => {
// Include hide()/fadeOut only if we're switching between users
let preUpdate;
if (this.newStreamData.id != this.id) {
preUpdate = container.hide();
} else {
preUpdate = Promise.resolve();
}
preUpdate.then(() => {
let {id, stream, videoType, resolve} = this.newStreamData;
this.newStreamData = null;

View File

@ -1,4 +1,4 @@
/* global $, interfaceConfig, APP, JitsiMeetJS */
/* global $, config, interfaceConfig, APP, JitsiMeetJS */
import ConnectionIndicator from "./ConnectionIndicator";
import UIUtil from "../util/UIUtil";
import UIEvents from "../../../service/UI/UIEvents";
@ -200,4 +200,26 @@ LocalVideo.prototype.changeVideo = function (stream) {
stream.on(TrackEvents.LOCAL_TRACK_STOPPED, endedHandler);
};
/**
* Shows or hides the local video container.
* @param {boolean} true to make the local video container visible, false
* otherwise
*/
LocalVideo.prototype.setVisible = function(visible) {
// We toggle the hidden class as an indication to other interested parties
// that this container has been hidden on purpose.
$("#localVideoContainer").toggleClass("hidden");
// We still show/hide it as we need to overwrite the style property if we
// want our action to take effect. Toggling the display property through
// the above css class didn't succeed in overwriting the style.
if (visible) {
$("#localVideoContainer").show();
}
else {
$("#localVideoContainer").hide();
}
};
export default LocalVideo;

View File

@ -207,7 +207,7 @@ var VideoLayout = {
if (APP.conference.isLocalId(id)) {
video = localVideoThumbnail;
}
else if (remoteVideos[id]) {
else {
video = remoteVideos[id];
}
@ -215,6 +215,14 @@ var VideoLayout = {
video.enableDeviceAvailabilityIcons(enable);
},
/**
* Shows/hides local video.
* @param {boolean} true to make the local video visible, false - otherwise
*/
setLocalVideoVisible(visible) {
localVideoThumbnail.setVisible(visible);
},
/**
* Checks if removed video is currently displayed and tries to display
* another one instead.
@ -277,7 +285,12 @@ var VideoLayout = {
onRemoteStreamAdded (stream) {
let id = stream.getParticipantId();
remoteVideos[id].addRemoteStreamElement(stream);
let remoteVideo = remoteVideos[id];
if (!remoteVideo)
return;
remoteVideo.addRemoteStreamElement(stream);
// if track is muted make sure we reflect that
if(stream.isMuted())
@ -360,7 +373,8 @@ var VideoLayout = {
},
/**
* Creates a remote video for participant for the given id.
* Creates a participant container for the given id and smallVideo.
*
* @param id the id of the participant to add
* @param {SmallVideo} smallVideo optional small video instance to add as a
* remote video, if undefined RemoteVideo will be created
@ -404,7 +418,11 @@ var VideoLayout = {
this.isLargeContainerTypeVisible(VIDEO_CONTAINER_TYPE)) ||
pinnedId === resourceJid ||
(!pinnedId && resourceJid &&
currentDominantSpeaker === resourceJid)) {
currentDominantSpeaker === resourceJid) ||
/* Playback started while we're on the stage - may need to update
video source with the new stream */
this.isCurrentlyOnLarge(resourceJid)) {
this.updateLargeVideo(resourceJid, true);
}
},
@ -413,7 +431,9 @@ var VideoLayout = {
* Shows the presence status message for the given video.
*/
setPresenceStatus (id, statusMsg) {
remoteVideos[id].setPresenceStatus(statusMsg);
let remoteVideo = remoteVideos[id];
if (remoteVideo)
remoteVideo.setPresenceStatus(statusMsg);
},
/**
@ -428,13 +448,17 @@ var VideoLayout = {
APP.conference.listMembers().forEach(function (member) {
let id = member.getId();
let remoteVideo = remoteVideos[id];
if (!remoteVideo)
return;
if (member.isModerator()) {
remoteVideos[id].removeRemoteVideoMenu();
remoteVideos[id].createModeratorIndicatorElement();
remoteVideo.removeRemoteVideoMenu();
remoteVideo.createModeratorIndicatorElement();
} else if (isModerator) {
// We are moderator, but user is not - add menu
if ($(`#remote_popupmenu_${id}`).length <= 0) {
remoteVideos[id].addRemoteVideoMenu();
remoteVideo.addRemoteVideoMenu();
}
}
});
@ -482,9 +506,13 @@ var VideoLayout = {
if (APP.conference.isLocalId(id)) {
localVideoThumbnail.showAudioIndicator(isMuted);
} else {
remoteVideos[id].showAudioIndicator(isMuted);
let remoteVideo = remoteVideos[id];
if (!remoteVideo)
return;
remoteVideo.showAudioIndicator(isMuted);
if (APP.conference.isModerator) {
remoteVideos[id].updateRemoteVideoMenu(isMuted);
remoteVideo.updateRemoteVideoMenu(isMuted);
}
}
},
@ -496,8 +524,9 @@ var VideoLayout = {
if (APP.conference.isLocalId(id)) {
localVideoThumbnail.setMutedView(value);
} else {
var remoteVideo = remoteVideos[id];
remoteVideo.setMutedView(value);
let remoteVideo = remoteVideos[id];
if (remoteVideo)
remoteVideo.setMutedView(value);
}
if (this.isCurrentlyOnLarge(id)) {
@ -514,7 +543,9 @@ var VideoLayout = {
APP.conference.isLocalId(id)) {
localVideoThumbnail.setDisplayName(displayName);
} else {
remoteVideos[id].setDisplayName(displayName, status);
let remoteVideo = remoteVideos[id];
if (remoteVideo)
remoteVideo.setDisplayName(displayName, status);
}
},
@ -636,9 +667,13 @@ var VideoLayout = {
console.error("No remote video for: " + resourceJid);
isReceived = false;
} else if (resourceJid &&
//TOFIX: smallVideo may be undefined
smallVideo.isVisible() &&
lastNEndpoints.indexOf(resourceJid) < 0 &&
localLastNSet.indexOf(resourceJid) >= 0) {
// TOFIX: if we're here we already know that the smallVideo
// exists. Look at the previous FIX above.
if (smallVideo)
smallVideo.showPeerContainer('avatar');
else if (!APP.conference.isLocalId(resourceJid))
@ -739,8 +774,9 @@ var VideoLayout = {
* @param object the stats data
*/
updateConnectionStats (id, percent, object) {
if (remoteVideos[id]) {
remoteVideos[id].updateStatsIndicator(percent, object);
let remoteVideo = remoteVideos[id];
if (remoteVideo) {
remoteVideo.updateStatsIndicator(percent, object);
}
},
@ -749,15 +785,19 @@ var VideoLayout = {
* @param id
*/
hideConnectionIndicator (id) {
remoteVideos[id].hideConnectionIndicator();
let remoteVideo = remoteVideos[id];
if (remoteVideo)
remoteVideo.hideConnectionIndicator();
},
/**
* Hides all the indicators
*/
hideStats () {
for(var video in remoteVideos) {
remoteVideos[video].hideIndicator();
for (var video in remoteVideos) {
let remoteVideo = remoteVideos[video];
if (remoteVideo)
remoteVideo.hideIndicator();
}
localVideoThumbnail.hideIndicator();
},

View File

@ -1,95 +0,0 @@
/* global config, APP */
/*
* 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 VideoLayout from '../UI/videolayout/VideoLayout';
import Feedback from '../UI/Feedback.js';
import Toolbar from '../UI/toolbars/Toolbar';
import BottomToolbar from '../UI/toolbars/BottomToolbar';
const _RECORDER_CUSTOM_ROLE = "recorder-role";
class Recorder {
/**
* Initializes a new {Recorder} instance.
*
* @param conference the {conference} which is to transport
* {Recorder}-related information between participants
*/
constructor (conference) {
this._conference = conference;
// If I am a recorder then I publish my recorder custom role to notify
// everyone.
if (config.iAmRecorder) {
VideoLayout.enableDeviceAvailabilityIcons(conference.localId, true);
this._publishMyRecorderRole();
Feedback.enableFeedback(false);
Toolbar.enable(false);
BottomToolbar.enable(false);
}
// Listen to "CUSTOM_ROLE" commands.
this._conference.commands.addCommandListener(
this._conference.commands.defaults.CUSTOM_ROLE,
this._onCustomRoleCommand.bind(this));
}
/**
* Publish the recorder custom role.
* @private
*/
_publishMyRecorderRole () {
var conference = this._conference;
var commands = conference.commands;
commands.removeCommand(commands.defaults.CUSTOM_ROLE);
var self = this;
commands.sendCommandOnce(
commands.defaults.CUSTOM_ROLE,
{
attributes: {
recorderRole: true
}
});
}
/**
* Notifies this instance about a &qout;Custom Role&qout; command (delivered
* by the Command(s) API of {this._conference}).
*
* @param attributes the attributes {Object} carried by the command
* @param id the identifier of the participant who issued the command. A
* notable idiosyncrasy of the Command(s) API to be mindful of here is that
* the command may be issued by the local participant.
*/
_onCustomRoleCommand ({ attributes }, id) {
// We require to know who issued the command because (1) only a
// moderator is allowed to send commands and (2) a command MUST be
// issued by a defined commander.
if (typeof id === 'undefined'
|| this._conference.isLocalId(id)
|| !attributes.recorderRole)
return;
var isRecorder = (attributes.recorderRole == 'true');
if (isRecorder)
VideoLayout.enableDeviceAvailabilityIcons(id, isRecorder);
}
}
export default Recorder;

View File

@ -80,6 +80,9 @@ function provider.get_sasl_handler(session)
-- here we check if 'room' claim exists
local room, roomErr = token_util.get_room_name(token, appSecret);
if room == nil then
if roomErr == nil then
roomErr = "'room' claim is missing";
end
return false, "not-allowed", roomErr;
end

View File

@ -23,7 +23,7 @@ local function _verify_token(token, appId, appSecret, roomName)
local issClaim = claims["iss"];
if issClaim == nil then
return nil, "Issuer field is missing";
return nil, "'iss' claim is missing";
end
if issClaim ~= appId then
return nil, "Invalid application ID('iss' claim)";
@ -31,7 +31,7 @@ local function _verify_token(token, appId, appSecret, roomName)
local roomClaim = claims["room"];
if roomClaim == nil then
return nil, "Room field is missing";
return nil, "'room' claim is missing";
end
if roomName ~= nil and roomName ~= roomClaim then
return nil, "Invalid room name('room' claim)";