/* * Copyright (c) 2020 Steve Seguin. All Rights Reserved. * * Use of this source code is governed by the APGLv3 open-source license * that can be found in the LICENSE file in the root of the source * tree. Alternative licencing options can be made available on request. * */ /*jshint esversion: 6 */ var formSubmitting = true; var activatedPreview = false; // function log(msg){ // uncomment to enable logging. // console.log(msg); // } // function warnlog(msg, url=false, lineNumber=false){ // onsole.warn(msg); // if (lineNumber){ // console.warn(lineNumber); // } // } // function errorlog(msg, url=false, lineNumber=false){ // console.error(msg); // if (lineNumber){ // console.error(lineNumber); // } // } function addEventToAll(targets, trigger, callback) { // js helper const target = document.querySelectorAll(targets); var triggers = trigger.split(" "); for (let i = 0; i < target.length; i++) { for (let j = 0; j < triggers.length; j++) { setTimeout(function(t1,t2){ t1.addEventListener(t2, function(e) { callback(e, t1); }); },0,target[i],triggers[j]); } } } function mapToAll(targets, callback, parentElement = document) { // js helper if (!targets) { return; } if (!parentElement) { return; } const target = parentElement.querySelectorAll(targets); for (let i = 0; i < target.length; i++) { callback(target[i]); } } var isIFrame = false; if ( parent && (window.location !== window.parent.location )) { isIFrame = true; } function changeParam(url, paramName, paramValue) { paramName = paramName.replace("?", ""); var qind = url.indexOf('?'); url = url.replace("?", "&"); var params = url.substring(qind + 1).split('&'); var query = ''; var match = false; for (var i = 0; i < params.length; i++) { var tokens = params[i].split('='); var name = tokens[0]; var value = ""; if (tokens.length > 1 && tokens[1] !== '') { value = tokens[1]; } if (name == paramName) { if (match) { continue; } // already matched the first time. match = true; value = paramValue; } if (value !== "") { value = '=' + value; } if (query == '') { query = "?" + name + value; } else { query = query + '&' + name + value; } } return url.substring(0, qind) + query; } function updateURL(param, force = false, cleanUrl = false) { param = param.replace("?", ""); var para = param.split('='); if (cleanUrl) { if (history.pushState) { var href = new URL(cleanUrl); if (para.length == 1) { href = changeParam(cleanUrl, para[0], ""); } else { href = changeParam(cleanUrl, para[0], para[1]); } log("--" + href.toString()); window.history.pushState({path: href.toString()}, '', href.toString()); } } else if (!(urlParams.has(para[0]))) { // don't need to replace as it doesn't exist. if (history.pushState) { var href = window.location.href; href = href.replace("??", "?"); var arr = href.split('?'); var newurl; if (arr.length > 1 && arr[1] !== '') { newurl = href + '&' + param; } else { newurl = href + '?' + param; } window.history.pushState({path: newurl.toString()}, '', newurl.toString()); } } else if (force) { if (history.pushState) { var href = new URL(window.location.href); if (para.length == 1) { href = changeParam(window.location.href, para[0], ""); } else { href = changeParam(window.location.href, para[0], para[1]); } log("---" + href.toString()); window.history.pushState({path: href.toString()}, '', href.toString()); } } if (session.sticky) { setCookie("settings", encodeURI(window.location.href), 90); } urlParams = new URLSearchParams(window.location.search); } function changeGuestSettings(ele){ var eles = ele.querySelectorAll('[data-param]'); var UUID = ele.dataset.UUID; var settings = {}; for (var i = 0;i< eles.length; i++){ if (eles[i].tagName.toLowerCase() == "input"){ if (eles[i].checked===true){ settings[eles[i].dataset.param] = true } else if (eles[i].checked===false){ settings[eles[i].dataset.param] = false; } else { settings[eles[i].dataset.param] = eles[i].value; } } } warnlog(settings); if (!settings.changepassword){ delete settings.password; } delete settings.changepassword; if (!settings.changeroom){ // send Migration message delete settings.roomid; } delete settings.roomid; delete settings.changeroom; warnlog(UUID); var msg = {}; msg.changeParams = settings; session.sendRequest(msg, UUID); closeModal(); } // proper room migration needs to happen; in sync. // updateMixer after settings changed // password needs to be special cased // room shouldn't be sent function applyNewParams(changeParams){ for (var key in changeParams){ session[key] = changeParams[key]; log(key); } log(changeParams); updateMixer(); } function promptUser(eleId, UUID=null){ if (document.getElementById("modalBackdrop")){ getById("promptModal").innerHTML = ''; // Delete modal getById("promptModal").remove(); getById("modalBackdrop").innerHTML = ''; // Delete modal getById("modalBackdrop").remove(); } zindex = 30 + document.querySelectorAll('#promptModal').length; modalTemplate = `
×
`; document.body.insertAdjacentHTML("beforeend", modalTemplate); // Insert modal at body end getById("promptModalMessage").innerHTML = getById(eleId).innerHTML; if (UUID){ getById("promptModalMessage").dataset.UUID = UUID; } document.getElementById("modalBackdrop").addEventListener("click", closeModal); getById("promptModal").addEventListener("click", function(e) { e.stopPropagation(); return false; }); } function warnUser(message){ // Allows for multiple alerts to stack better. // Every modal and backdrop has an increasing z-index // to block the previous modal if (document.getElementById("modalBackdrop")){ getById("alertModal").innerHTML = ''; // Delete modal getById("alertModal").remove(); getById("modalBackdrop").innerHTML = ''; // Delete modal getById("modalBackdrop").remove(); } zindex = 31 + document.querySelectorAll('.alertModal').length; message = message.replace(/\n/g,"
"); modalTemplate = `
× ${message}
`; document.body.insertAdjacentHTML("beforeend", modalTemplate); // Insert modal at body end document.getElementById("modalBackdrop").addEventListener("click", closeModal); getById("alertModal").addEventListener("click", function(e) { e.stopPropagation(); return false; }); } function closeModal(){ getById("modalBackdrop").innerHTML = ''; // Delete modal getById("modalBackdrop").remove(); getById("alertModal").innerHTML = ''; // Delete modal getById("alertModal").remove(); getById("promptModal").innerHTML = ''; // Delete modal getById("promptModal").remove(); } var filename = false; try { filename = window.location.pathname.substring(window.location.pathname.lastIndexOf('/') + 1); filename = filename.replace("??", "?"); filename2 = filename.split("?")[0]; // split at ??? if (filename.split(".").length == 1) { if (filename2.length < 2) { // easy win filename = false; } else if (filename.startsWith("&")) { // easy win var tmpHref = window.location.href.substring(0, window.location.href.lastIndexOf('/')) + "/?" + filename.split("&").slice(1).join("&"); log("TMP " + tmpHref); updateURL(filename.split("&")[1], true, tmpHref); filename = false; } else if (filename2.split("&")[0].includes("=")) { log("asdf " + filename.split("&")[0]); if (history.pushState) { var tmpHref = window.location.href.substring(0, window.location.href.lastIndexOf('/')); tmpHref = tmpHref + "/?" + filename; filename = false; //warnUser("Please ensure your URL is correctly formatted."); window.history.pushState({path: tmpHref.toString()}, '', tmpHref.toString()); } } else { filename = filename2.split("&")[0]; if (filename2 != filename) { warnUser("Warning: Please ensure your URL is correctly formatted."); } } } else { filename = false; } log(filename); } catch (e) { errorlog(e); } (function(w) { w.URLSearchParams = w.URLSearchParams || function(searchString) { var self = this; searchString = searchString.replace("??", "?"); self.searchString = searchString; self.get = function(name) { var results = new RegExp('[\?&]' + name + '=([^&#]*)').exec(self.searchString); if (results == null) { return null; } else { return decodeURI(results[1]) || 0; } }; }; })(window); var urlEdited = window.location.search.replace(/\?\?/g, "?"); urlEdited = urlEdited.replace(/\?/g, "&"); urlEdited = urlEdited.replace(/\&/, "?"); if (urlEdited !== window.location.search){ warnlog(window.location.search + " changed to " + urlEdited); window.history.pushState({path: urlEdited.toString()}, '', urlEdited.toString()); } var urlParams = new URLSearchParams(urlEdited); var sanitizeStreamID = function(streamID) { streamID = streamID.trim(); if (streamID.length < 1) { streamID = session.generateStreamID(8); if (!(session.cleanOutput)) { warnUser("No streamID was provided; one will be generated randomily.\n\nStream ID: " + streamID); } } var streamID_sanitized = streamID.replace(/[\W]+/g, "_"); if (streamID !== streamID_sanitized) { if (!(session.cleanOutput)) { warnUser("Info: Only AlphaNumeric characters should be used for the stream ID.\n\nThe offending characters have been replaced by an underscore"); } } if (streamID_sanitized.length > 24) { streamID_sanitized = streamID_sanitized.substring(0, 24); if (!(session.cleanOutput)) { warnUser("The Stream ID should be less than 25 alPhaNuMeric characters long.\n\nWe will trim it to length."); } } return streamID_sanitized; }; var checkStrength = function(string) { var matcher = /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{7,30}$/; if (string.match(matcher)) { return true; } else if (string.length > 20) { return true; } else { return false; } }; var checkStrengthRoom = function() { var result1 = checkStrength(getById('videoname1').value); var result2 = getById('passwordRoom').value.length; var target = getById('securityLevelRoom'); target.style.display = "block"; if (result1) { if (result2) { target.innerHTML = "Share only with those you trust"; } else { target.innerHTML = "A password is recommended"; } } else { target.innerHTML = "Insecure room name. Allowed chars: A-Z, a-z, 0-9, _"; } }; var sanitizeChat = function(string) { var temp = document.createElement('div'); temp.innerText = string; temp.innerText = temp.innerHTML; temp = temp.textContent || temp.innerText || ""; temp = temp.substring(0, Math.min(temp.length, 500)); return temp.trim(); }; var sanitizeString = function(str) { str = str.replace(/[^a-z0-9áéíóúñü \.,_-]/gim, ""); return str.trim(); }; var sanitizeLabel = function(string) { let temp = document.createElement("div"); temp.innerText = string; temp.innerText = temp.innerHTML; temp = temp.textContent || temp.innerText || ""; temp = temp.substring(0, Math.min(temp.length, 50)); return temp.trim(); }; var sanitizeRoomName = function(roomid) { roomid = roomid.trim(); if (roomid === "") { return roomid; } else if (roomid === false) { return roomid; } var sanitized = roomid.replace(/[\W]+/g, "_"); if (sanitized !== roomid) { if (!(session.cleanOutput)) { warnUser("Info: Only AlphaNumeric characters should be used for the room name.\n\nThe offending characters have been replaced by an underscore"); } } if (sanitized.length > 30) { sanitized = sanitized.substring(0, 30); if (!(session.cleanOutput)) { warnUser("The Room name should be less than 31 alPhaNuMeric characters long.\n\nWe will trim it to length."); } } return sanitized; }; var sanitizePassword = function(passwrd) { if (passwrd === "") { return passwrd; } else if (passwrd === false) { return passwrd; } else if (passwrd === null) { return passwrd; } passwrd = passwrd.trim(); if (passwrd.length < 1) { if (!(session.cleanOutput)) { warnUser("The password provided was blank."); } } var sanitized = encodeURIComponent(passwrd);//.replace(/[\W]+/g, "_"); //if (sanitized !== passwrd) { // if (!(session.cleanOutput)) { // warnUser("Info: Only AlphaNumeric characters should be used in the password.\n\nThe offending characters have been replaced by an underscore"); // } //} return sanitized; }; function getChromeVersion() { var raw = navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./); return raw ? parseInt(raw[2], 10) : false; } if (!(getChromeVersion()>=57)){ getById("effectSelector").disabled=true; getById("effectSelector3").disabled=true; getById("effectSelector").title = "Effects are only support on Chromium-based browsers"; getById("effectSelector3").title = "Effects are only support on Chromium-based browsers"; var elements = document.querySelectorAll('[data-effectsNotice]') for (let i = 0; i < elements.length; i++) { elements[i].style.display = "inline-block"; } } function safariVersion() { var ver = 0; try { ver = navigator.appVersion.split("Version/"); if (ver.length > 1) { ver = ver[1].split(" Safari"); } if (ver.length > 1) { ver = ver[0].split("."); } if (ver.length > 1) { ver = parseInt(ver[0]); } else { ver = 0; } } catch (e) { return 0; } return ver; } if (urlParams.has('optimize')) { var optimize = parseInt(urlParams.get('optimize')); if (!optimize) { optimize = 600; } session.optimize = optimize; } if (window.obsstudio) { session.disableWebAudio = true; // default true; might be useful to disable on slow or old computers? session.audioMeterGuest = false; session.audioEffects = false; session.obsfix = 15; // can be manually set via URL. ; VP8=15, VP9=30. (previous was 20.) try { log("OBS VERSION:" + window.obsstudio.pluginVersion); log("macOS: " + navigator.userAgent.indexOf('Mac OS X') != -1); log(window.obsstudio); if (!(urlParams.has('streamlabs'))) { var ver = window.obsstudio.pluginVersion; ver1 = ver.split("."); updateURL("streamlabs"); var cefVersion = getChromeVersion(); if (ver1.length == 3) { // Should be 3, but disabled3 if ((ver1.length == 3) && (parseInt(ver1[0]) == 2) && (cefVersion < 76) && (navigator.userAgent.indexOf('Mac OS X') != -1)) { getById("main").innerHTML = "

Update OBS Studio to v26.1.2 or newer; older versions and StreamLabs OBS are not supported on macOS.\
download here: https://github.com/obsproject/obs-studio/releases/tag/26.1.2\



\

Please use the Electron Capture app if there are further problems or if you wish to use StreamLabs on macOS still.

\
You can find more details within our wiki guide - https://github.com/steveseguin/obsninja/wiki/FAQ#mac-os\
You can bypass this error message by refreshing, Clicking Here, or by adding &streamlabs to the URL, but it may still not actually work.\ \
Please report this problem to steve@seguin.email if you feel it is an error.\
"; } } } } catch (e) { errorlog(e); } //if (session.optimize){ if (navigator.userAgent.indexOf('Mac OS X') != -1) { session.codec = "h264"; // default the codec to h264 if OBS and macOS } //} window.addEventListener('obsSceneChanged', function(event) { log("OBS EVENT"); log(event.detail.name); window.obsstudio.getCurrentScene(function(scene) { log("OBS SCENE"); log(scene); }); window.obsstudio.getStatus(function(status) { log("OBS STATUS:"); log(status); }); }); } window.onload = function winonLoad() { // This just keeps people from killing the live stream accidentally. Also give me a headsup that the stream is ending window.addEventListener("beforeunload", function(e) { try { session.ws.close(); if (session.videoElement.recording) { session.videoElement.recorder.writer.close(); session.videoElement.recording = false; } for (var i in session.rpcs) { if (session.rpcs[i].videoElement) { if (session.rpcs[i].videoElement.recording) { session.rpcs[i].videoElement.recorder.writer.close(); session.rpcs[i].videoElement.recording = false; } } } } catch (e) {} //setTimeout(function(){session.hangup();},0); return undefined; // ADDED OCT 29th; get rid of popup. Just close the socket connection if the user is refreshing the page. It's one or the other. }); }; getById("credits").innerHTML = "Version: " + session.version + " - " + getById("credits").innerHTML; var lastTouchEnd = 0; document.addEventListener('touchend', function(event) { var now = (new Date()).getTime(); if (now - lastTouchEnd <= 300) { event.preventDefault(); } lastTouchEnd = now; }, false); document.addEventListener('click', function(event) { if (session.firstPlayTriggered == false) { playAllVideos(); session.firstPlayTriggered = true; history.pushState({}, ''); } }); var Callbacks = []; var CtrlPressed = false; // global var AltPressed = false; document.addEventListener("keydown", event => { if ((event.ctrlKey) || (event.metaKey)) { // detect if CTRL is pressed CtrlPressed = true; } else { CtrlPressed = false; } if (event.altKey) { AltPressed = true; } else { AltPressed = false; } if (CtrlPressed && event.keyCode) { if (event.keyCode == 77) { // m if (event.metaKey) { if (AltPressed) { toggleMute(); // macOS } } else { toggleMute(); // Windows } // } else if (event.keyCode == 69) { // e // hangup(); } else if (event.keyCode == 66) { // b toggleVideoMute(); } } }); document.addEventListener("keyup", event => { if (!((event.ctrlKey) || (event.metaKey))) { if (CtrlPressed) { CtrlPressed = false; for (var i in Callbacks) { var cb = Callbacks[i]; log(cb.slice(1)); cb[0](...cb.slice(1)); // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax#A_better_apply } Callbacks = []; } } if (!(event.altKey)) { AltPressed = false; } }); window.onpopstate = function() { if (session.firstPlayTriggered) { window.location.reload(true); } }; if (typeof session === 'undefined') { // make sure to init the WebRTC if not exists. var session = WebRTC.Media; session.streamID = session.generateStreamID(); errorlog("Serious error: WebRTC session didn't load in time"); } function makeDraggableElement(elmnt) { try { elmnt.dragElement = false; elmnt.style.bottom = "auto"; elmnt.style.cursor = "grab"; elmnt.stashonmouseup = null; elmnt.stashonmousemove = null; } catch (e) { errorlog(e); return; } var pos1 = 0; var pos2 = 0; var pos3 = 0; var pos4 = 0; function dragMouseDown(e) { e = e || window.event; e.preventDefault(); pos3 = e.clientX; pos4 = e.clientY; elmnt.stashonmouseup = document.onmouseup; // I don't want to interfere with other drag events. elmnt.stashonmousemove = document.onmousemove; document.onmouseup = closeDragElement; document.onmousemove = elementDrag; } function elementDrag(e) { e = e || window.event; e.preventDefault(); elmnt.dragElement = true; pos1 = pos3 - e.clientX; pos2 = pos4 - e.clientY; pos3 = e.clientX; pos4 = e.clientY; var topDrag = (elmnt.offsetTop - pos2); if (topDrag > -3) { topDrag = -3; } elmnt.style.top = topDrag + "px"; elmnt.style.left = (elmnt.offsetLeft - pos1) + "px"; } elmnt.onmousedown = dragMouseDown; function closeDragElement() { document.onmouseup = elmnt.stashonmouseup; document.onmousemove = elmnt.stashonmousemove; } } function setCookie(cname, cvalue, exdays) { var d = new Date(); d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000)); var expires = "expires=" + d.toUTCString(); document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/"; } function getCookie(cname) { var name = cname + "="; var ca = document.cookie.split(';'); for (var i = 0; i < ca.length; i++) { var c = ca[i]; while (c.charAt(0) == ' ') { c = c.substring(1); } if (c.indexOf(name) == 0) { return c.substring(name.length, c.length); } } return ""; } if (getCookie("redirect") == "yes") { setCookie("redirect", "", 0); session.sticky = true; } else if (getCookie("settings") != "") { session.sticky = confirm("Would you like you load your previous session's settings?"); if (!session.sticky) { setCookie("settings", "", 0); log("deleting cookie as user said no"); } else { var cookieSettings = decodeURI(getCookie("settings")); setCookie("redirect", "yes", 1); window.location.replace(cookieSettings); } } if (urlParams.has('sticky')) { if (getCookie("permission") == "") { session.sticky = confirm("Would you allow us to store a cookie to keep your session settings persistent?"); } else { session.sticky = true; } if (session.sticky) { setCookie("permission", "yes", 999); setCookie("settings", encodeURI(window.location.href), 90); } } if (urlParams.has('retrytimeout')) { session.retryTimeout = parseInt(urlParams.get('retrytimeout')); } if (urlParams.has('ptz')){ session.ptz=true; } var screensharebutton = true; var screensharesupport = true; if (urlParams.has('nosettings') || urlParams.has('ns')) { screensharebutton = false; session.showSettings = false; } if (urlParams.has('nomicbutton') || urlParams.has('nmb')) { getById("mutebutton").setAttribute('style', "display: none !important"); } if (urlParams.has('novideobutton') || urlParams.has('nvb')) { getById("mutevideobutton").setAttribute('style', "display: none !important"); } if (urlParams.has('screenshareid') || urlParams.has('ssid')) { if (urlParams.get('screenshareid') || urlParams.get('ssid')) { session.screenshareid = urlParams.get('screenshareid') || urlParams.get('ssid'); session.screenshareid = sanitizeStreamID(session.screenshareid); } } if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) { //session.webcamonly = true; getById("shareScreenGear").style.display = "none"; screensharebutton = false; screensharesupport = false; getById("container-2").className = 'column columnfade advanced'; // Hide screen share on mobile getById("dropButton").style.display = "none"; //session.disableWebAudio = true; // default true; might be useful to disable on slow or old computers? session.audioEffects = false; // disable audio inbound effects also. session.audioMeterGuest = false; } else if ((iOS) || (iPad)) { getById("shareScreenGear").style.display = "none"; screensharebutton = false; screensharesupport = false; getById("container-2").className = 'column columnfade advanced'; // Hide screen share on mobile getById("dropButton").style.display = "none"; //session.audiobitrate = false; // iOS devices seem to get distortion with custom audio bitrates. Disable for now. //session.maxiosbitrate = 10; // this is 10-kbps by default already. //session.disableWebAudio = true; // default true; might be useful to disable on slow or old computers? session.audioEffects = false; // disable audio inbound effects also. session.audioMeterGuest = false; } else { log("MAKE DRAGGABLE"); setTimeout(function(){makeDraggableElement(document.getElementById("subControlButtons"));},100); if (safariVersion() && !getChromeVersion()){ getById("SafariWarning").style.display = "block"; } } if ((iOS) || (iPad)) { window.addEventListener('resize', function() { // Safari is the new IE. var msg = {}; msg.requestSceneUpdate = true; session.sendMessage(msg); if ( window.matchMedia("(orientation: portrait)").matches ) { document.getElementsByTagName("html")[0].style.height = "100vh"; setTimeout(function(){ document.getElementsByTagName("html")[0].style.height = "100%"; }, 1000); } else if ( window.matchMedia("(orientation: landscape)").matches ) { document.getElementsByTagName("html")[0].style.height = "100vh"; setTimeout(function(){ document.getElementsByTagName("html")[0].style.height = "100%"; }, 1000); } }); } if (/CriOS/i.test(navigator.userAgent) && (iOS || iPad)) { if (!(session.cleanOutput)) { try { navigator.mediaDevices.getUserMedia; } catch (e) { warnUser("Chrome on this device does not support the required technology to use this site.\n\nPlease use Safari instead or update your iOS and browser version."); } } } if (urlParams.has('broadcast') || urlParams.has('bc')) { log("Broadcast flag set"); session.broadcast = urlParams.get('broadcast') || urlParams.get('bc'); //if ((iOS) || (iPad)) { // session.nopreview = false; //} else { // session.nopreview = true; //} session.minipreview = 2; // full screen if nothing else on screen. session.style = 1; getById("header").style.display = "none"; getById("header").style.opacity = 0; } var directorLanding = false; if (urlParams.has('director') || urlParams.has('dir')) { directorLanding = urlParams.get('director') || urlParams.get('dir'); if (directorLanding === null) { directorLanding = true; } else if (directorLanding.length === 0) { directorLanding = true; } else { directorLanding = false; } } else if (filename === "director") { directorLanding = true; filename = false; } if (urlParams.has('rooms')) { session.rooms = urlParams.get('rooms').split(",").map(function(e) { return sanitizeRoomName(e); }); getById("rooms").classList.remove('advanced'); } if (urlParams.has('showdirector')) { session.showDirector = true; } if (urlParams.has('midi') || urlParams.has('hotkeys')) { session.midiHotkeys = urlParams.get('midi') || urlParams.get ('hotkeys') || 1; session.midiHotkeys = parseInt(session.midiHotkeys); } var loadedQRCode = false; function loadQR(){ if (loadedQRCode==false){ loadedQRCode=true; var script = document.createElement('script'); script.src = "./thirdparty/qrcode.min.js"; // dynamically load this only if its needed. Keeps loading time down. document.head.appendChild(script); } } if (urlParams.has('webcam') || urlParams.has('wc')) { session.webcamonly = true; screensharebutton = false; } else if (urlParams.has('screenshare') || urlParams.has('ss')) { session.screenshare = true; } else if (urlParams.has('fileshare') || urlParams.has('fs')) { getById("container-5").classList.remove('advanced'); getById("container-5").classList.add("skip-animation"); getById("container-5").classList.remove('pointer'); } else if (directorLanding) { getById("container-1").classList.remove('advanced'); getById("container-1").classList.add("skip-animation"); getById("container-1").classList.remove('pointer'); } else if (urlParams.has('website') || urlParams.has('iframe')) { getById("container-6").classList.remove('advanced'); getById("container-6").classList.add("skip-animation"); getById("container-6").classList.remove('pointer'); } if (urlParams.has('ssb')) { screensharebutton = true; } if (urlParams.has('mute') || urlParams.has('muted') || urlParams.has('m')) { session.muted = true; } if (urlParams.has('videomute') || urlParams.has('videomuted') || urlParams.has('vm')) { session.videoMutedFlag = true; } if (urlParams.has('deaf') || urlParams.has('deafen')) { session.directorSpeakerMuted=true; // false == true in this case. } if (urlParams.has('blind')) { session.directorDisplayMuted=true; // false == true in this case. } if (urlParams.has('dpi') || urlParams.has('dpr')) { session.devicePixelRatio = urlParams.get('dpi') || urlParams.get('dpr') || 2.0; } //else if (window.devicePixelRatio && window.devicePixelRatio!==1){ // session.devicePixelRatio = window.devicePixelRatio; // this annoys me to no end. //} if (urlParams.has('speakermute') || urlParams.has('mutespeaker') || urlParams.has('sm') || urlParams.has('ms')) { session.speakerMuted = true; getById("mutespeakertoggle").className = "las la-volume-mute my-float toggleSize"; //getById("mutespeakerbutton").className="advanced float2 red"; getById("mutespeakerbutton").classList.add("red"); getById("mutespeakerbutton").classList.add("float2"); getById("mutespeakerbutton").classList.remove("float"); var sounds = document.getElementsByTagName("video"); for (var i = 0; i < sounds.length; ++i) { sounds[i].muted = session.speakerMuted; } } if (urlParams.has('chatbutton') || urlParams.has('chat') || urlParams.has('cb')) { session.chatbutton = urlParams.get('chatbutton') || urlParams.get('chat') || urlParams.get('cb'); if (session.chatbutton === "false") { session.chatbutton = false; } else if (session.chatbutton === "0") { session.chatbutton = false; } else if (session.chatbutton === "no") { session.chatbutton = false; } else if (session.chatbutton === "off") { session.chatbutton = false; } else { session.chatbutton = true; getById("chatbutton").classList.remove("advanced"); getById("controlButtons").style.display = "inherit"; } } if (session.screenshare == true) { getById("container-3").className = 'column columnfade advanced'; // Hide screen share on mobile getById("container-2").classList.add("skip-animation"); getById("container-2").classList.remove('pointer'); } if (urlParams.has('manual')) { session.manual = true; } if (urlParams.has('hands') || urlParams.has('hand')) { session.raisehands = true; } if (urlParams.has('portrait') || urlParams.has('916') || urlParams.has('vertical')) { session.aspectratio = 1; // 9:16 (default of 0 is 16:9) } else if (urlParams.has('square') || urlParams.has('11')) { session.aspectratio = 2; // 9:16 (default of 0 is 16:9) } if (urlParams.has('record')) { if (safariVersion()) { if (!(session.cleanOutput)) { warnUser("Your browser or device is not supported. Try Chrome if on macOS."); } } else { session.recordLocal = urlParams.get('record'); if (session.recordLocal !== parseInt(session.recordLocal)) { session.recordLocal = 6000; } else { session.recordLocal = parseInt(session.recordLocal); } } } if (urlParams.has('pcm')) { session.pcm = true; } if (urlParams.has('bigbutton')) { session.bigmutebutton = true; getById("mutebutton").style.width = "min(40vh,40vw)"; getById("mutebutton").style.height = "min(40vh,40vw)"; getById("mutetoggle").style.width = "min(40vh,40vw)"; getById("mutetoggle").style.height = "min(40vh,40vw)"; } if (urlParams.has('scene')) { session.scene = parseInt(urlParams.get('scene')) || 0; session.disableWebAudio = true; session.audioEffects = false; session.audioMeterGuest = false; } if (session.scene>1){ // scene =0 and 1 should load instantly. session.hiddenSceneViewBitrate = 0; // By default this is ~ 400kbps, but if you have 10 scenes, i don't want to kill things. } if (urlParams.has('scenetype') || urlParams.has('type')) { session.sceneType = parseInt(urlParams.get('scenetype')) || parseInt(urlParams.get('type')) || false; } if (urlParams.has('mediasettings')) { session.forceMediaSettings = true; } if (urlParams.has('transcript') || urlParams.has('transcribe') || urlParams.has('trans')) { session.transcript = urlParams.get('transcript') || urlParams.get('transcribe') || urlParams.get('trans') || "en-US"; } if (urlParams.has('cc') || urlParams.has('closedcaptions') || urlParams.has('captions')) { session.closedCaptions = true; } if (session.webcamonly == true) { getById("container-2").className = 'column columnfade advanced'; // Hide screen share on mobile getById("container-3").classList.add("skip-animation"); getById("container-3").classList.remove('pointer'); setTimeout(function() { previewWebcam(); }, 100); } if (urlParams.has('css')){ var cssURL = urlParams.get('css'); cssURL = decodeURI(cssURL); log(cssURL); var cssStylesheet = document.createElement('link'); cssStylesheet.rel = 'stylesheet'; cssStylesheet.type = 'text/css'; cssStylesheet.media = 'screen'; cssStylesheet.href = cssURL; document.getElementsByTagName('head')[0].appendChild(cssStylesheet); cssStylesheet.onload = function() { getById("main").classList.remove('hidden'); log("loaded remote style sheet"); } cssStylesheet.onerror = function() { getById("main").classList.remove('hidden'); errorlog("REMOTE STYLE SHEET HAD ERROR"); } } else { getById("main").classList.remove('hidden'); } if (urlParams.has('password') || urlParams.has('pass') || urlParams.has('pw') || urlParams.has('p')) { session.password = urlParams.get('password') || urlParams.get('pass') || urlParams.get('pw') || urlParams.get('p'); if (!session.password) { session.password = prompt("Please enter the password below: \n\n(Note: Passwords are case-sensitive and you will not be alerted if it is incorrect.)"); } else if (session.password === "false") { session.password = false; session.defaultPassword = false; } else if (session.password === "0") { session.password = false; session.defaultPassword = false; } else if (session.password === "off") { session.password = false; session.defaultPassword = false; } } if (session.password) { session.password = sanitizePassword(session.password); getById("passwordRoom").value = session.password; session.defaultPassword = false; getById("addPasswordBasic").style.display = "none"; } if (urlParams.has('hash') || urlParams.has('crc') || urlParams.has('check')) { // could be brute forced in theory, so not as safe as just not using a hash check. session.taintedSession = null; // waiting to see if valid or not. var hash_input = urlParams.get('hash') || urlParams.get('crc') || urlParams.get('check'); if (session.password === false) { session.password = prompt("Please enter the password below: \n\n(Note: Passwords are case-sensitive and you will not be alerted if it is incorrect.)"); session.password = sanitizePassword(session.password); getById("passwordRoom").value = session.password; session.defaultPassword = false; } session.generateHash(session.password + session.salt, 6).then(function(hash) { // million to one error. log("hash is " + hash); if (hash.substring(0, 4) !== hash_input) { // hash crc checks are just first 4 characters. session.taintedSession = true; if (!(session.cleanOutput)) { getById("request_info_prompt").innerHTML = "The password was incorrect.\n\nRefresh and try again."; getById("request_info_prompt").style.display = "block"; getById("mainmenu").style.display = "none"; getById("head1").style.display = "none"; session.cleanOutput = true; } else { getById("request_info_prompt").innerHTML = ""; getById("request_info_prompt").style.display = "block"; getById("mainmenu").style.display = "none"; getById("head1").style.display = "none"; } } else { session.taintedSession = false; session.hash = hash; } }); } if (session.defaultPassword !== false) { session.password = session.defaultPassword; // no user entered password; let's use the default password if its not disabled. } if (urlParams.has('showlabels') || urlParams.has('showlabel') || urlParams.has('sl')) { session.showlabels = urlParams.get('showlabels') || urlParams.get('showlabel') || urlParams.get('sl') || ""; session.showlabels = sanitizeLabel(session.showlabels.replace(/[\W]+/g, "_").replace(/_+/g, '_')); if (session.showlabels == "") { session.showlabels = true; session.labelstyle = false; } else { session.labelstyle = session.showlabels; session.showlabels = true; } } if (urlParams.has('sizelabel') || urlParams.has('labelsize') || urlParams.has('fontsize')) { session.labelsize = urlParams.get('sizelabel') || urlParams.get('labelsize') || urlParams.get('fontsize') || 100; session.labelsize = parseInt(session.labelsize); } if (urlParams.has('label') || urlParams.has('l')) { session.label = urlParams.get('label') || urlParams.get('l'); var updateURLAsNeed = true; if (session.label == null || session.label.length == 0) { session.label = prompt("Please enter your display name:"); } else { var updateURLAsNeed = false; session.label = decodeURIComponent(session.label); } if (session.label != null) { session.label = sanitizeLabel(session.label); // alphanumeric was too strict. document.title = session.label; // what the result is. if (updateURLAsNeed) { var label = encodeURIComponent(session.label); if (urlParams.has('l')) { updateURL("l=" + label, true, false); } else { updateURL("label=" + label, true, false); } } } } if (urlParams.has('transparent')) { // sets the window to be transparent - useful for IFRAMES? getById("main").style.backgroundColor = "rgba(0,0,0,0)"; document.documentElement.style.setProperty('--container-color', '#0000'); document.documentElement.style.setProperty('--background-color', '#0000'); document.documentElement.style.setProperty('--regular-margin', '0'); document.documentElement.style.setProperty('--director-margin', '0 25px 0 0'); getById("directorLinksButton").style.color = "black"; session.transparent=true; } if (urlParams.has('stereo') || urlParams.has('s') || urlParams.has('proaudio')) { // both peers need this enabled for HD stereo to be on. If just pub, you get no echo/noise cancellation. if just viewer, you get high bitrate mono log("STEREO ENABLED"); session.stereo = urlParams.get('stereo') || urlParams.get('s') || urlParams.get('proaudio'); if (session.stereo) { session.stereo = session.stereo.toLowerCase(); } //var supportedConstraints = navigator.mediaDevices.getSupportedConstraints(); //supportedConstraints.channelCount; if (session.stereo === "false") { session.stereo = 0; session.audioInputChannels = 1; } else if (session.stereo === "0") { session.stereo = 0; session.audioInputChannels = 1; } else if (session.stereo === "no") { session.stereo = 0; session.audioInputChannels = 1; } else if (session.stereo === "off") { session.stereo = 0; session.audioInputChannels = 1; } else if (session.stereo === "1") { session.stereo = 1; } else if (session.stereo === "both") { session.stereo = 1; } else if (session.stereo === "3") { session.stereo = 3; } else if (session.stereo === "out") { session.stereo = 3; } else if (session.stereo === "mono") { session.stereo = 3; session.audiobitrate = 128; } else if (session.stereo === "4") { session.stereo = 4; } else if (session.stereo === "multi") { session.stereo = 4; } else if (session.stereo === "2") { session.stereo = 2; } else if (session.stereo === "in") { session.stereo = 2; } else { session.stereo = 5; // guests; no stereo in, no high bitrate in, but otherwise like stereo=1 } } if ((session.stereo == 1) || (session.stereo == 3) || (session.stereo == 4) || (session.stereo == 5)) { session.echoCancellation = false; session.autoGainControl = false; session.noiseSuppression = false; } if (urlParams.has('mono')) { session.mono = true; if ((session.stereo == 1) || (session.stereo == 4)) { session.stereo = 3; session.audiobitrate = 128; } else if (session.stereo == 5) { session.stereo = 3; session.audiobitrate = 128; } else if (session.stereo == 2) { session.stereo = 0; session.audiobitrate = 128; } } if (urlParams.has("channelcount") || urlParams.has("ac")) { session.audioInputChannels = urlParams.get('channelcount') || urlParams.get('ac'); session.audioInputChannels = parseInt(session.audioInputChannels); if (!session.audioInputChannels) { session.audioInputChannels = false; } } if (urlParams.has("aec") || urlParams.has("ec")) { session.echoCancellation = urlParams.get('aec') || urlParams.get('ec'); if (session.echoCancellation) { session.echoCancellation = session.echoCancellation.toLowerCase(); } if (session.echoCancellation == "false") { session.echoCancellation = false; } else if (session.echoCancellation == "0") { session.echoCancellation = false; } else if (session.echoCancellation == "no") { session.echoCancellation = false; } else if (session.echoCancellation == "off") { session.echoCancellation = false; } else { session.echoCancellation = true; } } if (urlParams.has("autogain") || urlParams.has("ag")) { session.autoGainControl = urlParams.get('autogain') || urlParams.get('ag'); if (session.autoGainControl) { session.autoGainControl = session.autoGainControl.toLowerCase(); } if (session.autoGainControl == "false") { session.autoGainControl = false; } else if (session.autoGainControl == "0") { session.autoGainControl = false; } else if (session.autoGainControl == "no") { session.autoGainControl = false; } else if (session.autoGainControl == "off") { session.autoGainControl = false; } else { session.autoGainControl = true; } } if (urlParams.has("denoise") || urlParams.has("dn")) { session.noiseSuppression = urlParams.get('denoise') || urlParams.get('dn'); if (session.noiseSuppression) { session.noiseSuppression = session.noiseSuppression.toLowerCase(); } if (session.noiseSuppression == "false") { session.noiseSuppression = false; } else if (session.noiseSuppression == "0") { session.noiseSuppression = false; } else if (session.noiseSuppression == "no") { session.noiseSuppression = false; } else if (session.noiseSuppression == "off") { session.noiseSuppression = false; } else { session.noiseSuppression = true; } } if (urlParams.has('roombitrate') || urlParams.has('roomvideobitrate') || urlParams.has('rbr')) { log("Room BITRATE SET"); session.roombitrate = urlParams.get('roombitrate') || urlParams.get('rbr') || urlParams.get('roomvideobitrate'); session.roombitrate = parseInt(session.roombitrate); if (session.roombitrate < 1) { session.roombitrate = 0; } } if ( urlParams.has('outboundaudiobitrate') || urlParams.has('oab')) { session.outboundAudioBitrate = parseInt(urlParams.get('outboundaudiobitrate')) || parseInt(urlParams.get('oab')) || false; } if (urlParams.has('outboundvideobitrate') || urlParams.has('ovb')) { session.outboundVideoBitrate = parseInt(urlParams.get('outboundvideobitrate')) || parseInt(urlParams.get('ovb')) || false; } if (urlParams.has('webp')){ session.webp = true; } if (urlParams.has('webpquality') || urlParams.has('webpq') || urlParams.has('wq')){ session.webPquality = parseInt(urlParams.get('webpquality')) || parseInt(urlParams.get('webpq')) || parseInt(urlParams.get('wq')) || 4; } if (urlParams.has('audiobitrate') || urlParams.has('ab')) { // both peers need this enabled for HD stereo to be on. If just pub, you get no echo/noise cancellation. if just viewer, you get high bitrate mono log("AUDIO BITRATE SET"); session.audiobitrate = urlParams.get('audiobitrate') || urlParams.get('ab'); session.audiobitrate = parseInt(session.audiobitrate); if (session.audiobitrate < 1) { session.audiobitrate = false; } else if (session.audiobitrate > 510) { session.audiobitrate = 510; } // this is to just prevent abuse } if ((iOS) || (iPad)) { session.audiobitrate = false; // iOS devices seem to get distortion with custom audio bitrates. Disable for now. } /* if (urlParams.has('whitebalance') || urlParams.has('temp')){ // Need to be applied after the camera is selected. bleh. not enforcible. remove for now. var temperature = urlParams.get('whitebalance') || urlParams.get('temp'); try{ updateCameraConstraints('colorTemperature', parseFloat(temperature)); } catch (e){errorlog(e);} } */ if (urlParams.has('streamid') || urlParams.has('view') || urlParams.has('v') || urlParams.has('pull')) { // the streams we want to view; if set, but let blank, we will request no streams to watch. session.view = urlParams.get('streamid') || urlParams.get('view') || urlParams.get('v') || urlParams.get('pull'); // this value can be comma seperated for multiple streams to pull getById("headphonesDiv2").style.display = "inline-block"; getById("headphonesDiv").style.display = "inline-block"; getById("addPasswordBasic").style.display = "none"; if (session.view == null) { session.view = ""; } if (session.view) { if (session.view.split(",").length > 1) { session.view_set = session.view.split(","); } } } if (urlParams.has('nopreview') || urlParams.has('np')) { log("preview OFF"); session.nopreview = true; } else if ((urlParams.has('preview')) || (urlParams.has('showpreview'))) { log("preview ON"); session.nopreview = false; } else if ((urlParams.has('minipreview')) || (urlParams.has('mini'))) { var mini = urlParams.has('minipreview') || urlParams.has('mini') || true; // 2 is a valid option. log("preview ON"); session.nopreview = false; session.minipreview = mini; } if (urlParams.has('obsfix')) { session.obsfix = urlParams.get('obsfix'); if (session.obsfix) { session.obsfix = session.obsfix.toLowerCase(); } if (session.obsfix == "false") { session.obsfix = false; } else if (session.obsfix == "0") { session.obsfix = false; } else if (session.obsfix == "no") { session.obsfix = false; } else if (session.obsfix == "off") { session.obsfix = false; } else if (parseInt(session.obsfix) > 0) { session.obsfix = parseInt(session.obsfix); } else { session.obsfix = 1; // aggressive. } } if (urlParams.has('controlroombitrate') || urlParams.has('crb')) { session.controlRoomBitrate = true; } if (urlParams.has('remote') || urlParams.has('rem')) { log("remote ENABLED"); session.remote = urlParams.get('remote') || urlParams.get('rem') || "nosecurity"; session.remote = session.remote.trim(); } if (urlParams.has('latency') || urlParams.has('al') || urlParams.has('audiolatency')) { log("latency ENABLED"); session.audioLatency = urlParams.get('latency') || urlParams.get('al') || urlParams.get('audiolatency'); session.audioLatency = parseInt(session.audioLatency) || 0; session.disableWebAudio = false; } if (urlParams.has('micdelay') || urlParams.has('delay') || urlParams.has('md')) { log("audio gain ENABLED"); session.micDelay = urlParams.get('micdelay') || urlParams.get('delay') || urlParams.get('md'); session.micDelay = parseInt(session.micDelay) || 0; session.disableWebAudio = false; } if (urlParams.has('audiogain') || urlParams.has('gain') || urlParams.has('g')) { log("audio gain ENABLED"); session.audioGain = urlParams.get('audiogain') || urlParams.get('gain') || urlParams.get('g'); session.audioGain = parseInt(session.audioGain) || 0; session.disableWebAudio = false; } if (urlParams.has('compressor') || urlParams.has('comp')) { log("audio gain ENABLED"); session.compressor = 1; session.disableWebAudio = false; } else if (urlParams.has('limiter')) { log("audio gain ENABLED"); session.compressor = 2; session.disableWebAudio = false; } if ((urlParams.has('equalizer')) || (urlParams.has('eq'))) { session.equalizer = true; session.disableWebAudio = false; } if ((urlParams.has('lowcut')) || (urlParams.has('lc')) || (urlParams.has('higpass'))) { session.lowcut = urlParams.get('lowcut') || urlParams.get('lc') || urlParams.get('higpass') || 100; session.lowcut = parseInt(session.lowcut); session.disableWebAudio = false; } if (urlParams.has('pip')) { session.pip = true; // togglePip //session.manual=true; //innerHTML = } if (urlParams.has('keyframeinterval') || urlParams.has('keyframerate') || urlParams.has('keyframe') || urlParams.has('fki')) { log("keyframerate ENABLED"); session.keyframerate = parseInt(urlParams.get('keyframeinterval') || urlParams.get('keyframerate') || urlParams.get('keyframe') || urlParams.get('fki')) || 0; } if (urlParams.has('tallyoff') || urlParams.has('obsoff') || urlParams.has('oo')) { log("OBS feedback disabled"); session.disableOBS = true; } if (urlParams.has('chroma')) { log("Chroma ENABLED"); getById("main").style.backgroundColor = "#" + (urlParams.get('chroma') || "000"); } if (urlParams.has("videodevice") || urlParams.has("vdevice") || urlParams.has('vd') || urlParams.has('device') || urlParams.has('d')) { session.videoDevice = urlParams.get("videodevice") || urlParams.get("vdevice") || urlParams.get("vd") || urlParams.get("device") || urlParams.get("d"); if (session.videoDevice === null) { session.videoDevice = "1"; } else if (session.videoDevice) { session.videoDevice = session.videoDevice.toLowerCase().replace(/[\W]+/g, "_"); } if (session.videoDevice == "false") { session.videoDevice = 0; } else if (session.videoDevice == "0") { session.videoDevice = 0; } else if (session.videoDevice == "no") { session.videoDevice = 0; } else if (session.videoDevice == "off") { session.videoDevice = 0; } else if (session.videoDevice == "snapcam") { session.videoDevice = "snap_camera"; } else if (session.videoDevice == "canon") { session.videoDevice = "eos"; } else if (session.videoDevice == "camlink") { session.videoDevice = "cam_link"; } else if (session.videoDevice == "ndi") { session.videoDevice = "newtek_ndi_video"; } else if (session.videoDevice == "") { session.videoDevice = 1; } else if (session.videoDevice == "1") { session.videoDevice = 1; } else if (session.videoDevice == "default") { session.videoDevice = 1; } else { // whatever the user entered I guess, santitized. session.videoDevice = session.videoDevice.replace(/[\W]+/g, "_").toLowerCase(); } if (session.videoDevice === 0) { getById("add_camera").innerHTML = "Share your Microphone"; miniTranslate(getById("add_camera"), "share-your-mic"); } getById("videoMenu").style.display = "none"; log("session.videoDevice:" + session.videoDevice); } // audioDevice if (urlParams.has("audiodevice") || urlParams.has("adevice") || urlParams.has("ad") || urlParams.has("device") || urlParams.has("d")) { session.audioDevice = urlParams.get("audiodevice") || urlParams.get("adevice") || urlParams.get("ad") || urlParams.get("device") || urlParams.get("d"); if (session.audioDevice === null) { session.audioDevice = "1"; } else if (session.audioDevice) { session.audioDevice = session.audioDevice.toLowerCase().replace(/[\W]+/g, "_"); } if (session.audioDevice == "false") { session.audioDevice = 0; } else if (session.audioDevice == "0") { session.audioDevice = 0; } else if (session.audioDevice == "no") { session.audioDevice = 0; } else if (session.audioDevice == "off") { session.audioDevice = 0; } else if (session.audioDevice == "") { session.audioDevice = 1; } else if (session.audioDevice == "1") { session.audioDevice = 1; } else if (session.audioDevice == "default") { session.audioDevice = 1; } else if (session.audioDevice == "ndi") { session.audioDevice = "line_newtek_ndi_audio"; } else { // whatever the user entered I guess session.audioDevice = session.audioDevice.replace(/[\W]+/g, "_").toLowerCase(); } if (session.videoDevice === 0) { if (session.audioDevice === 0) { getById("add_camera").innerHTML = "Click Start to Join"; miniTranslate(getById("add_camera"), "click-start-to-join"); getById("container-2").className = 'column columnfade advanced'; // Hide screen share on mobile getById("container-3").classList.add("skip-animation"); getById("container-3").classList.remove('pointer'); setTimeout(function() { previewWebcam(); }, 100); session.webcamonly = true; } } log("session.audioDevice:" + session.audioDevice); getById("audioMenu").style.display = "none"; getById("headphonesDiv").style.display = "none"; getById("headphonesDiv2").style.display = "none"; getById("audioScreenShare1").style.display = "none"; } if (urlParams.has("autojoin") || urlParams.has("autostart") || urlParams.has('aj') || urlParams.has('as')) { session.autostart = true; if (session.screenshare) { setTimeout(function() { publishScreen(); }, 200); } } if (urlParams.has('noiframe') || urlParams.has('noiframes') || urlParams.has('nif')) { session.noiframe = urlParams.get('noiframe') || urlParams.get('noiframes') || urlParams.get('nif'); if (!(session.noiframe)) { session.noiframe = []; } else { session.noiframe = session.noiframe.split(","); } log("disable iframe playback"); log(session.noiframe); } if (urlParams.has('exclude') || urlParams.has('ex')) { session.exclude = urlParams.get('exclude') || urlParams.get('ex'); if (!(session.exclude)) { session.exclude = false; } else { session.exclude = session.exclude.split(","); } log("exclude video playback"); log(session.exclude); } if (urlParams.has('novideo') || urlParams.has('nv') || urlParams.has('hidevideo') || urlParams.has('showonly')) { session.novideo = urlParams.get('novideo') || urlParams.get('nv') || urlParams.get('hidevideo') || urlParams.get('showonly'); if (!(session.novideo)) { session.novideo = []; } else { session.novideo = session.novideo.split(","); } log("disable video playback"); log(session.novideo); } if (urlParams.has('noaudio') || urlParams.has('na') || urlParams.has('hideaudio')) { session.noaudio = urlParams.get('noaudio') || urlParams.get('na') || urlParams.get('hideaudio'); if (!(session.noaudio)) { session.noaudio = []; } else { session.noaudio = session.noaudio.split(","); } log("disable audio playback"); } if (urlParams.has('forceios')) { log("allow iOS to work in video group chat; for this user at least"); session.forceios = true; } if (urlParams.has('nocursor')) { session.nocursor = true; log("DISABLE CURSOR"); var style = document.createElement('style'); style.innerHTML = ` video { margin: 0; padding: 0; overflow: hidden; cursor: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=), none; user-select: none; } `; document.head.appendChild(style); } if (urlParams.has('vbr')) { session.cbr = 0; } if (urlParams.has('order')) { session.order = parseInt(urlParams.get('order')) || 0; } if (urlParams.has('sensors') || urlParams.has('sensor') || urlParams.has('gyro') || urlParams.has('gyros') || urlParams.has('accelerometer')) { session.sensorData = urlParams.get('sensors') || urlParams.get('sensor') || urlParams.get('gyro') || urlParams.get('gyros') || urlParams.get('accelerometer') || 30; session.sensorData = parseInt(session.sensorData); } if (urlParams.has('minptime')) { session.minptime = parseInt(urlParams.get('minptime')) || 10; if (session.minptime < 10) { session.minptime = 10; } if (session.minptime > 300) { session.minptime = 300; } } if (urlParams.has('maxptime')) { session.maxptime = parseInt(urlParams.get('maxptime')) || 60; if (session.maxptime < 10) { session.maxptime = 10; } if (session.maxptime > 300) { session.maxptime = 300; } } if (urlParams.has('ptime')) { session.ptime = parseInt(urlParams.get('ptime')) || 20; if (session.minptime < 10) { session.ptime = 10; } if (session.minptime > 300) { session.ptime = 300; } } if (urlParams.has('codec')) { log("CODEC CHANGED"); session.codec = urlParams.get('codec').toLowerCase(); if (session.codec=="webp"){ session.webp = true; session.codec = false; } } //else if (window.obsstudio){ //if (session.obsfix===false){ // session.codec = "h264"; // H264 --- It's too laggy for many!!! //} //} if (urlParams.has('nonacks')){ // disables error control / throttling. session.noNacks = true; } if (urlParams.has('nopli')){ // disables error control / throttling. session.noPLIs = true; } if (urlParams.has('noremb')){ // disables error control / throttling. session.noREMB = true; } if (urlParams.has('scale')) { if (urlParams.get('scale') == "false") {} else if (urlParams.get('scale') == "0") {} else if (urlParams.get('scale') == "no") {} else if (urlParams.get('scale') == "off") {} else { log("Resolution scale requested"); session.scale = parseInt(urlParams.get('scale')) || 100; } session.dynamicScale = false; // default true } var ConfigSettings = getById("main-js"); var ln_template = false; var translation = false; try { if (ConfigSettings) { ln_template = ConfigSettings.getAttribute('data-translation'); // Translations if (typeof ln_template === "undefined") { ln_template = false; } else if (ln_template === null) { ln_template = false; } } if (urlParams.has('ln')) { ln_template = urlParams.get('ln'); } } catch (e) { errorlog(e); } if (ln_template) { // checking if manual lanuage override enabled try { log("Template: " + ln_template); fetch("./translations/" + ln_template + '.json').then(function(response) { if (response.status !== 200) { log('Looks like there was a problem. Status Code: ' + response.status); return; } response.json().then(function(data) { log(data); translation = data; var trans = data.innerHTML; var allItems = document.querySelectorAll('[data-translate]'); allItems.forEach(function(ele) { if (ele.dataset.translate in trans) { ele.innerHTML = trans[ele.dataset.translate]; } }); trans = data.titles; var allTitles = document.querySelectorAll('[title]'); allTitles.forEach(function(ele) { var key = ele.title.replace(/[\W]+/g, "-").toLowerCase(); if (key in trans) { ele.title = trans[key]; } }); trans = data.placeholders; var allPlaceholders = document.querySelectorAll('[placeholder]'); allPlaceholders.forEach(function(ele) { var key = ele.placeholder.replace(/[\W]+/g, "-").toLowerCase(); if (key in trans) { ele.placeholder = trans[key]; } }); getById("mainmenu").style.opacity = 1; }).catch(function(err) { errorlog(err); getById("mainmenu").style.opacity = 1; }); }).catch(function(err) { errorlog(err); getById("mainmenu").style.opacity = 1; }); } catch (error) { errorlog(error); getById("mainmenu").style.opacity = 1; } } else if (location.hostname !== "obs.ninja") { if (location.hostname === "rtc.ninja") { try { if (session.label === false) { document.title = ""; } getById("qos").innerHTML = ""; getById("logoname").innerHTML = ""; getById("helpbutton").style.display = "none"; getById("helpbutton").style.opacity = 0; getById("reportbutton").style.display = "none"; getById("reportbutton").style.opacity = 0; getById("mainmenu").style.opacity = 1; getById("mainmenu").style.margin = "30px 0"; getById("translateButton").style.display = "none"; getById("translateButton").style.opacity = 0; getById("info").style.display = "none"; getById("info").style.opacity = 0; getById("chatBody").innerHTML = ""; } catch (e) {} } try { fetch("./translations/blank.json").then(function(response) { if (response.status !== 200) { log('Looks like there was a problem. Status Code: ' + response.status); return; } response.json().then(function(data) { log(data); var trans = data.innerHTML; var allItems = document.querySelectorAll('[data-translate]'); allItems.forEach(function(ele) { if (ele.dataset.translate in trans) { ele.innerHTML = trans[ele.dataset.translate]; } }); trans = data.titles; var allTitles = document.querySelectorAll('[title]'); allTitles.forEach(function(ele) { var key = ele.title.replace(/[\W]+/g, "-").toLowerCase(); if (key in trans) { ele.title = trans[key]; } }); trans = data.placeholders; var allPlaceholders = document.querySelectorAll('[placeholder]'); allPlaceholders.forEach(function(ele) { var key = ele.placeholder.replace(/[\W]+/g, "-").toLowerCase(); if (key in trans) { ele.placeholder = trans[key]; } }); if (session.label === false) { document.title = location.hostname; } getById("qos").innerHTML = location.hostname; getById("logoname").innerHTML = getById("qos").outerHTML; getById("helpbutton").style.display = "none"; getById("reportbutton").style.display = "none"; getById("mainmenu").style.opacity = 1; }).catch(function(err) { errorlog(err); getById("mainmenu").style.opacity = 1; }); }).catch(function(err) { errorlog(err); getById("mainmenu").style.opacity = 1; }); if (session.label === false) { document.title = location.hostname; } getById("qos").innerHTML = location.hostname; getById("logoname").innerHTML = getById("qos").outerHTML; getById("helpbutton").style.display = "none"; getById("reportbutton").style.display = "none"; getById("chatBody").innerHTML = ""; } catch (error) { errorlog(error); } } else { // check if automatic language translation is available getById("mainmenu").style.opacity = 1; } try { if (location.hostname === "rtc.ninja") { // an extra-brand-free version of OBS.Ninja if (session.label === false) { document.title = ""; } getById("qos").innerHTML = ""; getById("logoname").innerHTML = ""; getById("helpbutton").style.display = "none"; getById("helpbutton").style.opacity = 0; getById("reportbutton").style.display = "none"; getById("reportbutton").style.opacity = 0; getById("mainmenu").style.opacity = 1; getById("mainmenu").style.margin = "30px 0"; getById("translateButton").style.display = "none"; getById("translateButton").style.opacity = 0; getById("info").style.display = "none"; getById("info").style.opacity = 0; getById("chatBody").innerHTML = ""; } else if (location.hostname !== "obs.ninja") { if (session.label === false) { document.title = location.hostname; } getById("qos").innerHTML = sanitizeLabel(location.hostname); getById("logoname").innerHTML = getById("qos").outerHTML; getById("helpbutton").style.display = "none"; getById("reportbutton").style.display = "none"; } } catch (e) {} if (isIFrame) { getById("helpbutton").style.display = "none"; getById("helpbutton").style.opacity = 0; getById("reportbutton").style.display = "none"; getById("reportbutton").style.opacity = 0; getById("chatBody").innerHTML = ""; } function miniTranslate(ele, ident = false) { if (ident) { ele.dataset.translate = ident; } else { ident = ele.dataset.translate; } try { if (ident in translation.innerHTML) { ele.innerHTML = translation.innerHTML[ident]; } } catch (e) {} } function changeLg(lang) { fetch("./translations/" + lang + '.json').then(function(response) { if (response.status !== 200) { logerror('Language translation file not found.' + response.status); return; } response.json().then(function(data) { log(data); translation = data; // translation.innerHTML[ele.dataset.translate] var trans = data.innerHTML; var allItems = document.querySelectorAll('[data-translate]'); allItems.forEach(function(ele) { if (ele.dataset.translate in trans) { ele.innerHTML = trans[ele.dataset.translate]; } }); trans = data.titles; var allTitles = document.querySelectorAll('[title]'); allTitles.forEach(function(ele) { var key = ele.title.replace(/[\W]+/g, "-").toLowerCase(); if (key in trans) { ele.title = trans[key]; } }); trans = data.placeholders; var allPlaceholders = document.querySelectorAll('[placeholder]'); allPlaceholders.forEach(function(ele) { var key = ele.placeholder.replace(/[\W]+/g, "-").toLowerCase(); if (key in trans) { ele.placeholder = trans[key]; } }); }); }).catch(function(err) { errorlog(err); }); } if (urlParams.has('beep') || urlParams.has('notify') || urlParams.has('tone')) { session.beepToNotify = true; } if (urlParams.has('r2d2')) { getById("testtone").innerHTML = ""; getById("testtone").src = "./media/robot.mp3"; session.beepToNotify = true; } if (urlParams.has('videobitrate') || urlParams.has('bitrate') || urlParams.has('vb')) { session.bitrate = urlParams.get('videobitrate') || urlParams.get('bitrate') || urlParams.get('vb'); if (session.bitrate) { if ((session.view_set) && (session.bitrate.split(",").length > 1)) { session.bitrate_set = session.bitrate.split(","); session.bitrate = parseInt(session.bitrate_set[0]); } else { session.bitrate = parseInt(session.bitrate); } if (session.bitrate < 1) { session.bitrate = false; } log("BITRATE ENABLED"); log(session.bitrate); } } if (urlParams.has('maxvideobitrate') || urlParams.has('maxbitrate') || urlParams.has('mvb')) { session.maxvideobitrate = urlParams.get('maxvideobitrate') || urlParams.get('maxbitrate') || urlParams.get('mvb'); session.maxvideobitrate = parseInt(session.maxvideobitrate); if (session.maxvideobitrate < 1) { session.maxvideobitrate = false; } log("maxvideobitrate ENABLED"); log(session.maxvideobitrate); } if (urlParams.has('totalroombitrate') || urlParams.has('totalroomvideobitrate') || urlParams.has('trb')) { session.totalRoomBitrate = urlParams.get('totalroombitrate') || urlParams.get('totalroomvideobitrate') || urlParams.get('trb'); session.totalRoomBitrate = parseInt(session.totalRoomBitrate); if (session.totalRoomBitrate < 1) { session.totalRoomBitrate = false; } log("totalRoomBitrate ENABLED"); log(session.totalRoomBitrate); } if (urlParams.has('height') || urlParams.has('h')) { session.height = urlParams.get('height') || urlParams.get('h'); session.height = parseInt(session.height); } if (urlParams.has('width') || urlParams.has('w')) { session.width = urlParams.get('width') || urlParams.get('w'); session.width = parseInt(session.width); } if (urlParams.has('quality') || urlParams.has('q')) { try { session.quality = urlParams.get('quality') || urlParams.get('q') || 0; session.quality = parseInt(session.quality); getById("gear_screen").parentNode.removeChild(getById("gear_screen")); getById("gear_webcam").parentNode.removeChild(getById("gear_webcam")); } catch (e) { errorlog(e); } } if (urlParams.has('sink')) { session.sink = urlParams.get('sink'); } else if (urlParams.has('outputdevice') || urlParams.has('od') || urlParams.has('audiooutput')) { session.outputDevice = urlParams.get('outputdevice') || urlParams.get('od') || urlParams.get('audiooutput'); if (session.outputDevice) { session.outputDevice = session.outputDevice.toLowerCase().replace(/[\W]+/g, "_"); } else { session.outputDevice = false; } if (session.outputDevice) { try { enumerateDevices().then(function(deviceInfos) { for (let i = 0; i !== deviceInfos.length; ++i) { if (deviceInfos[i].kind === 'audiooutput') { if (deviceInfos[i].label.replace(/[\W]+/g, "_").toLowerCase().includes(session.outputDevice)) { session.sink = deviceInfos[i].deviceId; log("AUDIO OUT DEVICE: " + deviceInfos[i].deviceId); break; } } } }); } catch (e) {} } } if (urlParams.has('fullscreen')) { session.fullscreen = true; } if (urlParams.has('stats')) { session.statsMenu = true; } if (urlParams.has('cleandirector') || urlParams.has('cdv')) { session.cleanDirector = true; } if (urlParams.has('cleanoutput') || urlParams.has('clean') || urlParams.has('cleanish')) { session.cleanOutput = true; getById("translateButton").style.display = "none"; getById("credits").style.display = "none"; getById("header").style.display = "none"; getById("controlButtons").style.display = "none"; var style = document.createElement('style'); style.innerHTML = ` video { background-image: none; } `; document.head.appendChild(style); } if (urlParams.has('cleanish')) { session.cleanish = true; } if (urlParams.has('channels')) { // must be loaded before channelOffset session.audioChannels = parseInt(urlParams.get('channels')); session.offsetChannel = 0; log("max channels is 32; channels offset"); session.audioEffects = true; } if (urlParams.has('channeloffset')) { session.offsetChannel = parseInt(urlParams.get('channeloffset')); log("max channels is 32; channels offset"); session.audioEffects = true; } if (urlParams.has('enhance')) { //if (parseInt(urlParams.get('enhance')>0){ session.enhance = true; //parseInt(urlParams.get('enhance')); //} } if (urlParams.has('maxviewers') || urlParams.has('mv')) { session.maxviewers = urlParams.get('maxviewers') || urlParams.get('mv'); if (session.maxviewers.length == 0) { session.maxviewers = 1; } else { session.maxviewers = parseInt(session.maxviewers); } log("maxviewers set"); } if (urlParams.has('maxpublishers') || urlParams.has('mp')) { session.maxpublishers = urlParams.get('maxpublishers') || urlParams.get('mp'); if (session.maxpublishers.length == 0) { session.maxpublishers = 1; } else { session.maxpublishers = parseInt(session.maxpublishers); } log("maxpublishers set"); } if (urlParams.has('maxconnections') || urlParams.has('mc')) { session.maxconnections = urlParams.get('maxconnections') || urlParams.get('maxconnections'); if (session.maxconnections.length == 0) { session.maxconnections = 1; } else { session.maxconnections = parseInt(session.maxconnections); } log("maxconnections set"); } if (urlParams.has('secure')) { session.security = true; if (!(session.cleanOutput)) { setTimeout(function() { warnUser("Enhanced Security Mode Enabled."); }, 100); } } if (urlParams.has('random') || urlParams.has('randomize')) { session.randomize = true; } if (urlParams.has('framerate') || urlParams.has('fr') || urlParams.has('fps')) { session.framerate = urlParams.get('framerate') || urlParams.get('fr') || urlParams.get('fps'); session.framerate = parseInt(session.framerate); log("framerate Changed"); log(session.framerate); } if (urlParams.has('maxframerate') || urlParams.has('mfr') || urlParams.has('mfps')) { session.maxframerate = urlParams.get('maxframerate') || urlParams.get('mfr') || urlParams.get('mfps'); session.maxframerate = parseInt(session.maxframerate); log("max framerate assigned"); log(session.maxframerate); } if (urlParams.has('buffer')) { // needs to be before sync session.buffer = parseFloat(urlParams.get('buffer')) || 0; log("buffer Changed: " + session.buffer); session.sync = 0; session.audioEffects = true; } if (urlParams.has('sync')) { session.sync = parseFloat(urlParams.get('sync')); log("sync Changed; in milliseconds. If not set, defaults to auto."); log(session.sync); session.audioEffects = true; if (session.buffer === false) { session.buffer = 0; } } if (urlParams.has('mirror')) { if (urlParams.get('mirror') == "3") { getById("main").classList.add("mirror"); } else if (urlParams.get('mirror') == "2") { session.mirrored = 2; } else if (urlParams.get('mirror') == "0") { session.mirrored = 0; } else if (urlParams.get('mirror') == "false") { session.mirrored = 0; } else if (urlParams.get('mirror') == "off") { session.mirrored = 0; } else { session.mirrored = 1; } } if (urlParams.has('flip')) { if (urlParams.get('flip') == "0") { session.flipped = false; } else if (urlParams.get('flip') == "false") { session.flipped = false; } else if (urlParams.get('flip') == "off") { session.flipped = false; } else { session.flipped = true; } } if ((session.mirrored) && (session.flipped)) { try { log("Mirror all videos"); var mirrorStyle = document.createElement('style'); mirrorStyle.innerHTML = "video {transform: scaleX(-1) scaleY(-1); }"; document.getElementsByTagName("head")[0].appendChild(mirrorStyle); } catch (e) { errorlog(e); } } else if (session.mirrored) { // mirror the video horizontally try { log("Mirror all videos"); var mirrorStyle = document.createElement('style'); mirrorStyle.innerHTML = "video {transform: scaleX(-1);}"; document.getElementsByTagName("head")[0].appendChild(mirrorStyle); } catch (e) { errorlog(e); } } else if (session.flipped) { // mirror the video vertically try { log("Mirror all videos"); var mirrorStyle = document.createElement('style'); mirrorStyle.innerHTML = "video {transform: scaleY(-1);}"; document.getElementsByTagName("head")[0].appendChild(mirrorStyle); } catch (e) { errorlog(e); } } if (urlParams.has('icefilter')) { log("ICE FILTER ENABLED"); session.icefilter = urlParams.get('icefilter'); } if (urlParams.has('effects') || urlParams.has('effect')) { session.effects = urlParams.get('effects') || urlParams.get('effect') || 0; session.effects = parseInt(session.effects); if (session.effects === 0){ getById("effectsDiv").style.display = "block"; } // mirror == 2 // face == 1 // blur = 3 // green = 4 // image = 5 // avatar = 6 } if (urlParams.has('activespeaker') || urlParams.has('speakerview') || urlParams.has('sas')){ session.activeSpeaker = true; //if (session.audioEffects === null) { session.audioEffects = true; session.audioMeterGuest = true; //} setInterval(function(){activeSpeaker(false)},100); }// else { // if (session.scene!==false){ // setInterval(function(){activeSpeaker(true)},100); // } //} if (urlParams.has('style') || urlParams.has('st')) { session.style = urlParams.get('style') || urlParams.get('st') || 1; if ((parseInt(session.style) == 1) || (session.style == "justvideo")) { // no audio only session.style = 1; } else if ((parseInt(session.style) == 2) || (session.style == "waveform")) { // audio waveform session.style = 2; session.audioEffects = true; ////!!!!!!! Do I want to enable the audioEffects myself? or do it here? } else if ((parseInt(session.style) == 3) || (session.style == "volume")) { // photo is taken? upload option? canvas? session.style = 3; session.audioEffects = true; } else { session.style = 1; } } if (urlParams.has('samplerate') || urlParams.has('sr')) { session.sampleRate = parseInt(urlParams.get('samplerate')) || parseInt(urlParams.get('samplerate')) || 48000; if (session.audioCtx) { session.audioCtx.close(); // close the default audio context. } session.audioCtx = new AudioContext({ // create a new audio context with a higher sample rate. sampleRate: session.sampleRate }); session.audioEffects = true; } if (urlParams.has('noaudioprocessing') || urlParams.has('noap')) { session.disableWebAudio = true; // default true; might be useful to disable on slow or old computers? session.audioEffects = false; // disable audio inbound effects also. session.audioMeterGuest = false; } if (urlParams.has('tcp')){ // forces the TURN servers to use TCP mode; still need to add &private to force TURN also tho session.forceTcpMode = true; } if (urlParams.has('speedtest')){ // forces essentially UDP mode, unless TCP is specified, and some other stuff session.speedtest = true; } if (urlParams.has('turn')) { var turnstring = urlParams.get('turn'); if (turnstring == "twilio") { // a sample function on loading remote credentials for TURN servers. try { session.ws = false; // prevents connection var twillioRequest = new XMLHttpRequest(); twillioRequest.onreadystatechange = function() { if (twillioRequest.status === 200) { try{ var res = JSON.parse(twillioRequest.responseText); } catch(e){return;} session.configuration = { iceServers: [{ "username": res["1"], "credential": res["2"], "url": "turn:global.turn.twilio.com:3478?transport=tcp", "urls": "turn:global.turn.twilio.com:3478?transport=tcp" }, { "username": res["1"], "credential": res["2"], "url": "turn:global.turn.twilio.com:443?transport=tcp", "urls": "turn:global.turn.twilio.com:443?transport=tcp" } ], sdpSemantics: 'unified-plan' // future-proofing }; if (session.ws===false){ session.ws=null; // allows connection (clears state) session.connect(); // connect if not already connected. } } // system does not connect if twilio API does not respond. }; twillioRequest.open('GET', 'https://api.obs.ninja:1443/twilio', true); // `false` makes the request synchronous twillioRequest.send(); } catch (e) { errorlog("Twilio Failed"); } } else if ((turnstring == "false") || (turnstring == "off") || (turnstring == "0")) { // disable TURN servers session.configuration = { iceServers: [ { urls: ["stun:stun.l.google.com:19302", "stun:stun4.l.google.com:19302"]} // more than 4 stun+turn servers will cause firefox issues? (2 + 2 for now then) ], sdpSemantics: 'unified-plan' // future-proofing }; } else { try { turnstring = turnstring.split(";"); if (turnstring !== "false") { // false disables the TURN server. Useful for debuggin var turn = {}; turn.username = turnstring[0]; // myusername turn.credential = turnstring[1]; //mypassword turn.urls = [turnstring[2]]; // ["turn:turn.obs.ninja:443"]; session.configuration.iceServers = [{ urls: ["stun:stun.l.google.com:19302", "stun:stun4.l.google.com:19302"] }]; session.configuration.iceServers.push(turn); } } catch (e) { if (!(session.cleanOutput)) { warnUser("TURN server parameters were wrong."); } errorlog(e); } } } else { chooseBestTURN(); // obs.ninja turn servers, if needed. } if (urlParams.has('privacy') || urlParams.has('private') || urlParams.has('relay')) { // please only use if you are also using your own TURN service. session.privacy = true; try { session.configuration.iceTransportPolicy = "relay"; // https://developer.mozilla.org/en-US/docs/Web/API/RTCIceCandidate/address } catch (e) { if (!(session.cleanOutput)) { warnUser("Privacy mode failed to configure."); } errorlog(e); } if (session.speedtest){ if (session.maxvideobitrate !== false) { if (session.maxvideobitrate > 6000) { session.maxvideobitrate = 6000; // Please feel free to get rid of this if using your own TURN servers... } } else { session.maxvideobitrate = 6000; // don't let people pull more than 2500 from you } if (session.bitrate !== false) { if (session.bitrate > 6000) { session.bitrate = 6000; // Please feel free to get rid of this if using your own TURN servers... } } } else { if (session.maxvideobitrate !== false) { if (session.maxvideobitrate > 2500) { session.maxvideobitrate = 2500; // Please feel free to get rid of this if using your own TURN servers... } } else { session.maxvideobitrate = 2500; // don't let people pull more than 2500 from you } if (session.bitrate !== false) { if (session.bitrate > 2500) { session.bitrate = 2500; // Please feel free to get rid of this if using your own TURN servers... } } } } if (urlParams.has('wss')) { if (urlParams.get('wss')) { session.wss = "wss://" + urlParams.get('wss'); } } if (urlParams.has('queue')) { session.queue = true; } if (isIFrame) { // reduce CPU load if not needed. window.onmessage = function(e) { // iFRAME support log(e); try { if ("function" in e.data) { // these are calling in-app functions, with perhaps a callback -- TODO: add callbacks var ret = null; if (e.data.function === "previewWebcam") { ret = previewWebcam(); } else if (e.data.function === "changeHTML") { ret = getById(e.data.target); ret.innerHTML = e.data.value; } else if (e.data.function === "publishScreen") { ret = publishScreen(); } else if (e.data.function === "eval") { eval(e.data.value); // eval == evil ; feedback welcomed } } } catch (err) { errorlog(err); } if ("sendChat" in e.data) { sendChat(e.data.sendChat); // sends to all peers; more options down the road } // Chat out gets called via getChatMessage function // Related code: parent.postMessage({"chat": {"msg":-----,"type":----,"time":---} }, "*"); if ("mic" in e.data) { // this should work for the director's mic mute button as well. Needs to be manually enabled the first time still tho. if (e.data.mic === true) { // unmute session.muted = false; // set log(session.muted); toggleMute(true); // apply } else if (e.data.mic === false) { // mute session.muted = true; // set log(session.muted); toggleMute(true); // apply } else if (e.data.mic === "toggle") { // toggle toggleMute(); } } if ("camera" in e.data) { // this should work for the director's mic mute button as well. Needs to be manually enabled the first time still tho. if (e.data.camera === true) { // unmute session.videoMuted = false; // set log(session.videoMuted); toggleVideoMute(true); // apply } else if (e.data.camera === false) { // mute session.videoMuted = true; // set log(session.videoMuted); toggleVideoMute(true); // apply } else if (e.data.camera === "toggle") { // toggle toggleVideoMute(); } } if ("mute" in e.data) { if (e.data.mute === true) { // unmute session.speakerMuted = true; // set toggleSpeakerMute(true); // apply } else if (e.data.mute === false) { // mute session.speakerMuted = false; // set toggleSpeakerMute(true); // apply } else if (e.data.mute === "toggle") { // toggle toggleSpeakerMute(); } } else if ("speaker" in e.data) { // same thing as mute. if (e.data.speaker === true) { // unmute session.speakerMuted = false; // set toggleSpeakerMute(true); // apply } else if (e.data.speaker === false) { // mute session.speakerMuted = true; // set toggleSpeakerMute(true); // apply } else if (e.data.speaker === "toggle") { // toggle toggleSpeakerMute(); } } if ("record" in e.data) { if (e.data.record == false) { // mute if ("recording" in session.videoElement) { recordLocalVideo("stop"); } } else if (e.data.record == true){ if ("recording" in session.videoElement) { // already recording } else { recordLocalVideo("start"); } } } if ("volume" in e.data) { for (var i in session.rpcs) { try { session.rpcs[i].videoElement.volume = parseFloat(e.data.volume); } catch (e) { errorlog(e); } } } if ("bitrate" in e.data) { for (var i in session.rpcs) { try { session.requestRateLimit(parseInt(e.data.bitrate), i); } catch (e) { errorlog(e); } } } if ("audiobitrate" in e.data) { for (var i in session.rpcs) { try { session.requestAudioRateLimit(parseInt(e.data.audiobitrate), i); } catch (e) { errorlog(e); } } } if ("sceneState" in e.data) { // TRUE OR FALSE - tells the connected peers if they are live or not via a tally light change. var visibility = e.data.sceneState; var bundle = {}; bundle.sceneUpdate = []; for (var UUID in session.rpcs) { if (session.rpcs[UUID].visibility !== visibility) { // only move forward if there is a change; the event likes to double fire you see. session.rpcs[UUID].visibility = visibility; var msg = {}; msg.visibility = visibility; if (session.rpcs[UUID].videoElement.style.display == "none") { // Flag will be left alone, but message will say its disabled. msg.visibility = false; } msg.UUID = UUID; session.sendRequest(msg, UUID); bundle.sceneUpdate.push(msg); } } session.sendRequest(bundle); // we want all publishing peers to know the state } if ("sendMessage" in e.data) { // webrtc send to viewers session.sendMessage(e.data); } if ("sendRequest" in e.data) { // webrtc send to publishers session.sendRequest(e.data); } if ("sendPeers" in e.data) { // webrtc send message to every connected peer; like send and request; a hammer vs a knife. session.sendPeers(e.data); } if ("reload" in e.data) { location.reload(); } if ("getStats" in e.data) { var stats = {}; stats.total_outbound_connections = Object.keys(session.pcs).length; stats.total_inbound_connections = Object.keys(session.rpcs).length; stats.inbound_stats = {}; for (var i in session.rpcs) { stats.inbound_stats[session.rpcs[i].streamID] = session.rpcs[i].stats; } for (var uuid in session.pcs) { setTimeout(function(UUID) { session.pcs[UUID].getStats().then(function(stats) { stats.forEach(stat => { if (stat.type == "outbound-rtp") { if (stat.kind == "video") { if ("qualityLimitationReason" in stat) { session.pcs[UUID].stats.quality_limitation_reason = stat.qualityLimitationReason; } if ("framesPerSecond" in stat) { session.pcs[UUID].stats.resolution = stat.frameWidth + " x " + stat.frameHeight + " @ " + stat.framesPerSecond; } if ("encoderImplementation" in stat) { session.pcs[UUID].stats.encoder = stat.encoderImplementation; } } } else if (stat.type == "remote-candidate") { if ("relayProtocol" in stat) { if ("ip" in stat) { session.pcs[UUID].stats.remote_relay_IP = stat.ip; } session.pcs[UUID].stats.remote_relayProtocol = stat.relayProtocol; } if ("candidateType" in stat) { session.pcs[UUID].stats.remote_candidateType = stat.candidateType; } } else if (stat.type == "local-candidate") { if ("relayProtocol" in stat) { if ("ip" in stat) { session.pcs[UUID].stats.local_relayIP = stat.ip; } session.pcs[UUID].stats.local_relayProtocol = stat.relayProtocol; } if ("candidateType" in stat) { session.pcs[UUID].stats.local_candidateType = stat.candidateType; } } else if ((stat.type == "candidate-pair" ) && (stat.nominated)) { if ("availableOutgoingBitrate" in stat){ session.pcs[UUID].stats.available_outgoing_bitrate_kbps = parseInt(stat.availableOutgoingBitrate/1024); } if ("totalRoundTripTime" in stat){ session.pcs[UUID].stats.total_roundTripTime_ms = stat.totalRoundTripTime*1000; } } return; }); return; }); }, 0, uuid); } setTimeout(function() { stats.outbound_stats = {}; for (var i in session.pcs) { stats.outbound_stats[i] = session.pcs[i].stats; } parent.postMessage({ "stats": stats }, "*"); }, 1000); } if ("getRemoteStats" in e.data) { session.sendRequest({"requestStats":true, "remote":session.remote}); } if ("getLoudness" in e.data) { log("GOT LOUDNESS REQUEST"); if (e.data.getLoudness == true) { session.pushLoudness = true; var loudness = {}; for (var i in session.rpcs) { loudness[session.rpcs[i].streamID] = session.rpcs[i].stats.Audio_Loudness; } parent.postMessage({ "loudness": loudness }, "*"); } else { session.pushLoudness = false; } } if ("getStreamIDs" in e.data) { if (e.data.getStreamIDs == true) { var streamIDs = {}; for (var i in session.rpcs) { streamIDs[session.rpcs[i].streamID] = session.rpcs[i].label; } parent.postMessage({ "streamIDs": streamIDs }, "*"); } } if ("close" in e.data) { for (var i in session.rpcs) { try { session.rpcs[i].close(); } catch (e) { errorlog(e); } } } if ("style" in e.data) { try { const style = document.createElement('style'); style.textContent = e.data.style; document.head.append(style); log(style); } catch (e) { errorlog(e); } } if ("automixer" in e.data) { if (e.data.automixer == true) { session.manual = false; try { updateMixer(); } catch (e) {} } else if (e.data.automixer == false) { session.manual = true; } } if ("target" in e.data) { log(e.data); for (var i in session.rpcs) { try { if ("streamID" in session.rpcs[i]) { if ((session.rpcs[i].streamID == e.data.target) || (e.data.target == "*")) { try { if ("settings" in e.data) { for (const property in e.data.settings) { session.rpcs[i].videoElement[property] = e.data.settings[property]; } } if ("add" in e.data) { getById("gridlayout").appendChild(session.rpcs[i].videoElement); } else if ("remove" in e.data) { try { session.rpcs[i].videoElement.parentNode.removeChild(session.rpcs[i].videoElement); } catch (e) { try { session.rpcs[i].videoElement.parentNode.parentNode.removeChild(session.rpcs[i].videoElement.parentNode); } catch (e) {} } } } catch (e) { errorlog(e); } } } } catch (e) { errorlog(e); } } } }; } function setupIncomingVideoTracking(v, UUID){ // video element. if (session.directorUUID===UUID){ v.muted=false; } else { v.muted = session.speakerMuted; } v.onpause = (event) => { // prevent things from pausing; human or other if (!((event.ctrlKey) || (event.metaKey) )){ warnlog("Video paused; force it to play again"); //return; //session.audioCtx.resume(); //log("ctx resume"); event.currentTarget.play().then(_ => { log("playing"); }).catch(error => { warnlog("didnt play 1"); }); } } v.onplay = function(){ try { var bigPlayButton = document.getElementById("bigPlayButton"); if (bigPlayButton){ bigPlayButton.parentNode.removeChild(bigPlayButton); } } catch(e){} if (session.pip){ if (v.readyState >= 3){ if (!(v.pip)){ v.pip=true; toggleSystemPip(v, true); } } } } if (session.pip){ v.onloadedmetadata = function(){ if (!v.paused){ if (!(v.pip)){ v.pip=true; toggleSystemPip(v, true); } } } } v.addEventListener('resize', (e) => { log("resize event"); var aspectRatio = parseFloat(e.target.videoWidth/e.target.videoHeight); if (v.dataset.aspectRatio){ if (aspectRatio != v.dataset.aspectRatio){ setTimeout(function(){updateMixer();},1); // We don't want to run this on the first resize? just subsequent ones. } } else { log("ASPECT RATIO CHANGED"); setTimeout(function(){updateMixer();},1); } v.dataset.aspectRatio = parseFloat(e.target.videoWidth/e.target.videoHeight); }); v.volume = 1.0; // play audio automatically v.autoplay = true; v.controls = false; v.className += "tile"; v.setAttribute("playsinline",""); v.controlTimer = null; changeAudioOutputDevice(v); // if enabled, changes to desired output audio device. if (document.getElementById("mainmenu")){ var m = getById("mainmenu"); m.remove(); } if (session.director){ v.controls = true; var container = getById("videoContainer_"+UUID); v.disablePictureInPicture = false v.setAttribute("controls","controls") container.appendChild(v); session.requestRateLimit(session.directorViewBitrate,UUID); /// limit resolution for director if (session.beepToNotify) { playtone(); } } else if (session.scene!==false){ v.controls = false; if (session.view){ // specific video to be played v.style.display="block"; } else if (session.scene===0){ v.style.display="block"; } else { // group scene I guess; needs to be added manually v.style.display="none"; v.muted= true; } setTimeout(function(){updateMixer();},1); } else if (session.roomid!==false){ if (session.cleanOutput){ v.controls = false; } else if (window.obsstudio) { v.controls = false; } else { v.controls = true; } if ((session.roomid==="") && (session.bitrate)){ // let's keep the default bitrates, since this isn't a real room and bitrates are specified. } else if (session.novideo !== false){ if (session.novideo.includes(session.rpcs[UUID].streamID)){ session.requestRateLimit(0,UUID);// limit resolution for guests see ln: 1804 in main.js also } } else { session.requestRateLimit(0,UUID);// limit resolution for guests see ln: 1804 in main.js also } setTimeout(function(){updateMixer();},1); } else { v.style.display="block"; if (window.obsstudio) { v.controls = false; } setTimeout(function(){updateMixer();},1); } v.addEventListener('click', function(e) { // show stats of video if double clicked log("clicked"); try { if ((e.ctrlKey)||(e.metaKey)){ e.preventDefault(); var uid = e.currentTarget.dataset.UUID; if ("stats" in session.rpcs[uid]){ var [menu, innerMenu] = statsMenuCreator(); printViewStats(innerMenu, session.rpcs[uid].stats, session.rpcs[uid].streamID ); menu.interval = setInterval(printViewStats,3000, innerMenu, session.rpcs[uid].stats, session.rpcs[uid].streamID); } e.stopPropagation(); return false; } } catch(e){errorlog(e);} }); if (session.statsMenu){ if ("stats" in session.rpcs[UUID]){ if (getById("menuStatsBox")){ clearInterval(getById("menuStatsBox").interval); getById("menuStatsBox").remove(); } var [menu, innerMenu] = statsMenuCreator(); printViewStats(innerMenu, session.rpcs[UUID].stats, session.rpcs[UUID].streamID ); menu.interval = setInterval(printViewStats,3000, innerMenu, session.rpcs[UUID].stats, session.rpcs[UUID].streamID); } } v.touchTimeOut = null; v.touchLastTap = 0; v.touchCount = 0; v.addEventListener('touchend', function(event) { log("touched"); document.ontouchup = null; document.onmouseup = null; document.onmousemove = null; document.ontouchmove = null; var currentTime = new Date().getTime(); var tapLength = currentTime - v.touchLastTap; clearTimeout(v.touchTimeOut); if (tapLength < 500 && tapLength > 0) { /// log("double touched"); v.touchCount+=1; event.preventDefault(); if (v.touchCount<5){ v.touchLastTap = currentTime; return false; } v.touchLastTap = 0; v.touchCount=0; log("double touched"); var uid = event.currentTarget.dataset.UUID; if ("stats" in session.rpcs[uid]){ var [menu, innerMenu] = statsMenuCreator(); printViewStats(innerMenu, session.rpcs[uid].stats, session.rpcs[uid].streamID ); menu.interval = setInterval(printViewStats,3000, innerMenu, session.rpcs[uid].stats, session.rpcs[uid].streamID); } event.stopPropagation(); return false; ////// } else { v.touchCount=1; v.touchTimeOut = setTimeout(function(vv) { clearTimeout(vv.touchTimeOut); vv.touchLastTap = 0; vv.touchCount=0; }, 5000, v); v.touchLastTap = currentTime; } }); if (session.remote){ v.addEventListener("wheel", session.remoteControl); } if (v.controls == false){ v.addEventListener("click", function () { if (v.paused){ log("PLAYING MANUALLY?"); v.play().then(_ => { log("playing"); }).catch(warnlog); } }); if (session.nocursor==false){ // we do not want to show the controls. This is because MacOS + OBS does not work; so electron app needs this. if (!(session.cleanOutput)){ if (!(window.obsstudio)){ if (v.controlTimer){ clearInterval(v.controlTimer); } v.controlTimer = setTimeout(showControlBar.bind(null,v),3000); //v.controlTimer = setTimeout(function (){v.controls=true;},3000); // 3 seconds before I enable the controls automatically. This way it doesn't auto appear during loading. 3s enough, right? } } } } setTimeout(session.processStats, 1000, UUID); } function updateMixerRun(e=false){ // this is the main auto-mixing code. It's a giant function that runs when there are changes to screensize, video track statuses, etc. if (getById("subControlButtons").dragElement){ if (parseInt(getById("subControlButtons").style.top) > 0){ getById("subControlButtons").style.top = "0px"; } else if (parseInt(getById("subControlButtons").style.top) < parseInt(50 - window.innerHeight) ){ getById("subControlButtons").style.top = parseInt( 50 - window.innerHeight)+"px"; } if (parseInt(getById("subControlButtons").style.left) < 0){ getById("subControlButtons").style.left = "0px"; } else if (parseInt(getById("subControlButtons").style.left) > parseInt( window.innerWidth - getById("subControlButtons").offsetWidth) ){ getById("subControlButtons").style.left = parseInt( window.innerWidth -getById("subControlButtons").offsetWidth )+"px"; } } if (session.director){return;} if (session.manual === true){return;} var playarea = getById("gridlayout"); var header = getById("header"); var hi = header.offsetHeight ; var w = window.innerWidth; var h = window.innerHeight - hi; if ( window.innerHeight<=700 ){ if (document.getElementById("controlButtons")){ var h = window.innerHeight - hi - document.getElementById("controlButtons").offsetHeight; } else { var h = window.innerHeight - hi; } } if (session.aspectratio){ if (session.aspectratio==1){ var arW = 9.0; var arH = 16.0; } else if (session.aspectratio==2){ var arW = 1.0; var arH = 1.0; } } else { var arW = 16.0; var arH = 9.0; } var ww = w/arW; var hh = h/arH; var mediaPool = []; var mediaPool_invisible = []; if (session.videoElement){ // I, myself, exist if (session.videoElement.style.display!="none"){ // local feed if (session.minipreview && (session.infocus!==true)){ session.videoElement.onclick = function(){ if (session.infocus === true){ session.infocus = false; } else { session.infocus = true; log("session: myself"); } setTimeout(()=>updateMixer(),10); }; } else { if (session.order!==false){ session.videoElement.order=session.order; } else { session.videoElement.order=0; } if (session.activeSpeaker && (!session.activelySpeaking)){ mediaPool_invisible.push(session.videoElement); } else { mediaPool.push(session.videoElement); } } } } if (session.screenShareElement){ // I, myself, exist if (session.screenShareElement.style.display!="none"){ // local feed if (session.order!==false){ session.screenShareElement.order=session.order; } else { session.screenShareElement.order=0; } if (session.infocus!==false){ mediaPool_invisible.push(session.screenShareElement); } else if (session.activeSpeaker && (!session.activelySpeaking)){ mediaPool_invisible.push(session.screenShareElement); } else { mediaPool.push(session.screenShareElement); } } } if (session.iframeEle){ // I, myself, exist if (session.iframeEle.style.display!="none"){ // local feed if (session.order!==false){ session.iframeEle.order=session.order; } else { session.iframeEle.order=0; } if (session.activeSpeaker && (!session.activelySpeaking)){ mediaPool_invisible.push(session.iframeEle); } else { mediaPool.push(session.iframeEle); } } } if ((session.infocus) && (session.infocus in session.rpcs)){ // remote guest being full screened; infocus == UUID log(session.infocus+" set fullscreen"); mediaPool = []; // remove myself from fullscreen for (var j in session.rpcs){ if (j != session.infocus){ session.requestRateLimit(0, j); // disable the video of non-fullscreen videos try { if (session.rpcs[j].videoElement.style.display!="none"){ // Add it if not hidden mediaPool_invisible.push(session.rpcs[j].videoElement); } } catch(e){} } else { // in focus video //////// try { if (session.rpcs[j].order!==false){ session.rpcs[j].videoElement.order=session.rpcs[j].order; } else { session.rpcs[j].videoElement.order=0; } /////////// if (session.activeSpeaker && (!session.rpcs[j].activelySpeaking)){ mediaPool_invisible.push(session.rpcs[j].videoElement); } else { mediaPool.push(session.rpcs[j].videoElement); } session.rpcs[j].videoElement.style.visibility = "visible"; if ((session.rpcs[j].targetBandwidth!==-1) && (session.rpcs[j].targetBandwidth