From fdf5ce970d21f15a227a702c967fb5e77d601426 Mon Sep 17 00:00:00 2001 From: Steve Seguin Date: Thu, 4 Aug 2022 17:23:46 -0400 Subject: [PATCH] versus.cam 1.0 support added --- iframe.html | 6 + index.html | 12 +- lib.js | 336 ++++++++------ main.css | 38 +- main.js | 198 ++++++++- popout.html | 71 ++- stats.html | 1229 +-------------------------------------------------- webrtc.js | 2 +- 8 files changed, 499 insertions(+), 1393 deletions(-) diff --git a/iframe.html b/iframe.html index e75e3fd..37a8258 100644 --- a/iframe.html +++ b/iframe.html @@ -37,6 +37,11 @@ max-width:300px; max-height:100px; } + .container{ + max-height: 900px; + overflow-y: auto; + overflow-x: auto; + } @@ -82,7 +82,7 @@ - + - + @@ -2208,11 +2208,11 @@ // session.defaultBackgroundImages = ["./media/bg_sample1.webp", "./media/bg_sample2.webp"]; // for &effects=5 (virtual backgrounds) - + - + diff --git a/lib.js b/lib.js index 71dc553..8b3c3af 100644 --- a/lib.js +++ b/lib.js @@ -538,6 +538,10 @@ function createVideoElement(){ } catch(e){errorlog(e);} var v = document.createElement("video"); videoElements.push(v); + if (session.volume!==false){ + v.volume = session.volume; // setting default volume + log("setting volume to manual"); + } return v; } @@ -872,7 +876,7 @@ async function confirmAlt(inputText, block=false){ } var modalTimeout=null; -function warnUser(message, timeout=false){ +function warnUser(message, timeout=false, sanitize=true){ // Allows for multiple alerts to stack better. // Every modal and backdrop has an increasing z-index // to block the previous modal @@ -885,7 +889,9 @@ function warnUser(message, timeout=false){ zindex = 31 + document.querySelectorAll('.alertModal').length; try{ - message = sanitizeChat(message,2000); + if (sanitize){ + message = sanitizeChat(message,2000); + } message = message.replace(/\n/g,"
"); } catch(e){ errorlog(message); @@ -928,19 +934,19 @@ var sanitizeStreamID = function(streamID) { if (streamID.length < 1) { streamID = session.generateStreamID(8); if (!(session.cleanOutput)) { - warnUser(miscTranslations["no-streamID-provided"] + streamID); + warnUser(miscTranslations["no-streamID-provided"] + streamID, false, false); } } var streamID_sanitized = streamID.replace(/[\W]+/g, "_"); if (streamID !== streamID_sanitized) { if (!(session.cleanOutput)) { - warnUser(miscTranslations["alphanumeric-only"]); + warnUser(miscTranslations["alphanumeric-only"], false, false); } } if (streamID_sanitized.length > 44) { streamID_sanitized = streamID_sanitized.substring(0, 50); if (!(session.cleanOutput)) { - warnUser(miscTranslations["stream-id-too-long"]); + warnUser(miscTranslations["stream-id-too-long"], false, false); } } return streamID_sanitized; @@ -2257,8 +2263,11 @@ function setupIncomingScreenTracking(v, UUID){ // SCREEN element. setTimeout(function(){updateMixer();},1); } }); - - v.volume = 1.0; // play audio automatically + if (session.volume!==false){ + v.volume = session.volume; + } else { + v.volume = 1.0; // play audio automatically + } v.autoplay = true; v.controls = session.showControls || false; v.classList.add("tile"); @@ -2608,7 +2617,11 @@ function setupIncomingVideoTracking(v, UUID){ // video element. } }); - v.volume = 1.0; // play audio automatically + if (session.volume!==false){ + v.volume = session.volume; + } else { + v.volume = 1.0; // play audio automatically + } v.autoplay = true; v.controls = session.showControls || false; v.classList.add("tile"); @@ -2955,7 +2968,8 @@ var updateMixerActive = false; //var cleanupTimeout = null; function updateMixer(e=false){ if (session.manual === true){return;} - if (session.director){return;} + else if (session.director){return;} + else if (session.windowed){return;} clearInterval(updateMixerTimer); if (updateMixerActive){ @@ -3762,9 +3776,9 @@ function updateMixerRun(e=false){ // this is the main auto-mixing code. It's a for (var i = 0; i
'; } - warnUser(string); + warnUser(string, false, false); if (session.hostedFiles){ if (session.hostedFiles.length){ getById("activeShares").innerHTML += "
Files being shared:
"; @@ -10611,24 +10622,25 @@ function publishWebcam(btn = false) { getById("head2").className = 'hidden'; if (session.roomid !== false) { // they are in a room or a faux room + + window.onresize = updateMixer; + window.onorientationchange = function(){setTimeout(async function(){ + if (session.forceAspectRatio){ + if (window.matchMedia("(orientation: portrait)").matches){ + await updateCameraConstraints("aspectRatio", 1.0/session.forceAspectRatio); + } else { + await updateCameraConstraints("aspectRatio", session.forceAspectRatio); + } + } + updateForceRotate(); + updateMixer(); + }, 200);}; + if ((session.roomid === "") && ((!(session.view)) || (session.view === ""))) { // no room, no viewing, viewing disabled if (session.manual===null){ session.manual = true; } - window.onresize = updateMixer; - window.onorientationchange = function(){setTimeout(async function(){ - if (session.forceAspectRatio){ - if (window.matchMedia("(orientation: portrait)").matches){ - await updateCameraConstraints("aspectRatio", 1.0/session.forceAspectRatio); - } else { - await updateCameraConstraints("aspectRatio", session.forceAspectRatio); - } - } - updateForceRotate(); - updateMixer(); - }, 200);}; - if (!(session.cleanOutput)) { var showReshare = getStorage("showReshare"); if (showReshare){ @@ -10644,18 +10656,6 @@ function publishWebcam(btn = false) { } else { log("ROOM ID ENABLED"); log("Update Mixer Event on REsize SET"); - window.onresize = updateMixer; - window.onorientationchange = function(){setTimeout(async function(){ - if (session.forceAspectRatio){ - if (window.matchMedia("(orientation: portrait)").matches){ - await updateCameraConstraints("aspectRatio", 1.0/session.forceAspectRatio); - } else { - await updateCameraConstraints("aspectRatio", session.forceAspectRatio); - } - } - updateForceRotate(); - updateMixer(); - }, 200);}; getById("main").style.overflow = "hidden"; //session.cbr=0; // we're just going to override it @@ -11377,7 +11377,7 @@ function outboundAudioPipeline() { // this function isn't letting me change the session.videoElement.srcObject.getVideoTracks().forEach(function(track) { // this seems to fix a bug with macbooks. newStream.addTrack(track, session.videoElement.srcObject); }); - } else { + } else if (session.streamSrc){ session.streamSrc.getVideoTracks().forEach(function(track) { // this seems to fix a bug with macbooks. newStream.addTrack(track, session.streamSrc); }); @@ -11582,7 +11582,7 @@ function outboundAudioPipeline() { // this function isn't letting me change the webAudio.destination.stream.addTrack(track, session.videoElement.srcObject); } }); - } else { + } else if (session.streamSrc){ session.streamSrc.getVideoTracks().forEach(function(track) { if (webAudio.id != track.id) { webAudio.destination.stream.addTrack(track, session.streamSrc); @@ -11609,9 +11609,11 @@ function outboundAudioPipeline() { // this function isn't letting me change the } var newStream = createMediaStream(); - session.streamSrc.getVideoTracks().forEach(function(track) { // this seems to fix a bug with macbooks. - newStream.addTrack(track, session.streamSrc); - }); + if (session.streamSrc){ + session.streamSrc.getVideoTracks().forEach(function(track) { // this seems to fix a bug with macbooks. + newStream.addTrack(track, session.streamSrc); + }); + } return newStream; } } catch (e) { @@ -12159,16 +12161,16 @@ function joinRoom(roomname) { if (session.defaultPassword===false){ if (session.password === false){ var invite = "https://"+location.host+location.pathname+"?room="+session.roomid+"&password=false"; - warnUser("You can invite others with:\n\n"+invite+""); + warnUser("You can invite others with:\n\n"+invite+"", false, false); } else { generateHash(session.password + session.salt, 4).then(function(hash) { var invite = "https://"+location.host+location.pathname+"?room="+session.roomid+"&hash="+hash; - warnUser("You can invite others with:\n\n"+invite+""); + warnUser("You can invite others with:\n\n"+invite+"", false, false); }); } } else { var invite = "https://"+location.host+location.pathname+"?room="+session.roomid; - warnUser("You can invite others with:\n\n"+invite+""); + warnUser("You can invite others with:\n\n"+invite+"", false, false); } } @@ -14482,17 +14484,16 @@ function gotDevices2(deviceInfos) { option.value = deviceInfo.deviceId || "default"; option.text = deviceInfo.label || `camera ${videoSelect.length + 1}`; try { - if (!knownTrack){ - if (session.canvasSource){ - session.canvasSource.srcObject.getVideoTracks().forEach(function(track) { - if (option.text == track.label) { - option.selected = "true"; - knownTrack = true; - } - }); - } + if (!knownTrack && session.canvasSource){ + session.canvasSource.srcObject.getVideoTracks().forEach(function(track) { + if (option.text == track.label) { + option.selected = "true"; + knownTrack = true; + } + }); + } - if (!knownTrack){ + if (!knownTrack && session.streamSrc){ session.streamSrc.getVideoTracks().forEach(function(track) { if (option.text == track.label) { option.selected = "true"; @@ -15707,23 +15708,26 @@ async function toggleScreenShare(reload = false) { //////////////////////////// } var addedAlready = false; - session.streamSrc.getVideoTracks().forEach(function(track) { - if (beforeScreenShare && (track.id == beforeScreenShare.id)){ - addedAlready=true; - } else { - session.streamSrc.removeTrack(track); - track.stop(); - } - }); - - session.videoElement.srcObject.getVideoTracks().forEach(function(track) { - if (beforeScreenShare && (track.id == beforeScreenShare.id)){ - addedAlready=true; - } else { - session.videoElement.srcObject.removeTrack(track); - track.stop(); - } - }); + if (session.streamSrc){ + session.streamSrc.getVideoTracks().forEach(function(track) { + if (beforeScreenShare && (track.id == beforeScreenShare.id)){ + addedAlready=true; + } else { + session.streamSrc.removeTrack(track); + track.stop(); + } + }); + } + if (session.videoElement.srcObject){ + session.videoElement.srcObject.getVideoTracks().forEach(function(track) { + if (beforeScreenShare && (track.id == beforeScreenShare.id)){ + addedAlready=true; + } else { + session.videoElement.srcObject.removeTrack(track); + track.stop(); + } + }); + } getById("screensharebutton").classList.add("float"); // disable the button after we know the tracks are disabled getById("screensharebutton").classList.remove("float2"); @@ -16059,11 +16063,11 @@ async function grabScreen(quality = 0, audio = true, videoOnEnd = false) { if (!(session.cleanOutput)) { setTimeout(function() { if (iOS || iPad){ - warnUser(miscTranslations["ios-no-screen-share"]); + warnUser(miscTranslations["ios-no-screen-share"], false, false); } else if (session.mobile){ - warnUser(miscTranslations["android-no-screen-share"]); + warnUser(miscTranslations["android-no-screen-share"], false, false); } else { - warnUser(miscTranslations["no-screen-share-supported"]); + warnUser(miscTranslations["no-screen-share-supported"], false, false); } }, 1); } @@ -16234,17 +16238,18 @@ async function grabScreen(quality = 0, audio = true, videoOnEnd = false) { try { stream.getVideoTracks()[0].onended = function(e) { // if screen share stops, warnlog(e); - - session.streamSrc.getVideoTracks().forEach(function(track) { - session.streamSrc.removeTrack(track); - track.stop(); - log("stopping video track 3"); - - if (beforeScreenShare && (beforeScreenShare.id == track.id)){ - beforeScreenShare.stop(); - beforeScreenShare=null; - } - }); + if (session.streamSrc){ + session.streamSrc.getVideoTracks().forEach(function(track) { + session.streamSrc.removeTrack(track); + track.stop(); + log("stopping video track 3"); + + if (beforeScreenShare && (beforeScreenShare.id == track.id)){ + beforeScreenShare.stop(); + beforeScreenShare=null; + } + }); + } if (session.videoElement.srcObject){ session.videoElement.srcObject.getVideoTracks().forEach(function(track) { @@ -16258,12 +16263,14 @@ async function grabScreen(quality = 0, audio = true, videoOnEnd = false) { } if (screenShareAudioTrack){ - session.streamSrc.getAudioTracks().forEach(function(track) { // previous video track; saving it. Must remove the track at some point. - if (screenShareAudioTrack.id == track.id) { // since there are more than one audio track, lets see if we can remove JUST the audio track for the screen share. - session.streamSrc.removeTrack(track); - track.stop(); - } - }); + if (session.streamSrc){ + session.streamSrc.getAudioTracks().forEach(function(track) { // previous video track; saving it. Must remove the track at some point. + if (screenShareAudioTrack.id == track.id) { // since there are more than one audio track, lets see if we can remove JUST the audio track for the screen share. + session.streamSrc.removeTrack(track); + track.stop(); + } + }); + } screenShareAudioTrack=null; senderAudioUpdate(); } @@ -16324,13 +16331,13 @@ async function grabScreen(quality = 0, audio = true, videoOnEnd = false) { if ((err.name == "NotAllowedError") || (err.name == "PermissionDeniedError")) { // User Stopped it. if (macOS){ - warnUser(miscTranslations["screen-permissions-denied"]); + warnUser(miscTranslations["screen-permissions-denied"], false, false); } } else { if (audio == true) { if (err.name == "NotReadableError"){ if (!(session.cleanOutput)){ - warnUser(miscTranslations["change-audio-output-device"]); + warnUser(miscTranslations["change-audio-output-device"], false, false); } return false; } else { @@ -16594,12 +16601,13 @@ async function grabVideo(quality = 0, eleName = 'previewWebcam', selector = "sel wasDisabled=false; }); } - - session.streamSrc.getVideoTracks().forEach(function(track) { - session.streamSrc.removeTrack(track); - track.stop(); - wasDisabled=false; - }); + if (session.streamSrc){ + session.streamSrc.getVideoTracks().forEach(function(track) { + session.streamSrc.removeTrack(track); + track.stop(); + wasDisabled=false; + }); + } if (session.videoElement.srcObject) { session.videoElement.srcObject.getVideoTracks().forEach(function(track) { @@ -17126,9 +17134,9 @@ async function grabVideo(quality = 0, eleName = 'previewWebcam', selector = "sel } if (!(session.cleanOutput)) { if (session.width || session.height || session.frameRate) { - warnUser(" Camera failed to load.\n\nPlease ensure your camera supports the resolution and frameRate that has been manually specified. Perhaps use &quality=0 instead."); + warnUser(" Camera failed to load.\n\nPlease ensure your camera supports the resolution and frameRate that has been manually specified. Perhaps use &quality=0 instead.", false, false); } else { - warnUser(" Camera failed to load.\n\nPlease make sure it is not already in use by another application.\n\nPlease make sure you have accepted the camera permissions."); + warnUser(" Camera failed to load.\n\nPlease make sure it is not already in use by another application.\n\nPlease make sure you have accepted the camera permissions.", false, false); } } } @@ -17942,9 +17950,9 @@ async function publishScreen2(constraints, audioList=[], audio=true, overrideFra if (!navigator.mediaDevices.getDisplayMedia){ setTimeout(function(){ if (iOS || iPad){ - warnUser("Sorry, but your iOS browser does not support screen-sharing.\n\nPlease see this guide for an alternative method to do so."); + warnUser("Sorry, but your iOS browser does not support screen-sharing.\n\nPlease see this guide for an alternative method to do so.", false, false); } else if (session.mobile){ - warnUser("Sorry, your browser does not support screen-sharing.\n\nThe Android native app should support it though."); + warnUser("Sorry, your browser does not support screen-sharing.\n\nThe Android native app should support it though.", false, false); } else { warnUser("Sorry, your browser does not support screen-sharing.\n\nPlease use the desktop versions of Firefox or Chrome instead."); } @@ -18214,7 +18222,7 @@ async function publishScreen2(constraints, audioList=[], audio=true, overrideFra } else { getById("mutespeakerbutton").classList.add("hidden"); - if (session.fullscreen){ + if (session.fullscreen){ session.windowed = false; if (session.mirrored && session.flipped){ v.style.transform = " scaleX(-1) scaleY(-1)"; @@ -18252,6 +18260,21 @@ async function publishScreen2(constraints, audioList=[], audio=true, overrideFra container.style.alignItems = "center"; container.backgroundColor = "#666"; } + + if (!session.windowed){ + window.onresize = updateMixer; + window.onorientationchange = function(){setTimeout(async function(){ + if (session.forceAspectRatio){ + if (window.matchMedia("(orientation: portrait)").matches){ + await updateCameraConstraints("aspectRatio", 1.0/session.forceAspectRatio); + } else { + await updateCameraConstraints("aspectRatio", session.forceAspectRatio); + } + } + updateForceRotate(); + updateMixer(); + }, 200);}; + } v.autoplay = true; v.controls = session.showControls || false; @@ -18338,14 +18361,14 @@ async function publishScreen2(constraints, audioList=[], audio=true, overrideFra notifyOfScreenShare(); if (macOS){ - warnUser(miscTranslations["screen-permissions-denied"]); + warnUser(miscTranslations["screen-permissions-denied"], false, false); } return false; } else { if (audio==true){ if (err.name == "NotReadableError"){ if (!(session.cleanOutput)){ - warnUser(miscTranslations["change-audio-output-device"]); + warnUser(miscTranslations["change-audio-output-device"], false, false); } return false; } else { @@ -19561,7 +19584,7 @@ function setupClosedCaptions() { Recognition.start(); } else if (!session.cleanOutput){ - warnUser(miscTranslations["speech-not-suppoted"]); + warnUser(miscTranslations["speech-not-suppoted"], false, false); } } @@ -22336,6 +22359,16 @@ function createIframePopup() { if (session.meshcast){ extras += "&meshcast"; } + if (session.remote){ + if (session.remote===true){ + extras += "&remote"; + } else { + extras += "&remote="+session.remote; + } + } + if (session.salt){ + extras += "&salt="+session.salt; + } if (session.meshcastBitrate){ extras += "&mcb="+session.meshcastBitrate; } @@ -22625,7 +22658,7 @@ async function requestBasicPermissions(constraint = {video: true, audio: true}, if (!(session.cleanOutput)) { setTimeout(function() { if (window.obsstudio){ - warnUser("Permissions denied.\n\nTo access the camera or microphone from within OBS, please refer to:\ndocs.vdo.ninja/guides/share-webcam-from-inside-obs."); + warnUser("Permissions denied.\n\nTo access the camera or microphone from within OBS, please refer to:\ndocs.vdo.ninja/guides/share-webcam-from-inside-obs.", false, false); } else { warnUser("Permissions denied. Please ensure you have allowed the mic/camera permissions."); } @@ -23272,7 +23305,7 @@ function pauseVideo(videoEle, update=true){ var deviceListElement = gotDevices3(deviceInfo, ele); if (deviceListElement){ - warnUser("Select the audio playback destination for this media:

