diff --git a/Makefile b/Makefile index afd026632..617a110ca 100644 --- a/Makefile +++ b/Makefile @@ -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) && \ diff --git a/conference.js b/conference.js index 75333fcc3..b6b33d491 100644 --- a/conference.js +++ b/conference.js @@ -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); } }; diff --git a/connection.js b/connection.js index aa0ca0f7c..5add17e48 100644 --- a/connection.js +++ b/connection.js @@ -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} */ -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} */ 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; diff --git a/connection_optimization/do_external_connect.js b/connection_optimization/do_external_connect.js index b4b9fbc58..98014eafb 100644 --- a/connection_optimization/do_external_connect.js +++ b/connection_optimization/do_external_connect.js @@ -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; diff --git a/lang/languages-bg.json b/lang/languages-bg.json index cff4ab662..a3963766e 100644 --- a/lang/languages-bg.json +++ b/lang/languages-bg.json @@ -3,5 +3,8 @@ "bg": "Български", "de": "Немски", "tr": "Турски", - "it": "Италиански" + "it": "Италиански", + "fr": "Френски", + "sl": "Словенски", + "sk": "Словашки" } \ No newline at end of file diff --git a/lang/languages-de.json b/lang/languages-de.json index f198c9c6a..0fbfc83f0 100644 --- a/lang/languages-de.json +++ b/lang/languages-de.json @@ -6,5 +6,6 @@ "it": "Italienisch", "fr": "Französisch", "sl": "Slowenisch", - "sk": "Slowakisch" + "sk": "Slowakisch", + "sv": "Schwedisch" } \ No newline at end of file diff --git a/lang/languages-es.json b/lang/languages-es.json new file mode 100644 index 000000000..560da664f --- /dev/null +++ b/lang/languages-es.json @@ -0,0 +1,10 @@ +{ + "en": "Inglés", + "bg": "Búlgaro", + "de": "Alemán", + "tr": "Turco", + "it": "Italiano", + "fr": "Francés", + "sl": "Esloveno", + "sk": "Eslovaco" +} \ No newline at end of file diff --git a/lang/languages-oc.json b/lang/languages-oc.json new file mode 100644 index 000000000..246316787 --- /dev/null +++ b/lang/languages-oc.json @@ -0,0 +1,10 @@ +{ + "en": "", + "bg": "", + "de": "", + "tr": "", + "it": "", + "fr": "", + "sl": "", + "sk": "" +} \ No newline at end of file diff --git a/lang/main-bg.json b/lang/main-bg.json index da2cfebab..2c71c48cd 100644 --- a/lang/main-bg.json +++ b/lang/main-bg.json @@ -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": "Натиснете за да
промените името", @@ -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": "Нужна е идентификация, за да създадете стая:
__room__
Може да се идентифицирате, за да създадете стая или да изчакате някой друг да го направи.", - "Authenticate": "Идентификация", + "sharePreziMsg": "Prezi string delete", + "Remove": "DELETE DELETE DELETE DELETE DELETE DELETE DELETE DELETE DELETE DELETE ", + "WaitingForHost": "Чакаме домакина ...", + "WaitForHostMsg": "__room__ още не е започнал. Ако вие сте домакина моля идентифицирайте се. В противен случай ще изчакаме домакина.", + "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 разширение. свалете го тук и пробвайте пак!", + "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": "Записът започна" } } \ No newline at end of file diff --git a/lang/main-de.json b/lang/main-de.json index 4f2ff7da0..4b1b7a538 100644 --- a/lang/main-de.json +++ b/lang/main-de.json @@ -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
den anderen Teilnehmern gesprochen werden kann.", + "micMutedPopup": "Ihr Mikrofon wurde stumm geschaltet damit das
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 __room__ 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 __room__ 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

Setzen Sie ein Passwort um den Raum zu schützen:", + "settings3": "Nickname erforderlich

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 Erweiterung installiert 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." } } \ No newline at end of file diff --git a/lang/main-es.json b/lang/main-es.json new file mode 100644 index 000000000..bff3334ce --- /dev/null +++ b/lang/main-es.json @@ -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
nombre", + "moderator": "El dueño de
esta conferencia", + "videomute": "Participante ha
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 __room__ 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

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í !", + "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" + } +} \ No newline at end of file diff --git a/lang/main-oc.json b/lang/main-oc.json new file mode 100644 index 000000000..17b612828 --- /dev/null +++ b/lang/main-oc.json @@ -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": "" + } +} \ No newline at end of file diff --git a/modules/API/API.js b/modules/API/API.js index 28236e124..f15de8bb5 100644 --- a/modules/API/API.js +++ b/modules/API/API.js @@ -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; } /** diff --git a/modules/UI/UI.js b/modules/UI/UI.js index 8a2e2fa98..3b3a88c98 100644 --- a/modules/UI/UI.js +++ b/modules/UI/UI.js @@ -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; diff --git a/modules/UI/recording/Recording.js b/modules/UI/recording/Recording.js index 23a660b29..53e9ac949 100644 --- a/modules/UI/recording/Recording.js +++ b/modules/UI/recording/Recording.js @@ -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); + } }, /** diff --git a/modules/UI/shared_video/SharedVideo.js b/modules/UI/shared_video/SharedVideo.js index aa9af4deb..ad4984ecc 100644 --- a/modules/UI/shared_video/SharedVideo.js +++ b/modules/UI/shared_video/SharedVideo.js @@ -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); } } diff --git a/modules/UI/util/UIUtil.js b/modules/UI/util/UIUtil.js index ff4f9af47..1f4574c8e 100644 --- a/modules/UI/util/UIUtil.js +++ b/modules/UI/util/UIUtil.js @@ -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; } }; diff --git a/modules/UI/videolayout/FilmStrip.js b/modules/UI/videolayout/FilmStrip.js index 7bdb9245b..27c3b4875 100644 --- a/modules/UI/videolayout/FilmStrip.js +++ b/modules/UI/videolayout/FilmStrip.js @@ -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); } }; diff --git a/modules/UI/videolayout/LargeVideo.js b/modules/UI/videolayout/LargeVideo.js index dc4ed19cf..351a2b0f3 100644 --- a/modules/UI/videolayout/LargeVideo.js +++ b/modules/UI/videolayout/LargeVideo.js @@ -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; diff --git a/modules/UI/videolayout/LocalVideo.js b/modules/UI/videolayout/LocalVideo.js index 09eeaa7f2..09b10184e 100644 --- a/modules/UI/videolayout/LocalVideo.js +++ b/modules/UI/videolayout/LocalVideo.js @@ -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; diff --git a/modules/UI/videolayout/VideoLayout.js b/modules/UI/videolayout/VideoLayout.js index 7c94da9db..3bb02ca9d 100644 --- a/modules/UI/videolayout/VideoLayout.js +++ b/modules/UI/videolayout/VideoLayout.js @@ -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(); }, diff --git a/modules/recorder/Recorder.js b/modules/recorder/Recorder.js deleted file mode 100644 index ca156f264..000000000 --- a/modules/recorder/Recorder.js +++ /dev/null @@ -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; diff --git a/prosody-plugins/mod_auth_token.lua b/prosody-plugins/mod_auth_token.lua index 1b24e94c2..9a9befe77 100644 --- a/prosody-plugins/mod_auth_token.lua +++ b/prosody-plugins/mod_auth_token.lua @@ -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 diff --git a/prosody-plugins/token/util.lib.lua b/prosody-plugins/token/util.lib.lua index c281c3d95..0f7c55364 100644 --- a/prosody-plugins/token/util.lib.lua +++ b/prosody-plugins/token/util.lib.lua @@ -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)";