"); + warnUser("Select the audio playback destination for this media:\n\n"); getById("alertModalMessage").appendChild(deviceListElement); } else { warnUser("No output devices available"); @@ -23551,7 +23584,7 @@ function timeSince(date) { } var messageList = [] -function sendChatMessage(chatMsg = false) { // filtered + visual +function sendChatMessage(chatMsg = false, bc = false) { // filtered + visual var data = {}; if (chatMsg === false) { var msg = document.getElementById('chatInput').value; @@ -23725,7 +23758,7 @@ function sendChatMessage(chatMsg = false) { // filtered + visual document.getElementById('chatInput').value = ""; messageList = messageList.slice(-100); - if (session.broadcastChannel !== false) { + if (!bc && session.broadcastChannel !== false) { log(session.broadcastChannel); session.broadcastChannel.postMessage(data); } @@ -23831,12 +23864,63 @@ function createPopoutChat() { messageList: messageList }); } else if ("msg" in e.data) { - sendChatMessage(e.data.msg); + sendChatMessage(e.data.msg, true); } } return false; } +function replaceURLs(message) { + if(!message) return; + var urlRegex = /(((https?:\/\/)|(www\.))[^\s]+)/g; + return message.replace(urlRegex, function (url) { + url = url.replace(//g, ">").replace(/["']/g, ""); // try to sanitize things, just in case. + + var punc = ""; + while (url[url.length-1] === "."){ + url = url.slice(0,-1); + punc += "."; + } + while (url[url.length-1] === ";"){ + url = url.slice(0,-1); + punc += ";"; + } + while (url[url.length-1] === ","){ + url = url.slice(0,-1); + punc += ","; + } + while (url[url.length-1] === "!"){ + url = url.slice(0,-1); + punc += "!"; + } + while (url[url.length-1] === ":"){ + url = url.slice(0,-1); + punc += ":"; + } + while (url[url.length-1] === "*"){ + url = url.slice(0,-1); + punc += "*"; + } + while (url[url.length-1] === ")"){ + url = url.slice(0,-1); + punc += ")"; + } + while (url[url.length-1] === "?"){ + url = url.slice(0,-1); + punc += "?"; + } + + var hyperlink = url; + if (!hyperlink.match('^https?:\/\/')) { + hyperlink = 'http://' + hyperlink; + } + if (url.length>35){ + url = url.substring(0, 35)+"..."; + } + return '' + url + ''+punc; + }); +} + function getChatMessage(msg, label = false, director = false, overlay = false) { msg = sanitizeChat(msg); // keep it clean. @@ -23918,7 +24002,6 @@ function getChatMessage(msg, label = false, director = false, overlay = false) { getById("chatNotification").value = 1; } getById("chatNotification").classList.add("notification"); - } @@ -23994,22 +24077,23 @@ function updateMessages(){ var time = timeSince(messageList[i].time) || ""; var msg = document.createElement("div"); + var message = replaceURLs(messageList[i].msg); if (messageList[i].type == "sent") { - msg.innerHTML = messageList[i].msg + " - " + time + ""; + msg.innerHTML = message + " - " + time + ""; msg.classList.add("outMessage"); } else if ((messageList[i].type == "recv") || (messageList[i].type == "action")) { var label = ""; if (messageList[i].label) { label = messageList[i].label; } - msg.innerHTML = label + messageList[i].msg + " - " + time + ""; + msg.innerHTML = label + message + " - " + time + ""; msg.classList.add("inMessage"); } else if (messageList[i].type == "alert") { - msg.innerHTML = messageList[i].msg + " - " + time + ""; + msg.innerHTML = message + " - " + time + ""; msg.classList.add("inMessage"); } else { - msg.innerHTML = messageList[i].msg; + msg.innerHTML = message; msg.classList.add("outMessage"); } @@ -25898,12 +25982,12 @@ function effectsDynamicallyUpdate(event, ele){ } else if (session.effects === "6"){ if (!gpgpuSupport){ if (!session.cleanOutput){ - warnUser("Hardware acceleration isn't detected.

This effect will not work",4000); + warnUser("Hardware acceleration isn't detected.

This effect will not work",4000,false); return; } } else if (gpgpuSupport == "Google SwiftShader"){ if (!session.cleanOutput){ - warnUser("Hardware acceleration isn't detected.

Please enable it for this effect to work correctly.

Settings -> Advanced -> System -> Use hardware-accleration"); + warnUser("Hardware acceleration isn't detected.

Please enable it for this effect to work correctly.

Settings -> Advanced -> System -> Use hardware-accleration", false, false); } return; } @@ -25977,7 +26061,7 @@ function loadEffect(effect){ if (gpgpuSupport == "Google SwiftShader"){ if (!session.cleanOutput){ - warnUser("Hardware acceleration isn't detected.

Please enable it for better performance.

Settings -> Advanced -> System -> Use hardware-accleration"); + warnUser("Hardware acceleration isn't detected.

Please enable it for better performance.

Settings -> Advanced -> System -> Use hardware-accleration", false, false); } } } @@ -26104,7 +26188,7 @@ async function loadTFLiteModel(){ if (LaunchTFWorkerCallback){TFLiteWorker();} } function smdInfo(){ - warnUser("For improved performance, use Chrome v87 or newer with SIMD support enabled.
Enable SIMD here: chrome://flags/#enable-webassembly-simd"); + warnUser("For improved performance, use Chrome v87 or newer with SIMD support enabled.
Enable SIMD here: chrome://flags/#enable-webassembly-simd", false, false); } function getGuestTarget(type, id){ diff --git a/main.css b/main.css index 07ee3af..2f3a648 100644 --- a/main.css +++ b/main.css @@ -24,6 +24,7 @@ --video-background-image: url("data:image/svg+xml,%3Csvg viewBox='-42 0 512 512.002' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='m210.351562 246.632812c33.882813 0 63.222657-12.152343 87.195313-36.128906 23.972656-23.972656 36.125-53.304687 36.125-87.191406 0-33.875-12.152344-63.210938-36.128906-87.191406-23.976563-23.96875-53.3125-36.121094-87.191407-36.121094-33.886718 0-63.21875 12.152344-87.191406 36.125s-36.128906 53.308594-36.128906 87.1875c0 33.886719 12.15625 63.222656 36.132812 87.195312 23.976563 23.96875 53.3125 36.125 87.1875 36.125zm0 0'/%3E%3Cpath d='m426.128906 393.703125c-.691406-9.976563-2.089844-20.859375-4.148437-32.351563-2.078125-11.578124-4.753907-22.523437-7.957031-32.527343-3.308594-10.339844-7.808594-20.550781-13.371094-30.335938-5.773438-10.15625-12.554688-19-20.164063-26.277343-7.957031-7.613282-17.699219-13.734376-28.964843-18.199219-11.226563-4.441407-23.667969-6.691407-36.976563-6.691407-5.226563 0-10.28125 2.144532-20.042969 8.5-6.007812 3.917969-13.035156 8.449219-20.878906 13.460938-6.707031 4.273438-15.792969 8.277344-27.015625 11.902344-10.949219 3.542968-22.066406 5.339844-33.039063 5.339844-10.972656 0-22.085937-1.796876-33.046874-5.339844-11.210938-3.621094-20.296876-7.625-26.996094-11.898438-7.769532-4.964844-14.800782-9.496094-20.898438-13.46875-9.75-6.355468-14.808594-8.5-20.035156-8.5-13.3125 0-25.75 2.253906-36.972656 6.699219-11.257813 4.457031-21.003906 10.578125-28.96875 18.199219-7.605469 7.28125-14.390625 16.121094-20.15625 26.273437-5.558594 9.785157-10.058594 19.992188-13.371094 30.339844-3.199219 10.003906-5.875 20.945313-7.953125 32.523437-2.058594 11.476563-3.457031 22.363282-4.148437 32.363282-.679688 9.796875-1.023438 19.964844-1.023438 30.234375 0 26.726562 8.496094 48.363281 25.25 64.320312 16.546875 15.746094 38.441406 23.734375 65.066406 23.734375h246.53125c26.625 0 48.511719-7.984375 65.0625-23.734375 16.757813-15.945312 25.253906-37.585937 25.253906-64.324219-.003906-10.316406-.351562-20.492187-1.035156-30.242187zm0 0'/%3E%3C/svg%3E"); --advanced-mode: inline-block; --background-main-image: unset; + --show-codirectors: inline-block; } * { @@ -570,13 +571,23 @@ hr { } .directorsgrid .vidcon { - display: inline-block !important; + display: inline-block; width: 269.7px !important; background: #7E7E7E; color: #FCFCFC; vertical-align: top; } +.directorBox { + background-color: #606383 !important; + display: var(--show-codirectors) !important; +} + +.directorBlue { + background-color: #5c7785 !important; + display: var(--show-codirectors) !important; +} + .directorsgrid .vidcon>.las { color: black; background: #999999; @@ -726,7 +737,6 @@ hr { top: 1px; background-color: #FFF2; font-size: 1.5em; - display:none; z-index: 2; cursor: help; } @@ -2808,6 +2818,7 @@ input[type=checkbox] { overflow-y:scroll; overflow-wrap: anywhere; max-height: 800px; + line-height: 22px; } #chatBody::-webkit-scrollbar { @@ -2815,12 +2826,23 @@ input[type=checkbox] { background: transparent; /* make scrollbar transparent */ } +div#chatBody a { + color: blue; + text-decoration: underline; + background-color: #c9c9c9; + border: 2px solid black; + padding: 2px 10px; + border-radius: 6px; + cursor: pointer; +} + #chatModule { bottom: 50px; position: fixed; margin: 10px; align-self: center; - width: 400px; + width: 574px; + min-width: 300px; max-width: 100%; z-index:3; } @@ -2829,11 +2851,19 @@ input[type=checkbox] { background-color: #FFFE; max-width: 700px; min-width: 320px; + width: calc(100% - 89px); font-size: 105%; - margin-left: 7px; padding: 3px; border: 3px solid black; } +.chatBarInputButton { + width:60px; + background-color:#EEE; + top: -1px; + position: relative; + margin-right: 10px; +} + .debugStats { font-size: 0.8rem; list-style-type: none; diff --git a/main.js b/main.js index e68e69a..402467e 100644 --- a/main.js +++ b/main.js @@ -25,8 +25,8 @@ async function main(){ // main asyncronous thread; mostly initializes the user s } } - if (urlParams.has('ln')) { - ln_template = urlParams.get('ln') || null; + if (urlParams.has('ln') || urlParams.has('language')) { + ln_template = urlParams.get('ln') || urlParams.get('language') || null; } } catch (e) { errorlog(e); @@ -273,7 +273,14 @@ async function main(){ // main asyncronous thread; mostly initializes the user s session.pushEffectsData=true; } - if (iOS || iPad) { + + if (urlParams.has('notmobile')){ + session.mobile = false; + } else if (urlParams.has('mobile')){ + session.mobile = true; + session.audioEffects = false; // disable audio inbound effects also. + session.audioMeterGuest = false; + } else if (iOS || iPad) { session.mobile = true; session.audioEffects = false; // disable audio inbound effects also. session.audioMeterGuest = false; @@ -316,6 +323,11 @@ async function main(){ // main asyncronous thread; mostly initializes the user s } } + + // flagship + + + if (urlParams.has('broadcast') || urlParams.has('bc')) { log("Broadcast flag set"); session.broadcast = urlParams.get('broadcast') || urlParams.get('bc') || null; @@ -517,6 +529,10 @@ async function main(){ // main asyncronous thread; mostly initializes the user s getById("hangupbutton").style.display = "none"; } + if (urlParams.has('socialstream')){ + session.socialstream = urlParams.get('socialstream') || false; + } + if (urlParams.has('midioffset')){ session.midiOffset = urlParams.get('midioffset') || 0; session.midiOffset = parseInt(session.midiOffset); @@ -632,7 +648,7 @@ async function main(){ // main asyncronous thread; mostly initializes the user s session.hidesololinks=true; } - if (urlParams.has('ssb')) { + if (urlParams.has('ssb') || urlParams.has('screensharebutton')) { session.screensharebutton = true; } @@ -1097,6 +1113,9 @@ async function main(){ // main asyncronous thread; mostly initializes the user s getById("addPasswordBasic").style.display = "none"; } + if (urlParams.has('salt') && urlParams.get('salt')){ + session.salt = urlParams.get('salt'); + } 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. @@ -1331,9 +1350,9 @@ async function main(){ // main asyncronous thread; mostly initializes the user s } - if (urlParams.has("aec") || urlParams.has("ec")) { + if (urlParams.has("echocancellation") || urlParams.has("aec") || urlParams.has("ec")) { - session.echoCancellation = urlParams.get('aec') || urlParams.get('ec'); + session.echoCancellation = urlParams.get("echocancellation") || urlParams.get('aec') || urlParams.get('ec'); if (session.echoCancellation) { session.echoCancellation = session.echoCancellation.toLowerCase(); @@ -1663,6 +1682,12 @@ async function main(){ // main asyncronous thread; mostly initializes the user s session.audioGain = parseInt(session.audioGain) || 0; session.disableWebAudio = false; } + if (urlParams.has('volume') || urlParams.has('vol') ) { // This sets the default volume for all new video playback elements; 0 to 100. + log("setting default volume for playback"); + session.volume = urlParams.get('volume') || urlParams.get('vol') || 100; + session.volume = parseInt(session.volume) || 0; + session.volume = session.volume/100; // 0 to 1.0 + } if (urlParams.has('compressor') || urlParams.has('comp')) { log("audio gain ENABLED"); session.compressor = 1; @@ -1699,6 +1724,10 @@ async function main(){ // main asyncronous thread; mostly initializes the user s getById("obsState").style.setProperty("display", "none", "important"); } + if (urlParams.has('hidecodirectors')){ + document.querySelector(':root').style.setProperty("--show-codirectors", "none", "important"); + } + if (urlParams.has('obscontrols') || urlParams.has('remoteobs') || urlParams.has('obsremote') || urlParams.has('obs') || urlParams.has('controlobs')) { session.obsControls = urlParams.get('obscontrols') || urlParams.get('remoteobs') || urlParams.get('obsremote') || urlParams.get('obs') || urlParams.get('controlobs'); if (session.obsControls) { // whether to show the button or not; that's it. @@ -2399,8 +2428,8 @@ async function main(){ // main asyncronous thread; mostly initializes the user s session.meshcastScreenShareCodec = session.meshcastScreenShareCodec.toLowerCase(); } - if (urlParams.has('mcab') || urlParams.has('mcaudiobitrate') || urlParams.has('meshcastab')){ - session.meshcastAudioBitrate = urlParams.get('mcab') || urlParams.get('mcaudiobitrate') || urlParams.get('meshcastab') || 32; + if (urlParams.has('mcab') || urlParams.has('mcaudiobitrate') || urlParams.has('meshcastab') || urlParams.has('meshcastaudiobitrate ')){ + session.meshcastAudioBitrate = urlParams.get('mcab') || urlParams.get('mcaudiobitrate') || urlParams.get('meshcastab') || urlParams.get('meshcastaudiobitrate ') || 32; session.meshcastAudioBitrate = parseInt(session.meshcastAudioBitrate); } @@ -2929,7 +2958,6 @@ async function main(){ // main asyncronous thread; mostly initializes the user s } else { session.sendframes = session.iframetarget || "*"; } - } if (urlParams.has('tcp')){ // forces the TURN servers to use TCP mode; still need to add &private to force TURN also tho @@ -3516,7 +3544,7 @@ async function main(){ // main asyncronous thread; mostly initializes the user s } else if (session.chatbutton === false) { getById("chatbutton").classList.add("hidden"); } - } + } if (urlParams.has('nofileshare') || urlParams.has('nodownloads') || urlParams.has('nofiles')){ session.hostedFiles = false; @@ -3693,7 +3721,7 @@ async function main(){ // main asyncronous thread; mostly initializes the user s if (location.protocol !== 'https:') { if (!(session.cleanOutput)) { - warnUser("SSL (https) is not enabled. This site will not work without it!

Try accessing the site from here instead."); + warnUser("SSL (https) is not enabled. This site will not work without it!

Try accessing the site from here instead.", false, false); } } @@ -3723,6 +3751,12 @@ async function main(){ // main asyncronous thread; mostly initializes the user s session.limitTotalBitrate = session.totalRoomBitrate_default; // 500, with the max per guest stream out at maxMobileBitrate (350kbps) or 35-kbps if more than X in the room. } + if (urlParams.has('maxmobilebitrate')) { + session.maxMobileBitrate = parseInt(urlParams.has('maxmobilebitrate')) || 0; + } + if (urlParams.has('lowmobilebitrate')) { + session.lowMobileBitrate = parseInt(urlParams.has('lowmobilebitrate')) || 0; + } // Please contact steve on discord.vdo.ninja if you'd like this iFRAME tweaked, expanded, etc -- it's updated based on user request @@ -3871,16 +3905,25 @@ async function main(){ // main asyncronous thread; mostly initializes the user s if ("volume" in e.data) { // might not work with iframes or meshcast currently. + session.volume = parseFloat(e.data.volume) || 0; + if (session.volume > 1.0){ // this is a bit quasi improper. But the API is official 0 to 1.0; not 0 to 100, so this is mainly a catch for those not using the API right. + session.volume = session.volume/100.0; + } + if (!("target" in e.data) || (e.data.target == "*")){ + if (session.videoElement){ + session.videoElement.volume = session.volume; + } + } for (var i in session.rpcs) { try { if (!session.rpcs[i].videoElement){continue;} if ("streamID" in session.rpcs[i]) { if ("target" in e.data) { if ((session.rpcs[i].streamID == e.data.target) || (e.data.target == "*")) { // specify a stream ID or let it apply to all videos - session.rpcs[i].videoElement.volume = parseFloat(e.data.volume); + session.rpcs[i].videoElement.volume = session.volume; } } else { - session.rpcs[i].videoElement.volume = parseFloat(e.data.volume); + session.rpcs[i].videoElement.volume = session.volume; } } } catch (e) { @@ -3909,17 +3952,92 @@ async function main(){ // main asyncronous thread; mostly initializes the user s } } + if (("targetBitrate" in e.data) || ("targetAudioBitrate" in e.data)) { // this sets the fundemental bitrate target, but does not necessarily "lock" . - if ("bitrate" in e.data) { /// set a video bitrate for a video; scene or view link; kbps + var msg = {}; + if ("targetBitrate" in e.data){ + msg.targetBitrate = e.data.targetBitrate; + } + if ("targetAudioBitrate" in e.data){ + msg.targetAudioBitrate = e.data.targetAudioBitrate; + } + if (e.data.requestAs){ + msg.requestAs = e.data.requestAs; + } + if (e.data.remote){ + msg.remote = e.data.remote; + } for (var i in session.rpcs) { try { if ("streamID" in session.rpcs[i]) { if ("target" in e.data) { if ((session.rpcs[i].streamID == e.data.target) || (e.data.target == "*")) { // specify a stream ID or let it apply to all videos - session.requestRateLimit(parseInt(e.data.bitrate), i); + session.sendRequest(msg, i); + } + } else if (e.data.UUID && (e.data.UUID===i)) { + session.sendRequest(msg, i); + } else if (e.data.streamID) { + if (session.rpcs[i].streamID == e.data.streamID) { // specify a stream ID or let it apply to all videos + session.sendRequest(msg, i); } } else { - session.requestRateLimit(parseInt(e.data.bitrate), i); // bitrate = 0 pauses the video + session.sendRequest(msg, i); // bitrate = 0 pauses the video + } + } + } catch (e) { + errorlog(e); + } + } + } + + if ("manualBitrate" in e.data){ + for (var i in session.rpcs) { + try { + if ("streamID" in session.rpcs[i]) { + if ("target" in e.data) { + if ((session.rpcs[i].streamID == e.data.target) || (e.data.target == "*")) { // specify a stream ID or let it apply to all videos + session.rpcs[i].manualBitrate = e.data.manualBitrate; + session.requestRateLimit(false, i); + } + } else if (e.data.UUID && (e.data.UUID===i)) { + session.rpcs[i].manualBitrate = e.data.manualBitrate; + session.requestRateLimit(false, i); + } else if (e.data.streamID) { + if (session.rpcs[i].streamID == e.data.streamID) { // specify a stream ID or let it apply to all videos + session.rpcs[i].manualBitrate = e.data.manualBitrate + session.requestRateLimit(false, i); + } + } else { + session.rpcs[i].manualBitrate = e.data.manualBitrate; + session.requestRateLimit(false, i); + } + } + } catch (e) { + errorlog(e); + } + } + } + + if ("bitrate" in e.data) { /// set a video bitrate for a video; scene or view link; kbps + var lock = true; + if ("lock" in e.data){ // since this is the iframe API, we're going to assume the default is manual over-ride. VDO.Ninja's automixer logic won't override a locked bitrate. + lock = e.data.lock; + } + for (var i in session.rpcs) { + try { + if ("streamID" in session.rpcs[i]) { // we only target publishers with this call + if ("target" in e.data) { + if ((session.rpcs[i].streamID == e.data.target) || (e.data.target == "*")) { // specify a stream ID or let it apply to all videos + session.requestRateLimit(e.data.bitrate, i, false, lock); + } + } else if (e.data.UUID && (e.data.UUID===i)) { + session.requestRateLimit(e.data.bitrate, i, false, lock); + } else if (e.data.streamID) { + if (session.rpcs[i].streamID == e.data.streamID) { // specify a stream ID or let it apply to all videos + session.requestRateLimit(e.data.bitrate, i, false, lock); + } + } else { + session.requestRateLimit(e.data.bitrate, i, false, lock); // bitrate = 0 pauses the video } } } catch (e) { @@ -3929,17 +4047,27 @@ async function main(){ // main asyncronous thread; mostly initializes the user s } if ("audiobitrate" in e.data) { // changes the audio bitrate of a specific or all inbound media tracks. kbps + var lock = true; + if ("lock" in e.data){ // since this is the iframe API, we're going to assume the default is manual over-ride. VDO.Ninja's automixer logic won't override a locked bitrate. + lock = e.data.lock; + } for (var i in session.rpcs) { try { - if ("streamID" in session.rpcs[i]) { + if ("streamID" in session.rpcs[i]) { // we only target publishers with this call if ("target" in e.data) { if ((session.rpcs[i].streamID == e.data.target) || (e.data.target == "*")) { // specify a stream ID or let it apply to all videos - session.requestAudioRateLimit(parseInt(e.data.audiobitrate), i); + session.requestAudioRateLimit(parseInt(e.data.bitrate), i, lock); + } + } else if (e.data.UUID && (e.data.UUID===i)) { + session.requestAudioRateLimit(parseInt(e.data.bitrate), i, lock); + } else if (e.data.streamID) { + if (session.rpcs[i].streamID == e.data.streamID) { // specify a stream ID or let it apply to all videos + session.requestAudioRateLimit(parseInt(e.data.bitrate), i, lock); } } else { - session.requestAudioRateLimit(parseInt(e.data.audiobitrate), i); // bitrate = 0 pauses the video + session.requestAudioRateLimit(parseInt(e.data.bitrate), i, lock); // bitrate = 0 pauses the video } - } + } } catch (e) { errorlog(e); } @@ -4137,7 +4265,7 @@ async function main(){ // main asyncronous thread; mostly initializes the user s } if ("getStreamIDs" in e.data) { // get a list of stream Ids, with a label if it is present. label = false if not there - if (e.data.getStreamIDs == true) { + if (e.data.getStreamIDs) { var streamIDs = {}; for (var i in session.rpcs) { streamIDs[session.rpcs[i].streamID] = session.rpcs[i].label; @@ -4145,7 +4273,27 @@ async function main(){ // main asyncronous thread; mostly initializes the user s parent.postMessage({ "streamIDs": streamIDs }, session.iframetarget); - + } + } + + if ("getStreamInfo" in e.data) { // get a list of stream Ids, with a label if it is present. label = false if not there + try { + var UUIDS = {}; + for (var i in session.rpcs){ + UUIDS[i] = {}; + UUIDS[i].label = session.rpcs[i].label || false; + UUIDS[i].streamID = session.rpcs[i].streamID || false; + if (session.rpcs[i].stats && session.rpcs[i].stats.info){ + UUIDS[i].info = session.rpcs[i].stats.info; + } else { + UUIDS[i].info = {}; + } + } + parent.postMessage({ + "streamInfo": UUIDS + }, session.iframetarget); + } catch(e){ + errorlog(e); } } @@ -4234,7 +4382,11 @@ async function main(){ // main asyncronous thread; mostly initializes the user s } // session.viewheight or session.viewwidth if ((targetWidth || targetHeight) && e.data.UUID){ - session.requestResolution(e.data.UUID, wrw || 4096 , hrh || 2160 ); // this is fine. + var requestAs = false; + if (e.data.requestAs){ + requestAs = e.data.requestAs; + } + session.requestResolution(e.data.UUID, targetWidth || 4096 , targetHeight || 2160 , false, requestAs); // this is fine. } //////////////// diff --git a/popout.html b/popout.html index ebd8295..91f5378 100644 --- a/popout.html +++ b/popout.html @@ -1,7 +1,6 @@ -