Add files via upload

This commit is contained in:
Steve Seguin 2021-08-29 03:55:35 -04:00 committed by GitHub
parent ad4d5585b6
commit a1b2cea991
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 8642 additions and 1856 deletions

View File

@ -92,14 +92,7 @@
</div>
</div>
<div class="card">
<h2>MKV to MP4 (no transcoding)</h2>
<div>
<small>The same as: fmpeg -i INPUTFILE -vcodec copy -acodec copy output.mp4</small>
<input type="file" id="uploader2" accept=".mkv" title="Convert MKV to MP4">
</div>
</div>
<div class="card">
<h2>WebM MP4 files (no transcoding, attempts to force high resolutions)</h2>
<h2>WebM to MP4 files (no transcoding, attempts to force high resolutions)</h2>
<div>
<input type="file" id="uploader3" accept=".webm" title="Convert WebM to MP4">
</div>
@ -110,6 +103,13 @@
<input type="file" id="uploader4" accept=".webm" title="Convert WebM to OPUS">
</div>
</div>
<div class="card">
<h2>MKV to MP4 (no transcoding)</h2>
<div>
<small>The same as: fmpeg -i INPUTFILE -vcodec copy -acodec copy output.mp4</small>
<input type="file" id="uploader2" accept=".mkv" title="Convert MKV to MP4">
</div>
</div>
<div id="processing">
<span id="message"></span>
<video id="player" controls style="display:none"></video>

View File

@ -277,7 +277,7 @@
</head>
<body >
<div id="header" style="-webkit-app-region: drag;color:#6f6f6f;font-size:40px; line-height: 40px; padding: 20px; letter-spacing: 3; font-weight: bold;">Electron Capture</div>
<div id="header" style="-webkit-app-region: drag;color:#6f6f6f;font-size:40px; line-height: 40px; padding: 5px 10px; letter-spacing: 3; font-weight: bold;">Electron Capture</div>
<div class="container" >
<div id='warning4mac' style="display:none;"> ✨ Great News! OBS v26.1.2 <a href="https://github.com/obsproject/obs-browser/issues/209#issuecomment-748683083">now supports</a> VDO.Ninja without needing the Electron Capture app! 🥳</div>
@ -400,15 +400,16 @@ if ((location.hostname.toLowerCase() == "vdo.ninja") || (location.hostname.toLow
var checkVersion = setTimeout(function(){ // pre 1.5.2
compareVersions("0.0.0");
},500);
const ipcRenderer = require('electron').ipcRenderer;
console.log("ELECTRON DETECTED");
ipcRenderer.on('appVersion', function(event, version) {
clearTimeout(checkVersion);
console.log("version: "+version);
compareVersions(version);
})
ipcRenderer.send('getAppVersion');
try{
const ipcRenderer = require('electron').ipcRenderer;
console.log("ELECTRON DETECTED");
ipcRenderer.on('appVersion', function(event, version) {
clearTimeout(checkVersion);
console.log("version: "+version);
compareVersions(version);
})
ipcRenderer.send('getAppVersion');
} catch(e){}
}
}
} catch(e){console.error(e);}
@ -549,6 +550,20 @@ function modURL(){
var url = document.getElementById('changeText').value;
if (url.startsWith("obs.ninja")){
url = "https://"+url;
} else if (url.startsWith("youtube.com")){
url = "https://"+url;
} else if (url.startsWith("twitch.tv")){
url = "https://"+url;
} else if (url.startsWith("vdo.ninja")){
url = "https://"+url;
} else if (url.startsWith("http://")){
// pass
} else if (url.startsWith("https://")){
// pass
} else if (url.startsWith("file:")){
// pass
} else {
url = "https://"+url;
}
console.log(url);
return url;
@ -558,7 +573,7 @@ function gohere(){
localStorage.setItem('lastUrls', JSON.stringify(lastUrls));
var url = modURL();
if ((document.getElementById('changeText').value.includes("obs.ninja")) && (document.getElementById('changeText').value.includes("http")) && (document.getElementById('changeText').value.includes("&sink"))){
alert("Notice: OBS.Ninja has been replaced by VDO.Ninja.\n\nPlease update your links accordingly for audio output to work correctly.");
alert("Notice: OBS.Ninja has been renamed to VDO.Ninja.\n\nPlease update your links accordingly for audio output to work correctly.");
} else if (!(document.getElementById('changeText').value.includes(window.location.hostname)) && (document.getElementById('changeText').value.includes("http")) && (document.getElementById('changeText').value.includes("&sink"))){
alert("Notice: The &sink command is domain specific.\nVisit https://YOURDOMAIN.com/electron instead.");
}

159
examples/chat.html Normal file
View File

@ -0,0 +1,159 @@
<html>
<head>
<meta charset="utf8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>OBSN Chat Overlay</title>
<style>
@font-face {
font-family: 'Cousine';
src: url('fonts/Cousine-Bold.ttf') format('truetype');
}
body {
margin:0;
padding:0 10px;
height:100%;
border: 0;
display: flex;
flex-direction: column-reverse;
position:absolute;
bottom:0;
overflow:hidden;
max-width:100%;
}
div {
margin:0;
background-color: black;
padding: 8px 8px 0px 8px;
color: white;
font-family: Cousine, monospace;
font-size: 3.2em;
line-height: 1.1em;
letter-spacing: 0.0em;
text-transform: uppercase;
text-shadow: 0.05em 0.05em 0px rgba(0,0,0,1);
max-width:100%;
word-wrap: break-word;
overflow-wrap: break-word;
word-break: break-all;
hyphens: auto;
display:inline-block;
}
a {
color:white;
font-size:1.2em;
text-transform: none;
word-wrap: break-word;
overflow-wrap: break-word;
word-wrap: break-word;
word-break: break-all;
hyphens: auto;
}
</style>
<script>
(function (w) {
w.URLSearchParams =
w.URLSearchParams ||
function (searchString) {
var self = this;
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 urlParams = new URLSearchParams(window.location.search);
function loadIframe() {
var iframe = document.createElement("iframe");
var view= "";
if (urlParams.has("view")) {
view = "&view="+(urlParams.get("view") || "");
}
var room="";
if (urlParams.has("room")) {
room = "&room="+urlParams.get("room");
}
var password="";
if (urlParams.has("password")) {
password = "&password="+urlParams.get("password");
}
iframe.allow = "autoplay";
var srcString = "./?novideo&noaudio&label=chatOverlay&scene"+room+view+password;
iframe.src = srcString;
iframe.style.width="0";
iframe.style.height="0";
iframe.style.border="0";
document.body.appendChild(iframe);
//////////// LISTEN FOR EVENTS
var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
var eventer = window[eventMethod];
var messageEvent = eventMethod === "attachEvent" ? "onmessage" : "message";
/// If you have a routing system setup, you could have just one global listener for all iframes instead.
eventer(messageEvent, function (e) {
if (e.source != iframe.contentWindow){return} // reject messages send from other iframes
console.log(e);
if ("gotChat" in e.data){
logData(e.data.gotChat.label,e.data.gotChat.msg);
}
});
}
function printValues(obj) {
var out = "";
for (var key in obj) {
if (typeof obj[key] === "object") {
out += "<br />";
out += printValues(obj[key]);
} else {
if (key.startsWith("_")) {
} else {
out += "<b>" + key + "</b>: " + obj[key] + "<br />";
}
}
}
return out;
}
function logData(type, data) {
var span = document.createElement('span');
var entry = document.createElement('div');
if (type){
type = "<i>"+type.replace(/_/g, ' ')+"</i>";
}
entry.innerHTML = type + data;
span.appendChild(entry);
document.body.prepend(span);
}
</script>
</head>
<body onload="loadIframe();">
</body>
</html>

331
examples/draggable.html Normal file
View File

@ -0,0 +1,331 @@
<html>
<head><title>Dual Input</title>
<style>
body{
padding:0;
margin:0;
}
iframe {
border:0;
margin:0;
padding:0;
display:block;
margin:0px;
min-height: 100px;
min-width: 100px;
max-height: 95%;
max-width: 99%%;
float: left;
position: fixed;
}
#viewlink {
width:400px;
}
input{
padding:5px;
margin:5px;
}
button{
padding:5px;
margin:5px;
position:relative;
}
.menu {
z-index: 10;
float:right;
right: 20px;
color: #fff;
}
.close {
background-color: #d33;
color: #fff;
}
.reload {
background-color: #0a0;
color: #fff;
}
.popup {
z-index: 9;
background-color: #f1f1f1;
border: 1px solid #d3d3d3;
text-align: center;
min-height: 100px;
min-width: 100px;
max-height: 95%;
max-width: 99%;
scale: 0.5;
}
.popup {
position: absolute;
/*resize: both; !*enable this to css resize*! */
overflow: auto;
}
.popup-header {
cursor: move;
background-color: #2196f3;
}
.popup .resizer-right {
width: 5px;
height: 100%;
background: transparent;
position: absolute;
right: 0;
bottom: 0;
cursor: e-resize;
}
.popup .resizer-bottom {
width: 100%;
height: 5px;
background: transparent;
position: absolute;
right: 0;
bottom: 0;
cursor: n-resize;
}
.popup .resizer-both {
width: 5px;
height: 5px;
background: transparent;
z-index: 10;
position: absolute;
right: 0;
bottom: 0;
cursor: nw-resize;
}
/*NOSELECT*/
.popup * {
-webkit-touch-callout: none; /* iOS Safari */
-webkit-user-select: none; /* Safari */
-khtml-user-select: none; /* Konqueror HTML */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none; /* Non-prefixed version, currently
supported by Chrome and Opera */
}
</style>
</head>
<body>
<input placeholder="Enter an OBS.Ninja Room Link" id="viewlink" />
<button onclick="loadIframe();">Load URL</button>You can drag and resize the generated windows; multiple can be created.
<div id="container"></div>
<script>
var currentZIndex = 100;
function initDragElement(popup){
var pos1 = 0,
pos2 = 0,
pos3 = 0,
pos4 = 0;
var elmnt = null;
var header = getHeader(popup);
var iframe = getIFrame(popup);
popup.onmousedown = function() {
this.style.zIndex = "" + ++currentZIndex;
};
if (header) {
header.parentPopup = popup;
header.onmousedown = dragMouseDown;
}
function dragMouseDown(e) {
elmnt = this.parentPopup;
elmnt.style.zIndex = "" + ++currentZIndex;
e = e || window.event;
// get the mouse cursor position at startup:
pos3 = e.clientX;
pos4 = e.clientY;
document.onmouseup = closeDragElement;
// call a function whenever the cursor moves:
document.onmousemove = elementDrag;
}
function elementDrag(e) {
if (!elmnt) {
return;
}
e = e || window.event;
// calculate the new cursor position:
pos1 = pos3 - e.clientX;
pos2 = pos4 - e.clientY;
pos3 = e.clientX;
pos4 = e.clientY;
// set the element's new position:
elmnt.style.top = elmnt.offsetTop - pos2 + "px";
elmnt.style.left = elmnt.offsetLeft - pos1 + "px";
iframe.style.top = elmnt.offsetTop - pos2 + "px";
iframe.style.left = elmnt.offsetLeft - pos1 + "px";
}
function closeDragElement() {
/* stop moving when mouse button is released:*/
document.onmouseup = null;
document.onmousemove = null;
}
function getHeader(element) {
var headerItems = element.getElementsByClassName("popup-header");
if (headerItems.length === 1) {
return headerItems[0];
}
return null;
}
function getIFrame(element) {
var headerItems = element.getElementsByTagName("iframe");
if (headerItems.length === 1) {
return headerItems[0];
}
return null;
}
}
function initResizeElement(p) {
var iframe = getIFrame(p);
var element = null;
var startX, startY, startWidth, startHeight;
var right = document.createElement("div");
right.className = "resizer-right";
p.appendChild(right);
right.addEventListener("mousedown", initDrag, false);
right.parentPopup = p;
var bottom = document.createElement("div");
bottom.className = "resizer-bottom";
p.appendChild(bottom);
bottom.addEventListener("mousedown", initDrag, false);
bottom.parentPopup = p;
var both = document.createElement("div");
both.className = "resizer-both";
p.appendChild(both);
both.addEventListener("mousedown", initDrag, false);
both.parentPopup = p;
function initDrag(e) {
element = this.parentPopup;
startX = e.clientX;
startY = e.clientY;
startWidth = parseInt(
document.defaultView.getComputedStyle(element).width,
10
);
startHeight = parseInt(
document.defaultView.getComputedStyle(element).height,
10
);
document.documentElement.addEventListener("mousemove", doDrag, false);
document.documentElement.addEventListener("mouseup", stopDrag, false);
document.documentElement.addEventListener("click", stopDrag, false)
}
function doDrag(e) {
if (e.buttons==0){
stopDrag(e);
return false;
}
element.style.width = startWidth + e.clientX - startX + "px";
element.style.height = startHeight + e.clientY - startY + "px";
iframe.style.width = startWidth + e.clientX - startX + "px";
iframe.style.height = startHeight + e.clientY - startY + "px";
}
function stopDrag(e) {
document.documentElement.removeEventListener("mousemove", doDrag, false);
document.documentElement.removeEventListener("mouseup", stopDrag, false);
}
function getIFrame(element) {
var headerItems = element.getElementsByTagName("iframe");
if (headerItems.length === 1) {
return headerItems[0];
}
return null;
}
}
function loadIframe(){
var iframeContainer = document.createElement("div");
iframeContainer.className="popup";
iframeContainer.style.zIndex = "" + ++currentZIndex;
iframeContainer.style.width="325px";
iframeContainer.style.height="420px";
var button = document.createElement("button");
button.innerHTML = "Move";
button.className = "popup-header menu";
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Close";
button.className = "menu close";
button.onclick = function(){iframe.contentWindow.postMessage({"close":true}, '*');iframe.parentNode.parentNode.removeChild(iframeContainer);}
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Reload";
button.className = "menu reload";
button.onclick = function(){iframe.contentWindow.postMessage({"reload":true}, '*');}
iframeContainer.appendChild(button);
var iframe = document.createElement("iframe");
iframe.allow="autoplay";
iframe.src = document.getElementById("viewlink").value || "https://obs.ninja";
iframe.style.width="325px";
iframe.style.height="420px";
iframeContainer.appendChild(iframe);
document.getElementById("container").appendChild(iframeContainer);
initDragElement(iframeContainer);
initResizeElement(iframeContainer);
}
</script>
</body>
</html>

View File

@ -1,328 +1,73 @@
<html>
<head><title>Dual Input</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta content="text/html;charset=utf-8" http-equiv="Content-Type" />
<style>
body{
padding:0;
margin:0;
background-color:#003;
width:100%;
height:100%;
}
iframe {
width:100%;
height:100%;
border:0;
margin:0;
padding:0;
position:absolute;
display:block;
margin:0px;
min-height: 100px;
min-width: 100px;
max-height: 95%;
max-width: 99%%;
float: left;
position: fixed;
}
#viewlink {
width:400px;
}
input{
padding:5px;
margin:5px;
}
button{
padding:5px;
margin:5px;
position:relative;
}
.menu {
z-index: 10;
float:right;
right: 20px;
color: #fff;
}
.close {
background-color: #d33;
color: #fff;
}
.reload {
background-color: #0a0;
color: #fff;
}
.popup {
z-index: 9;
background-color: #f1f1f1;
border: 1px solid #d3d3d3;
text-align: center;
min-height: 100px;
min-width: 100px;
max-height: 95%;
max-width: 99%;
scale: 0.5;
}
.popup {
position: absolute;
/*resize: both; !*enable this to css resize*! */
overflow: auto;
}
.popup-header {
cursor: move;
background-color: #2196f3;
}
.popup .resizer-right {
width: 5px;
height: 100%;
background: transparent;
position: absolute;
right: 0;
bottom: 0;
cursor: e-resize;
}
.popup .resizer-bottom {
width: 100%;
height: 5px;
background: transparent;
position: absolute;
right: 0;
bottom: 0;
cursor: n-resize;
}
.popup .resizer-both {
width: 5px;
height: 5px;
background: transparent;
z-index: 10;
position: absolute;
right: 0;
bottom: 0;
cursor: nw-resize;
}
/*NOSELECT*/
.popup * {
-webkit-touch-callout: none; /* iOS Safari */
-webkit-user-select: none; /* Safari */
-khtml-user-select: none; /* Konqueror HTML */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none; /* Non-prefixed version, currently
supported by Chrome and Opera */
padding:10px;
width:80%;
font-size:1.2em;
z-index: 1000;
}
</style>
</head>
<body>
<input placeholder="Enter an OBS.Ninja Room Link" id="viewlink" />
<button onclick="loadIframe();">Load URL</button>You can drag and resize the generated windows; multiple can be created.
<div id="container"></div>
<div id="container1" style="width:100%;height:100%;display:none;"></div>
<div id="container2" style="width: calc(25vh*1.777);height: calc(25vh); display:none; float:left; position: fixed; top: 2%; left: 0%;"></div>
<input placeholder="Enter a Room name" id="viewlink" type="text" onchange="loadIframes()"/>
<script>
var currentZIndex = 100;
function initDragElement(popup){
var pos1 = 0,
pos2 = 0,
pos3 = 0,
pos4 = 0;
function loadIframes(url=false){
var elmnt = null;
var header = getHeader(popup);
var iframe = getIFrame(popup);
popup.onmousedown = function() {
this.style.zIndex = "" + ++currentZIndex;
};
if (header) {
header.parentPopup = popup;
header.onmousedown = dragMouseDown;
}
function dragMouseDown(e) {
elmnt = this.parentPopup;
elmnt.style.zIndex = "" + ++currentZIndex;
e = e || window.event;
// get the mouse cursor position at startup:
pos3 = e.clientX;
pos4 = e.clientY;
document.onmouseup = closeDragElement;
// call a function whenever the cursor moves:
document.onmousemove = elementDrag;
}
function elementDrag(e) {
if (!elmnt) {
return;
}
e = e || window.event;
// calculate the new cursor position:
pos1 = pos3 - e.clientX;
pos2 = pos4 - e.clientY;
pos3 = e.clientX;
pos4 = e.clientY;
// set the element's new position:
elmnt.style.top = elmnt.offsetTop - pos2 + "px";
elmnt.style.left = elmnt.offsetLeft - pos1 + "px";
var roomname = document.getElementById("viewlink").value;
iframe.style.top = elmnt.offsetTop - pos2 + "px";
iframe.style.left = elmnt.offsetLeft - pos1 + "px";
}
function closeDragElement() {
/* stop moving when mouse button is released:*/
document.onmouseup = null;
document.onmousemove = null;
}
function getHeader(element) {
var headerItems = element.getElementsByClassName("popup-header");
if (headerItems.length === 1) {
return headerItems[0];
}
return null;
}
function getIFrame(element) {
var headerItems = element.getElementsByTagName("iframe");
if (headerItems.length === 1) {
return headerItems[0];
}
return null;
}
}
function initResizeElement(p) {
var iframe = getIFrame(p);
var element = null;
var startX, startY, startWidth, startHeight;
var right = document.createElement("div");
right.className = "resizer-right";
p.appendChild(right);
right.addEventListener("mousedown", initDrag, false);
right.parentPopup = p;
var bottom = document.createElement("div");
bottom.className = "resizer-bottom";
p.appendChild(bottom);
bottom.addEventListener("mousedown", initDrag, false);
bottom.parentPopup = p;
var both = document.createElement("div");
both.className = "resizer-both";
p.appendChild(both);
both.addEventListener("mousedown", initDrag, false);
both.parentPopup = p;
function initDrag(e) {
element = this.parentPopup;
startX = e.clientX;
startY = e.clientY;
startWidth = parseInt(
document.defaultView.getComputedStyle(element).width,
10
);
startHeight = parseInt(
document.defaultView.getComputedStyle(element).height,
10
);
document.documentElement.addEventListener("mousemove", doDrag, false);
document.documentElement.addEventListener("mouseup", stopDrag, false);
document.documentElement.addEventListener("click", stopDrag, false)
}
function doDrag(e) {
if (e.buttons==0){
stopDrag(e);
return false;
}
element.style.width = startWidth + e.clientX - startX + "px";
element.style.height = startHeight + e.clientY - startY + "px";
document.getElementById("viewlink").parentNode.removeChild(document.getElementById("viewlink"));
document.getElementById("container1").style.display="inline-block";
document.getElementById("container2").style.display="inline-block";
iframe.style.width = startWidth + e.clientX - startX + "px";
iframe.style.height = startHeight + e.clientY - startY + "px";
}
function stopDrag(e) {
document.documentElement.removeEventListener("mousemove", doDrag, false);
document.documentElement.removeEventListener("mouseup", stopDrag, false);
}
function getIFrame(element) {
var headerItems = element.getElementsByTagName("iframe");
if (headerItems.length === 1) {
return headerItems[0];
}
return null;
}
}
function loadIframe(){
var iframeContainer = document.createElement("div");
iframeContainer.className="popup";
iframeContainer.style.zIndex = "" + ++currentZIndex;
iframeContainer.style.width="325px";
iframeContainer.style.height="420px";
var path = window.location.host+window.location.pathname.split("/").slice(0,-1).join("/");
var button = document.createElement("button");
button.innerHTML = "Move";
button.className = "popup-header menu";
iframeContainer.appendChild(button);
var room1 = "https://"+path+"/?room="+roomname+"&push="+roomname+"_front&webcam&autostart&vd=front&ad=1&exclude="+roomname+"_rear";
var room2 = "https://"+path+"/?room="+roomname+"&push="+roomname+"_rear&webcam&autostart&vd=back&ad=0&view&cleanoutput&nosettings&transparent";
var button = document.createElement("button");
button.innerHTML = "Close";
button.className = "menu close";
button.onclick = function(){iframe.contentWindow.postMessage({"close":true}, '*');iframe.parentNode.parentNode.removeChild(iframeContainer);}
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Reload";
button.className = "menu reload";
button.onclick = function(){iframe.contentWindow.postMessage({"reload":true}, '*');}
iframeContainer.appendChild(button);
var iframe = document.createElement("iframe");
iframe.allow="autoplay";
iframe.src = document.getElementById("viewlink").value || "https://obs.ninja";
iframe.style.width="325px";
iframe.style.height="420px";
iframe.allow = "autoplay;camera;microphone;fullscreen;picture-in-picture;";
iframe.src = room1;
var iframeContainer = document.createElement("div");
iframeContainer.appendChild(iframe);
document.getElementById("container1").appendChild(iframeContainer);
document.getElementById("container").appendChild(iframeContainer);
initDragElement(iframeContainer);
initResizeElement(iframeContainer);
setTimeout(function(){
var iframe = document.createElement("iframe");
iframe.allow = "autoplay;camera;microphone;fullscreen;picture-in-picture;";
iframe.src = room2;
var iframeContainer = document.createElement("div");
iframeContainer.appendChild(iframe);
document.getElementById("container2").appendChild(iframeContainer);
},3000);
}

3086
examples/main.css Normal file

File diff suppressed because it is too large Load Diff

555
examples/midi.html Normal file
View File

@ -0,0 +1,555 @@
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/webmidi"></script>
<link rel="stylesheet" href="./main.css" />
<style>
.container {
max-width: 80%;
width: fit-content;
margin: 0 auto;
}
h1 {
margin-top: 1em;
margin-bottom: 1em;
padding: 10px;
}
.card {
margin: 10px;
box-shadow: 0 4px 8px 0 rgb(0 0 0 / 10%);
background-color: #ddd;
color: black;
margin-bottom: 1.5em;
}
.card>div {
padding: 10px;
}
.card h2 {
font-size: 1.5em;
padding: 10px;
background-color: #457b9d;
color: white;
border-bottom: 2px solid #3b6a87;
}
small {
font-style: italic;
display: block;
margin-left: 1em;
}
span.warning {
color: rgb(212, 191, 0);
}
input {
padding: 10px;
}
video {
max-width: 640px;
max-height: 360px;
padding: 20px;
}
audio {
max-width: 640px;
max-height: 360px;
padding: 20px;
}
div#processing {
display: none;
justify-content: center;
place-items: center;
position: absolute;
inset: 0;
font-size: 1.5em;
font-weight: bold;
background: #141926;
flex-direction: column;
}
button {
margin:5px;
border:solid black 2px;
}
body {
color:white;
}
a {
color: #225273!important;
}
</style>
<title>VDO.Ninja MIDI Controller</title>
</head>
<body>
<div id="header">
<a id="logoname" href="./" style="text-decoration: none; color: white; margin: 2px">
<span data-translate="logo-header">
<font id="qos">V</font>DO.Ninja
</span>
</a>
</div>
<div class="container">
<div id="info">
<h1>VDO.Ninja MIDI test app</h1>
<div class="card">
<h2>About</h2>
<div>
You can check the console debug logs for added details.
<br /><br />You can download a virtual MIDI I/O controller for windwos here:<br />
http://www.tobias-erichsen.de/software/loopmidi.html
<br /><br />This code uses the WebMIDI.js library, referenced here:<br />
https://github.com/djipco/webmidi
<br /><br />
Below you can test the <a href="https://docs.vdo.ninja/general-settings/midi#midi-pass-through-mode">MIDI hotkey commands</a> for VDO.Ninja below:<br />
</div>
</div>
<div class="card">
<h2>Select the MIDI Output device:</h2>
<div>
<label for="outputdevice">MIDI Output device:</label>
<select name="outputdevice" id="outputdevice">
</select>
</div>
</div>
<div class="card">
<h2>&midi=1</h2>
<div id="container1">
</div>
</div>
<div class="card">
<h2>&midi=3</h2>
<div id="container2">
</div>
</div>
<div class="card">
<h2>&midi=4 ; director</h2>
<div id="container3">
</div>
</div>
<div class="card">
<h2>&midi=4 ; guest 1</h2>
<div id="container4">
</div>
</div>
<div class="card">
<h2>&midi=4 ; guest 2</h2>
<div id="container5">
</div>
</div>
<div class="card">
<h2>Sample Remote Director Control links</h2>
<div id="container6">
</div>
</div>
</div>
</div>
<div id='commands'>
</div>
<script>
// Enable WebMidi.js
WebMidi.enable(function (err) {
if (err) {
console.log("WebMidi could not be enabled.", err);
}
// Viewing available inputs and outputs
console.log(WebMidi.inputs);
console.log(WebMidi.outputs);
var output = WebMidi.outputs[0];
var midiout = 0;
var outputdevice = document.getElementById("outputdevice");
for (var i=0;i<WebMidi.outputs.length;i++){
var opt = document.createElement('option');
opt.value = WebMidi.outputs[i].id;
opt.innerHTML = WebMidi.outputs[i].name + " (id:"+(1+i)+")";
if (i==0){
midiout = opt.value;
opt.selected = true;
}
outputdevice.appendChild(opt);
}
var path = window.location.host+window.location.pathname.split("/").slice(0,-1).join("/");
outputdevice.onchange = function(e){
midiout = outputdevice.value;
output = WebMidi.getOutputById(midiout);
console.log("MIDI DEVICE CHANGED: "+midiout);
var container = document.getElementById("container6");
container.innerHTML = "<br />https://"+path+"/?midiremote=4&director=ROOMNAMEHERE";
container.innerHTML += "<br /><br />";
container.innerHTML += "https://"+path+"/?room=ROOMNAMEHERE&midiout="+(outputdevice.selectedIndex+1)+"&vd=0&ad=0&push&autostart&label=MIDI_CONTROLLER";
}
var container = document.getElementById("container6");
container.innerHTML = "<br />https://"+path+"/?midiremote=4&director=ROOMNAMEHERE";
container.innerHTML += "<br /><br />";
container.innerHTML += "https://"+path+"/?room=ROOMNAMEHERE&midiout="+(outputdevice.selectedIndex+1)+"&vd=0&ad=0&push&autostart&label=MIDI_CONTROLLER";
// Reacting when a new device becomes available
WebMidi.addListener("connected", function(e) {
console.log(e);
});
// Reacting when a device becomes unavailable
WebMidi.addListener("disconnected", function(e) {
console.log(e);
});
// Display the current time
console.log(WebMidi.time);
// Retrieve an input by name, id or index
// var input = WebMidi.getInputByName("StreamDeck2Daw");
// input = WebMidi.getInputById("1809568182");
var input = WebMidi.inputs[1];
// Listen for a 'note on' message on all channels
input.addListener('noteon', "all",
function (e) {
console.log("Received 'noteon' message (" + e.note.name + e.note.octave + ").");
console.log(e);
}
);
// Listen to pitch bend message on channel 3
input.addListener('pitchbend', 3,
function (e) {
console.log("Received 'pitchbend' message.", e);
}
);
// Listen to control change message on all channels
input.addListener('controlchange', "all",
function (e) {
console.log("Received 'controlchange' message.", e);
}
);
// Listen to NRPN message on all channels
input.addListener('nrpn', "all",
function (e) {
if(e.controller.type === 'entry') {
console.log("Received 'nrpn' 'entry' message.", e);
}
if(e.controller.type === 'decrement') {
console.log("Received 'nrpn' 'decrement' message.", e);
}
if(e.controller.type === 'increment') {
console.log("Received 'nrpn' 'increment' message.", e);
}
console.log("message value: " + e.controller.value + ".", e);
}
);
var container = document.getElementById("container1");
///
var button = document.createElement("button");
button.innerHTML = "Note G3; Chat";
button.onclick = function(){output.playNote("G3");}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Note A3; Mute";
button.onclick = function(){output.playNote("A3");}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Note B3; Mute Video";
button.onclick = function(){output.playNote("B3");}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Note C4; ScreenShare";
button.onclick = function(){output.playNote("C4");}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Note D4; Hangup";
button.onclick = function(){output.playNote("D4");}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Note E4; Hands";
button.onclick = function(){output.playNote("E4");}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Note F4; Record";
button.onclick = function(){output.playNote("F4");}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Note G4; Turn on Dir's Audio";
button.onclick = function(){output.playNote("G4");}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Note A4; Stop Dir's Audio";
button.onclick = function(){output.playNote("A4");}; // "speaker" also works in the same way.
container.appendChild(button);
///
var container = document.getElementById("container2");
///
var button = document.createElement("button");
button.innerHTML = "Note C1; velocity 0";
button.onclick = function(){output.playNote("C1", 1, {velocity: 0});}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Note C1; velocity 1";
button.onclick = function(){output.playNote("C1", 1, {velocity: 1});}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Note C1; velocity 2";
button.onclick = function(){output.playNote("C1", 1, {velocity: 2});}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Note C1; velocity 3";
button.onclick = function(){output.playNote("C1", 1, {velocity: 3});}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Note C1; velocity 4";
button.onclick = function(){output.playNote("C1", 1, {velocity: 4});}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Note C1; velocity 5";
button.onclick = function(){output.playNote("C1", 1, {velocity: 5});}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Note C1; velocity 6";
button.onclick = function(){output.playNote("C1", 1, {velocity: 6});}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Note C1; velocity 7";
button.onclick = function(){output.playNote("C1", 1, {velocity: 7});}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Note C1; velocity 8";
button.onclick = function(){output.playNote("C1", 1, {velocity: 8});}; // "speaker" also works in the same way.
container.appendChild(button);
///
var container = document.getElementById("container3");
///
var button = document.createElement("button");
button.innerHTML = "channel 110; value 0";
button.onclick = function(){output.sendControlChange(110, 0, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "channel 110; value 1";
button.onclick = function(){output.sendControlChange(110, 1, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "channel 110; value 2";
button.onclick = function(){output.sendControlChange(110, 2, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "channel 110; value 3";
button.onclick = function(){output.sendControlChange(110, 3, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "channel 110; value 4";
button.onclick = function(){output.sendControlChange(110, 4, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "channel 110; value 5";
button.onclick = function(){output.sendControlChange(110, 5, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "channel 110; value 6";
button.onclick = function(){output.sendControlChange(110, 6, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "channel 110; value 7";
button.onclick = function(){output.sendControlChange(110, 7, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "channel 110; value 8";
button.onclick = function(){output.sendControlChange(110, 8, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
///
var container = document.getElementById("container4");
///
var button = document.createElement("button");
button.innerHTML = "channel 111; value 0";
button.onclick = function(){output.sendControlChange(111, 0, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "channel 111; value 1";
button.onclick = function(){output.sendControlChange(111, 1, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "channel 111; value 2";
button.onclick = function(){output.sendControlChange(111, 2, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "channel 111; value 3";
button.onclick = function(){output.sendControlChange(111, 3, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "channel 111; value 4";
button.onclick = function(){output.sendControlChange(111, 4, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "channel 111; value 5";
button.onclick = function(){output.sendControlChange(111, 5, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
///
var container = document.getElementById("container5");
///
var button = document.createElement("button");
button.innerHTML = "channel 112; transfer popup";
button.onclick = function(){output.sendControlChange(112, 0, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "channel 112; scene 1";
button.onclick = function(){output.sendControlChange(112, 1, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "channel 112; mute in scene";
button.onclick = function(){output.sendControlChange(112, 2, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "channel 112; mute everywhere";
button.onclick = function(){output.sendControlChange(112, 3, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "channel 112; hang up";
button.onclick = function(){output.sendControlChange(112, 4, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "channel 112; solo chat";
button.onclick = function(){output.sendControlChange(112, 5, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "remote speaker";
button.onclick = function(){output.sendControlChange(112, 6, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "remote display";
button.onclick = function(){output.sendControlChange(112, 7, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "scene 2";
button.onclick = function(){output.sendControlChange(112, 12, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "scene 3";
button.onclick = function(){output.sendControlChange(112, 13, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "scene 4";
button.onclick = function(){output.sendControlChange(112, 14, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "scene 5";
button.onclick = function(){output.sendControlChange(112, 15, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "scene 6";
button.onclick = function(){output.sendControlChange(112, 16, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = " scene 7";
button.onclick = function(){output.sendControlChange(112, 17, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "scene 8";
button.onclick = function(){output.sendControlChange(112, 18, 1);}; // "speaker" also works in the same way.
container.appendChild(button);
});
</script>
</body>
</html>

97
examples/minidirector.css Normal file
View File

@ -0,0 +1,97 @@
body{
zoom: 75%;
}
.hidden{
display:unset!important;
visibility: visible;
width:unset;
height:unset;
opacity: 1;
}
button[data-action-type='solo-chat'] {
display:none! important;
}
button[data-action-type='recorder-local'] {
display:none! important;
}
span[data-action-type='ordering'] {
display:none! important;
}
button[data-action-type='open-file-share'] {
display:none! important;
}
button[data-action-type='add-channel']{
display:none! important;
}
button[data-action-type='toggle-remote-speaker']{
display:none! important;
}
button[data-action-type='toggle-remote-display']{
display:none! important;
}
button[data-action-type='hide-guest']{
display:none! important;
}
button[data-action-type='create-timer']{
display:none! important;
}
button[data-action-type='change-url']{
display:none! important;
}
button[data-action-type='change-params']{
display:none! important;
}
span[data-action-type='change-quality']{
display:none! important;
}
span[data-action-type='sceneCluster2']{
display:none! important;
}
span[data-action-type='sceneCluster1']{
display:none! important;
}
.orderspan{
display:none! important;
}
#roomHeader{
display:none! important;
}
.directorContainer {
display:none!important;
}
.hideDropMenu{
display:none!important;
}
#header{
display:none!important;
}
body {
background-color: #000;
}
button[class="pull-right"]{
display:none! important;
}
div#guestFeeds {
padding: 1px!important;
margin: 1px!important;
}
div[class="vidcon directorMargins"] {
padding: 1px!important;
margin: 1px!important;
width: 260px;
}
button[data-action-type]{
margin: 1px!important;
}

486
examples/obs_client.html Normal file
View File

@ -0,0 +1,486 @@
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="./thirdparty/obs-websocket.min.js"></script>
<link rel="stylesheet" href="./main.css" />
<style>
.container {
max-width: 80%;
width: fit-content;
margin: 0 auto;
}
h1 {
margin-top: 1em;
margin-bottom: 1em;
padding: 10px;
}
.card {
margin: 10px;
box-shadow: 0 4px 8px 0 rgb(0 0 0 / 10%);
background-color: #ddd;
color: black;
margin-bottom: 1.5em;
}
.card>div {
padding: 10px;
}
.card h2 {
font-size: 1.5em;
padding: 10px;
background-color: #457b9d;
color: white;
border-bottom: 2px solid #3b6a87;
}
small {
font-style: italic;
display: block;
margin-left: 1em;
}
span.warning {
color: rgb(212, 191, 0);
}
input {
padding: 10px;
display: inline-block;
flex-flow: unset;
margin: 10px;
}
video {
max-width: 640px;
max-height: 360px;
padding: 20px;
}
audio {
max-width: 640px;
max-height: 360px;
padding: 20px;
}
div#processing {
display: none;
justify-content: center;
place-items: center;
position: absolute;
inset: 0;
font-size: 1.5em;
font-weight: bold;
background: #141926;
flex-direction: column;
}
button {
margin:5px;
border:solid black 2px;
}
body {
color:white;
display: inline-block;
flex-flow: unset;
}
a {
color: #225273!important;
}
.hidden{
display:none !important;
}
.stat {
background-color: black;
margin: 7px;
padding: 5px;
}
.stat:nth-child(2n) {
background-color: #333;
}
.stat:empty {
display:none;
}
</style>
<title>OBS Controller Demo using VDO.NInja</title>
</head>
<div class="container">
<h1>OBS remote (client)</h1>
<div id="info">
<div class="card">
<h2>Scenes</h2>
<div id="scene_list">
</div>
</div>
<div class="card hidden">
<h2>General</h2>
<div>
<button onclick="basicCommand(this);" data-command="GetVersion">GetVersion</button>
<button onclick="basicCommand(this);" data-command="GetStats">GetStats</button>
<button onclick="basicCommand(this);" data-command="GetVideoInfo">GetVideoInfo</button>
</div>
</div>
<div class="card">
<h2>Output</h2>
<div id="outputs">
<button onclick="basicCommand(this);" data-command="ListOutputs">ListOutputs</button>
</div>
</div>
<div class="card">
<h2>Active Sources</h2>
<div id="active_source_list">
</div>
</div>
<div class="card">
<h2>All Sources</h2>
<div id="source_list">
</div>
</div>
<div class="card hidden">
<h2>Source debug</h2>
<div>
<button onclick="basicCommand(this);" data-command="GetMediaSourcesList">GetMediaSourcesList</button>
<button onclick="basicCommand(this);" data-command="GetSourcesList">GetSourcesList</button>
<button onclick="basicCommand(this);" data-command="GetSourceActive">GetSourceActive</button>
<button onclick="basicCommand(this);" data-command="GetAudioActive">GetAudioActive</button>
</div>
</div>
<div id="audio" class="card hidden">
<h2>Audio</h2>
<div>
<button class="hidden" onclick="basicCommand(this, {source:selectedSource});" data-command="GetVolume">GetVolume</button>
<input type='range' min="0" max="100" value="100" onchange="basicCommand(this, {source:selectedSource, volume:parseInt(this.value)/100});" data-command="SetVolume" />
<button class="hidden" onclick="basicCommand(this, {source:selectedSource});" data-command="ToggleMute">ToggleMute</button>
</div>
</div>
<div class="card" id='commands'>
<h2>Custom Commands</h2>
<input id="newCommand" placeholder="enter a custom command" type='text' /><button id="goCreate" onclick="createCommand()">Create</button>
<br />
A list of possible commands <a href="https://github.com/Palakis/obs-websocket/blob/4.x-current/docs/generated/protocol.md#requests">available here:</a><br />
</div>
</div>
<div style="width:100%;display:block;margin:0;padding:0;">
<div id='OBSstats' style="width:49%;display:inline-block;vertical-align: top;">
<div id="stat-current-profile" class='stat'></div>
<div id="stat-current-scene" class='stat'></div>
<div id="stat-streaming" class='stat'></div>
<div id="stat-memory-usage" class='stat'></div>
<div id="stat-recording" class='stat'></div>
<div id="stat-recording-paused" class='stat'></div>
<div id="stat-average-frame-time" class='stat'></div>
<div id="stat-cpu-usage" class='stat'></div>
<div id="stat-fps" class='stat'></div>
<div id="stat-free-disk-space" class='stat'></div>
</div>
<div id='OBSsettings' style="width:49%;display:inline-block;vertical-align: top;">
<div id="setting-baseHeight" class='stat'></div>
<div id="setting-baseWidth" class='stat'></div>
<div id="setting-outputHeight" class='stat'></div>
<div id="setting-outputWidth" class='stat'></div>
<div id="setting-scaleType" class='stat'></div>
<div id="setting-fps" class='stat'></div>
</div>
</div>
</div>
<script>
function basicCommand(ele, data={}){
sendToOBS(ele.dataset.command, data);
}
function createCommand(){
var command = document.getElementById("newCommand").value;
document.getElementById("newCommand").value = "";
var button = document.createElement("button");
button.innerText = command;
button.dataset.command = command;
button.onclick = function(){basicCommand(this);};
document.getElementById("commands").appendChild(button);
}
(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);
if (urlParams.get("password")){
var password = urlParams.get("password");
localStorage.setItem('password',password)
} else if (localStorage.getItem('password')){
password = localStorage.getItem('password');
}
if (urlParams.get("room")){
var roomname = urlParams.get("room")
localStorage.setItem('roomname',roomname)
} else if (localStorage.getItem('roomname')){
roomname = localStorage.getItem('roomname');
}
const obs = new OBSWebSocket();
var scenesData = {};
var selectedSource = null;
scenesData.scenes = [];
function sendToOBS(action, data){
var msg = {};
msg.sendToOBS = {};
msg.sendToOBS.action = action;
msg.sendToOBS.data = data;
iframe.contentWindow.postMessage({"sendData":msg}, '*');
}
var iframe = document.createElement("iframe");
iframe.src = "./?room="+roomname+"&password="+password+"&push&novideo=mainOBSOutput&noaudio=mainOBSOutput&autostart&vd=0&ad=0&transparent&cleanoutput&label=CLIENT_"+Math.floor(Math.random() * 1000000);
// iframe.style.opacity = 0;
iframe.style.width = "160px";
iframe.style.height = "90px";
iframe.style.position = "fixed";
iframe.style.top = "10px";
iframe.style.right = "170px";
document.body.appendChild(iframe)
var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
var eventer = window[eventMethod];
var messageEvent = eventMethod === "attachEvent" ? "onmessage" : "message";
eventer(messageEvent, function (e) {
if (e.source != iframe.contentWindow){return} // reject messages send from other iframes
if ("dataReceived" in e.data){
if ("sentFromOBS" in e.data.dataReceived){
processIncoming(e.data.dataReceived.sentFromOBS);
}
}
});
function changeScene(scene){
sendToOBS('SetCurrentScene', {
'scene-name': scene
});
}
function processIncoming(data){
if ("scenes" in data){
scenesData = data.scenes;
updateSceneList();
}
if ("callbackData" in data){
console.log(data.callbackData);
} else if ("callbackError" in data){
console.log(data.callbackError);
}
if ("rawData" in data){
var data = JSON.parse(data.rawData);
console.log(data);
if (("update-type" in data) && (data["update-type"]="Heartbeat")){
for (var i in data){
if (i === "stats"){
for (var j in data[i]){
if (document.getElementById("stat-"+j)){
if (typeof data[i][j] == "number"){
data[i][j] = parseInt(data[i][j]*100)/100.0;
}
if (data[i][j]===true){
document.getElementById("stat-"+j).innerHTML = j+ " : <font color='#CFC'>" + data[i][j]+"</font>";
} else if (data[i][j] === false){
document.getElementById("stat-"+j).innerHTML = j+ " : <font color='#FCC'>" + data[i][j]+"</font>";
} else {
document.getElementById("stat-"+j).innerHTML = j+ " : <font color='#DDD'>" + data[i][j]+"</font>";
}
}
}
} else if (document.getElementById("stat-"+i)){
if (data[i]===true){
document.getElementById("stat-"+i).innerHTML = i+ " : <font color='#CFC'>" + data[i]+"</font>";
} else if (data[i] === false){
document.getElementById("stat-"+i).innerHTML = i+ " : <font color='#FCC'>" + data[i]+"</font>";
} else {
document.getElementById("stat-"+i).innerHTML = i+ " : <font color='#DDD'>" + data[i]+"</font>";
}
}
}
} else if ("baseHeight" in data){
for (var i in data){
if (document.getElementById("setting-"+i)){
if (data[i]===true){
document.getElementById("setting-"+i).innerHTML = i+ " : <font color='#CFC'>" + data[i]+"</font>";
} else if (data[i] === false){
document.getElementById("setting-"+i).innerHTML = i+ " : <font color='#FCC'>" + data[i]+"</font>";
} else {
document.getElementById("setting-"+i).innerHTML = i+ " : <font color='#DDD'>" + data[i]+"</font>";
}
}
}
} else if ("sources" in data){
if ("name" in data){
document.getElementById("active_source_list").innerHTML = "";
for (var i =0;i<data.sources.length;i++){
var button = document.createElement("button");
button.innerText = data.sources[i].name;
button.dataset.name = data.sources[i].name;
button.dataset.type = "source"
button.onclick = function(){
console.log("CLICKED: " + this.innerText);
selectedSource = this.dataset.name;
document.getElementById("audio").classList.add("hidden");
var sources = document.querySelectorAll("[data-name");
for (var k = 0 ; k<sources.length; k++){
sources[k].classList.remove("pressed");
}
var sources = document.querySelectorAll("[data-name='"+selectedSource+"']");
console.log(sources);
for (var k = 0 ; k<sources.length; k++){
document.getElementById("audio").classList.remove("hidden");
sources[k].classList.add("pressed");
}
};
document.getElementById("active_source_list").appendChild(button);
}
if (selectedSource){
var sources = document.querySelectorAll("[data-name");
document.getElementById("audio").classList.add("hidden");
for (var k = 0 ; k<sources.length; k++){
sources[k].classList.remove("pressed");
}
var sources = document.querySelectorAll("[data-name='"+selectedSource+"']");
console.log(sources);
for (var k = 0 ; k<sources.length; k++){
sources[k].classList.add("pressed");
document.getElementById("audio").classList.remove("hidden");
}
} else {
document.getElementById("audio").classList.add("hidden");
}
} else {
document.getElementById("source_list").innerHTML = "";
for (var i =0;i<data.sources.length;i++){
var button = document.createElement("button");
button.innerText = data.sources[i].name;
button.dataset.name = data.sources[i].name;
button.dataset.type = "source"
button.onclick = function(){
console.log("CLICKED: " + this.innerText);
selectedSource = this.dataset.name;
document.getElementById("audio").classList.add("hidden");
var sources = document.querySelectorAll("[data-name");
for (var k = 0 ; k<sources.length; k++){
sources[k].classList.remove("pressed");
}
var sources = document.querySelectorAll("[data-name='"+selectedSource+"']");
console.log(sources);
for (var k = 0 ; k<sources.length; k++){
sources[k].classList.add("pressed");
document.getElementById("audio").classList.remove("hidden");
}
};
document.getElementById("source_list").appendChild(button);
}
if (selectedSource){
var sources = document.querySelectorAll("[data-name");
document.getElementById("audio").classList.add("hidden");
for (var k = 0 ; k<sources.length; k++){
sources[k].classList.remove("pressed");
}
var sources = document.querySelectorAll("[data-name='"+selectedSource+"']");
console.log(sources);
for (var k = 0 ; k<sources.length; k++){
sources[k].classList.add("pressed");
document.getElementById("audio").classList.remove("hidden");
}
} else {
document.getElementById("audio").classList.add("hidden");
}
}
<!-- congestion: 0 -->
<!-- droppedFrames: 0 -->
<!-- flags: {audio: true, encoded: true, multiTrack: true, rawValue: 31, service: true, …} -->
<!-- height: 1080 -->
<!-- name: "simple_stream" -->
<!-- reconnecting: false -->
<!-- settings: {bind_ip: 'default', dyn_bitrate: false, low_latency_mode_enabled: false, new_socket_loop_enabled: false} -->
<!-- totalBytes: 351121 -->
<!-- totalFrames: 30 -->
<!-- type: "rtmp_output" -->
<!-- width: 1920 -->
} else if ("outputs" in data){
document.getElementById("outputs").innerHTML = "";
for (var i =0;i<data.outputs.length;i++){
var button = document.createElement("button");
button.innerText = data.outputs[i].name;
button.dataset.output = data.outputs[i].name;
button.onclick = function(){
console.log("CLICKED: " + this.innerText);
var outputName = this.dataset.output;
if (this.classList.contains("pressed")){
this.classList.remove("pressed");
sendToOBS("StopOutput",{outputName:outputName});
} else {
this.classList.add("pressed");
sendToOBS("StartOutput",{outputName:outputName});
}
};
document.getElementById("outputs").appendChild(button);
}
}
}
}
function updateSceneList(){
var scenes = scenesData.scenes;
document.getElementById("scene_list").innerHTML = "";
scenes.forEach(scene => {
var button = document.createElement("button");
button.innerText = scene.name;
button.onclick = function(){
console.log("CLICKED");
changeScene(this.innerText);
}; // "speaker" also works in the same way.
document.getElementById("scene_list").appendChild(button);
if (scene.name === scenesData.currentScene) {
button.classList.add("pressed");
}
});
}
</script>
</body>
</html>

388
examples/obs_ws_dock.html Normal file
View File

@ -0,0 +1,388 @@
<html>
<head>
<script type="text/javascript" src="https://vdo.ninja/beta/thirdparty/obs-websocket.min.js"></script>
<link rel="stylesheet" href="https://vdo.ninja/beta/main.css" />
<title>OBS Controller Demo using VDO.Ninja</title>
<style>
.container {
max-width: 80%;
width: fit-content;
margin: 0 auto;
}
h1 {
margin-top: 1em;
margin-bottom: 1em;
padding: 10px;
}
.card {
margin: 10px;
box-shadow: 0 4px 8px 0 rgb(0 0 0 / 10%);
background-color: #ddd;
color: black;
margin-bottom: 1.5em;
}
.card>div {
padding: 10px;
}
.card h2 {
font-size: 1.5em;
padding: 10px;
background-color: #457b9d;
color: white;
border-bottom: 2px solid #3b6a87;
}
small {
font-style: italic;
display: block;
margin-left: 1em;
}
span.warning {
color: rgb(212, 191, 0);
}
input {
padding: 10px;
display: inline-block;
flex-flow: unset;
margin: 10px;
}
video {
max-width: 640px;
max-height: 360px;
padding: 20px;
}
audio {
max-width: 640px;
max-height: 360px;
padding: 20px;
}
div#processing {
display: none;
justify-content: center;
place-items: center;
position: absolute;
inset: 0;
font-size: 1.5em;
font-weight: bold;
background: #141926;
flex-direction: column;
}
button {
margin:5px;
border:solid black 2px;
}
body {
color:white;
display: inline-block;
flex-flow: unset;
}
a {
color: #CEF!important;
}
#info{
margin: 20px;
max-height: 50%;
overflow: auto;
}
#client {
margin:10px;
display:block;
}
label {
color:white;
}
</style>
</head>
<body>
<div class="container">
<h1>OBS remote (server)</h1>
<span id='setup'>
<label for="address">Websocket Address</label>
<input name="address" id="address" placeholder="address (optional)" value="localhost:4444" />
<label for="address">Websocket Password</label>
<input name="password" id="password" placeholder="password here (optional)" />
<br />
<label for="vdoroomname">Room name to use</label>
<input name="vdoroomname" id="vdoroomname" placeholder="vdo room name to use (optional)" />
<label for="vdopassword">Room password to use</label>
<input name="vdopassword" id="vdopassword" placeholder="vdo password to use (optional)" />
<br />
<button id="address_button">Connect</button>
<button id="address_button_2">Connect and share OBS Output</button>
</span>
<a id="client" target="_blank"></a>
<div id="info"></div>
</div>
<script>
var hostname = "vdo.ninja/beta"; // all that's supported as of this moment.
console.error("TODO: Change hostname");
const obs = new OBSWebSocket();
var scenesData = {};
scenesData.scenes = [];
function sendToOBS(action, data={}){
document.getElementById("info").innerHTML = action + "<br />"+document.getElementById("info").innerHTML;
obs.sendCallback(action, data, sendCallback)
}
(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 roomname = Math.floor(Math.random() * 1000000);
var pwurl = Math.floor(Math.random() * 1000000);
if (urlParams.get("password")){
pwurl = urlParams.get("password");
localStorage.setItem('password',pwurl)
} else if (localStorage.getItem('password')){
pwurl = localStorage.getItem('password');
} else {
localStorage.setItem('password',pwurl)
}
if (urlParams.get("room")){
roomname = urlParams.get("room")
localStorage.setItem('roomname',roomname)
} else if (localStorage.getItem('roomname')){
roomname = localStorage.getItem('roomname');
} else {
localStorage.setItem('roomname',roomname)
}
document.getElementById('vdoroomname').value = roomname;
document.getElementById('vdopassword').value = pwurl;
if (localStorage.getItem('address')){
document.getElementById('address').value = localStorage.getItem('address');
}
if (localStorage.getItem('wspass')){
document.getElementById('password').value = localStorage.getItem('wspass');
}
var iframe = null;
function createIFrame(visible=true){
iframe = document.createElement("iframe");
if (visible){
iframe.src = "https://"+hostname+"/?room="+roomname+"&push=mainOBSOutput&od=0&transparent&webcam&vd=obs&view&password="+pwurl+"&label=OBS_"+Math.floor(Math.random() * 1000000);
iframe.style.minWidth = "720px";
iframe.style.minHeight = "480px";
iframe.style.maxWidth = "50%";
iframe.style.maxHeight = "50%";
iframe.style.display = "block";
iframe.style.margin = "auto";
iframe.allow = "autoplay;camera;microphone;fullscreen;picture-in-picture;";
} else {
iframe.src = "https://"+hostname+"/?room="+roomname+"&push&autostart&vd=0&view&ad=0&transparent&cleanoutput&password="+pwurl+"&label=OBS_"+Math.floor(Math.random() * 1000000);
iframe.style.opacity = 0;
iframe.style.width = 0;
iframe.style.height = 0;
iframe.style.position = "absolulte";
iframe.style.top = "-100px";
iframe.style.left = "-100px";
}
document.getElementById("client").parentNode.insertBefore(iframe, document.getElementById("client").nextSibling);
var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
var eventer = window[eventMethod];
var messageEvent = eventMethod === "attachEvent" ? "onmessage" : "message";
eventer(messageEvent, function (e) {
if (e.source != iframe.contentWindow){return} // reject messages send from other iframes
console.log(e);
if ("dataReceived" in e.data){
if ("sendToOBS" in e.data.dataReceived){
if ("action" in e.data.dataReceived.sendToOBS){
if ("data" in e.data.dataReceived.sendToOBS){
sendToOBS(e.data.dataReceived.sendToOBS.action, e.data.dataReceived.sendToOBS.data);
}
}
}
} else if ("action" in e.data){
if (e.data.action === "new-push-connection"){
console.log(e.data);
updateSceneList();
}
}
});
}
function sendCallback(err, data){
console.log("CALLBACK TRIGGERED");
var msg = {};
msg.sentFromOBS = {};
msg.sentFromOBS.callbackData = data;
msg.sentFromOBS.callbackError = err;
try {
iframe.contentWindow.postMessage({"sendData":msg}, '*');
} catch(e){}
}
function sendRawData(data){
var msg = {};
msg.sentFromOBS = {};
msg.sentFromOBS.rawData = data;
try {
iframe.contentWindow.postMessage({"sendData":msg}, '*');
} catch(e){}
}
function updateSceneList(){
var msg = {};
msg.sentFromOBS = {};
msg.sentFromOBS.scenes = scenesData;
try {
iframe.contentWindow.postMessage({"sendData":msg}, '*');
} catch(e){}
console.log(msg);
obs.send("GetSourcesList");
obs.send('GetCurrentScene');
obs.send("GetVideoInfo");
obs.send("ListOutputs");
}
document.getElementById('address_button').addEventListener('click', e => {
connect(e, false);
});
document.getElementById('address_button_2').addEventListener('click', e => {
connect(e, true);
});
function connect(e, camera){
const address = document.getElementById('address').value || "localhost:4444";
const password = document.getElementById('password').value;
roomname = document.getElementById('vdoroomname').value || Math.floor(Math.random() * 1000000);
pwurl = document.getElementById('vdopassword').value || Math.floor(Math.random() * 1000000);
localStorage.setItem('roomname',roomname)
localStorage.setItem('password',pwurl)
createIFrame(camera); // connects to VDO.Ninja's IFRAME API
localStorage.setItem('address',address);
if (password){
localStorage.setItem('wspass',password);
var ret = obs.connect({
address: address,
password: password
});
} else {
var ret = obs.connect({
address: address
});
}
ret.then(() => {
console.log(`Success!`);
return obs.send('GetSceneList');
}).then(data => {
document.getElementById("setup").style.display = "none";
scenesData = data;
updateSceneList();
var clientLink = "https://"+hostname+"/obs_client.html?room="+roomname+"&password="+pwurl;
document.getElementById("client").href = clientLink;
document.getElementById("client").innerHTML = "<b><font style='color:#70c4ff;'>client link:</font></b> "+clientLink;
document.getElementById("info").innerHTML = "<br /><p style='color:#bdffbd;'>Connection to OBS websockets opened.</p>" + document.getElementById("info").innerHTML;
try {
obs._socket.onmessage2 = obs._socket.onmessage;
obs._socket.onmessage = function(data){
obs._socket.onmessage2(data);
if (data.type && data.data){
if (data.type == "message"){
sendRawData(data.data);
}
}
}
} catch(e){console.error(e);}
}).catch(err => { // Promise convention dicates you have a catch on every chain.
console.log(err);
document.getElementById("info").innerHTML = "<br />Error trying to connect." + document.getElementById("info").innerHTML;
if ("error" in err){
document.getElementById("info").innerHTML = "<br />"+err.error + document.getElementById("info").innerHTML;
}
});
};
// We use the source visibility one and filter visibility web socket commands quite often in shows.
obs.on('SwitchScenes', data => {
console.log(`New Active Scene: ${data.sceneName}`);
scenesData.currentScene = data.sceneName
updateSceneList();
});
obs.on('ConnectionOpened', (data) => function(){
document.getElementById("setup").style.display = "none";
document.getElementById("info").innerHTML = "<br /><p style='color:#bdffbd;'>Connection to OBS websockets opened.</p>" + document.getElementById("info").innerHTML;
});
obs.on('ConnectionClosed', (data) => function(){
document.getElementById("setup").style.display = "unset";
document.getElementById("info").innerHTML = "<br />Connection to OBS websockets closed" + document.getElementById("info").innerHTML;
});
obs.on('AuthenticationSuccess', (data) => function(){
document.getElementById("setup").style.display = "none";
document.getElementById("info").innerHTML = "<br />OBS websockets authenticated" + document.getElementById("info").innerHTML;
});
obs.on('AuthenticationFailure', (data) => function(){
document.getElementById("setup").style.display = "unset";
document.getElementById("info").innerHTML = "<br />Authentication to OBS websockets failed" + document.getElementById("info").innerHTML;
});
obs.on('ScenesChanged', (data) => function(){
scenesData = data;
updateSceneList()
});
// You must add this handler to avoid uncaught exceptions.
obs.on('error', err => {
document.getElementById("info").innerHTML = "<br />OBS websockets error" + document.getElementById("info").innerHTML;
console.error('socket error:', err);
if ("error" in err){
document.getElementById("info").innerHTML = "<br />"+err.error + document.getElementById("info").innerHTML;
}
});
</script>
</body>
</html>

69
examples/p2p.html Normal file
View File

@ -0,0 +1,69 @@
<html>
<body>
<div id="results" style="overflow:scroll;max-height:300px;">
starting...
</div>
<script> // https://jsfiddle.net/steveseguin/0t3ayvk8/31/
var connectionID = Math.random()*100000001;
function RecvDataWindow(){
var iframe = document.createElement("iframe");
iframe.src = "https://vdo.ninja/beta/?view="+connectionID+"&cleanoutput";
iframe.style.width = "0px";
iframe.style.height = "0px";
iframe.style.position = "fixed";
iframe.style.left = "-100px";
iframe.style.top = "-100px";
iframe.id = "frame1"
document.body.appendChild(iframe);
var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
var eventer = window[eventMethod];
var messageEvent = eventMethod === "attachEvent" ? "onmessage" : "message";
eventer(messageEvent, function (e) {
if (e.source != iframe.contentWindow){return} // reject messages send from other iframes
if ("dataReceived" in e.data){ // raw data
document.getElementById("results").innerHTML += e.data.dataReceived+"<br />";
console.log(e.data);
try {
iframe.contentWindow.postMessage({"sendData":"pong!!", "UUID":e.data.UUID}, '*');
} catch(E){}
}
});
}
function SendDataWindow(){
var iframe = document.createElement("iframe");
iframe.src = "https://vdo.ninja/beta/?push="+connectionID+"&vd=0&ad=0&autostart&cleanoutput";
iframe.style.width = "0px";
iframe.style.height = "0px";
iframe.style.position = "fixed";
iframe.style.left = "-100px";
iframe.style.top = "-100px";
iframe.id = "frame2";
document.body.appendChild(iframe);
setInterval(function(){
try {
console.log(".");
document.getElementById("frame2").contentWindow.postMessage({"sendData":"ping!!"}, '*');
} catch(E){}
}, 1000);
var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
var eventer = window[eventMethod];
var messageEvent = eventMethod === "attachEvent" ? "onmessage" : "message";
eventer(messageEvent, function (e) {
if (e.source != iframe.contentWindow){return} // reject messages send from other iframes
if ("dataReceived" in e.data){ // raw data
console.log(e.data);
document.getElementById("results").innerHTML += e.data.dataReceived+"<br />";
}
});
}
SendDataWindow();
RecvDataWindow();
</script>
</body>
</html>

280
examples/sensors.html Normal file
View File

@ -0,0 +1,280 @@
<html>
<head><title>Sensor and video stream access example</title>
<style>
body{
padding:0;
margin:0;
background-color: rgb(222,242,253);
}
iframe {
border:0;
margin:0;
padding:0;
display:block;
margin:10px;
width:640px;
height:320px;
}
#viewlink {
width:400px;
}
#container {
display:block;
padding:0px;
}
input{
padding:5px;
margin:5px;
}
button{
padding:5px;
margin:5px;
}
canvas{
width:100%;
display:block;
margin:0;
padding:0;
}
</style>
</head>
<body>
<canvas id="canvas" style="display:none;max-height:70vh;max-width:calc(70vh*1.777);width:100%;height:100%;" width="1920" height="1080" ></canvas>
<input placeholder="Enter a VDO.Ninja View URL here" id="viewlink" style="display:block;" onchange="loadIframe();"/>
<label for="hori">FOA-Horizontal</label>
<input type="range" id="hori" name="hori" value="63" title="63" min="40" max="80" title="67" onchange="updateHor(this);">
<label for="vert">FOA-Vertical</label>
<input type="range" id="vert" name="vert" value="50" title="50" min="30" max="70" onchange="updateVer(this);">
<label for="draw">Draw Delay</label>
<input type="range" id="draw" name="draw" value="110" title="110" min="0" max="500" style="width:500px" onchange="updateDelay(this);"><br /><br />
Add &sensor to the push link to send data; see: <a target="_blank" href="https://docs.vdo.ninja/source-settings/sensor">https://docs.vdo.ninja/source-settings/sensor</a>
<div id="container">
</div>
<script>
// https://www.camerafv5.com/devices/manufacturers/google/pixel_4a_sunfish_1/ ; pixel 4a specs
var horFOA = 49.6;
var verFOA = 63.3;
var drawDelay = 110;
function updateHor(hor){
horFOA = parseInt(hor.value);
hor.title = horFOA;
}
function updateVer(ver){
verFOA = parseInt(ver.value);
ver.title = verFOA;
}
function updateDelay(time){
drawDelay = parseInt(time.value);
time.title = drawDelay;
}
function loadIframe(url=false){ // this is pretty important if you want to avoid camera permission popup problems. You can also call it automatically via: <body onload=>loadIframe();"> , but don't call it before the page loads.
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext('2d');
ctx.imageSmoothingEnabled= false;
var iframe = document.createElement("iframe");
var iframeContainer = document.createElement("div");
if (url){
var iframesrc = url;
} else {
var iframesrc = document.getElementById("viewlink").value;
}
console.log(iframesrc);
document.getElementById("viewlink").parentNode.removeChild(document.getElementById("viewlink"));
document.getElementById("canvas").style.display="block";
iframe.allow = "autoplay;camera;microphone;fullscreen;picture-in-picture;";
if (iframesrc==""){
iframesrc="./";
}
iframe.src = iframesrc;
iframeContainer.appendChild(iframe);
document.getElementById("container").appendChild(iframeContainer);
var videos = iframe.contentWindow.document.querySelectorAll("video");
var sensors = {};
function drawFrame(vid){
try {
if (sensors.mag){ // androids may not support this.
var angle = 1.5 * Math.PI - Math.atan2(sensors.mag.y,sensors.mag.x);
var startPixel = (angle / ( 2 * Math.PI)) * 1920;
var endPixel = (verFOA/360) * 1920 + startPixel;
} else if (sensors.ori){
var angle = sensors.ori.a;
var frontToBack = sensors.ori.b;
var leftToRight = sensors.ori.g;
var startPixel = Math.floor((angle / 360) * 1920);
var width = Math.floor((verFOA/360) * 1920);
var height = vid.videoHeight*(width/vid.videoWidth);
var h_offset = Math.floor(((frontToBack+(verFOA/2))/180 * 1080)-540);
var w_offset = Math.floor((leftToRight+horFOA)/180 * 1920);
}
setTimeout(function(a1,a2,a3,a4,a5){
ctx.filter = 'blur(4px)';
ctx.drawImage(a1,a2,a3,a4,a5);
ctx.filter = "none";
ctx.drawImage(a1,a2,a3,a4,a5);
}, drawDelay, vid, startPixel-w_offset, h_offset, width, height);
} catch(e){console.error(e);}
};
setInterval(function(){
if (videos.length){
if ("UUID" in sensors){
if (videos[0].id !== "videosource_"+sensors.UUID){
videos = iframe.contentWindow.document.querySelectorAll("video#videosource_"+sensors.UUID);
}
if (videos.length){
drawFrame(videos[0]);
}
}
}
},100);
//////////// LISTEN FOR EVENTS
var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
var eventer = window[eventMethod];
var messageEvent = eventMethod === "attachEvent" ? "onmessage" : "message";
/// If you have a routing system setup, you could have just one global listener for all iframes instead.
eventer(messageEvent, function (e) {
if (e.source != iframe.contentWindow){return} // reject messages send from other iframes
if ("stats" in e.data){
var outputWindow = document.createElement("div");
//console.log(e.data.stats);
var out = "<br />total_inbound_connections:"+e.data.stats.total_inbound_connections;
out += "<br />total_outbound_connections:"+e.data.stats.total_outbound_connections;
for (var streamID in e.data.stats.inbound_stats){
out += "<br /><br /><b>streamID:</b> "+streamID+"<br />";
out += printValues(e.data.stats.inbound_stats[streamID]);
}
outputWindow.innerHTML = out;
iframeContainer.appendChild(outputWindow);
}
if ("gotChat" in e.data){
var outputWindow = document.createElement("div");
outputWindow.innerHTML = e.data.gotChat.msg;
outputWindow.style.border="1px dotted black";
iframeContainer.appendChild(outputWindow);
}
if ("action" in e.data){
var outputWindow = document.createElement("div");
outputWindow.innerHTML = "child-page-action: "+e.data.action+"<br />";
outputWindow.style.border="1px dotted black";
iframeContainer.appendChild(outputWindow);
console.log(e.data.action);
if (e.data.action == "new-view-connection"){
setTimeout(function(){
videos = iframe.contentWindow.document.querySelectorAll("video");
console.log(videos);
},500);
}
}
if ("streamIDs" in e.data){
var outputWindow = document.createElement("div");
outputWindow.innerHTML = "child-page-action: streamIDs<br />";
for (var key in e.data.streamIDs) {
outputWindow.innerHTML += "streamID: " + key + ", label:"+e.data.streamIDs[key] + "\n";
}
outputWindow.style.border="1px dotted black";
iframeContainer.appendChild(outputWindow);
}
if ("loudness" in e.data){
//console.log(e.data);
if (document.getElementById("loudness")){
outputWindow = document.getElementById("loudness");
} else {
var outputWindow = document.createElement("div");
outputWindow.style.border="1px dotted black";
iframeContainer.appendChild(outputWindow);
outputWindow.id = "loudness";
}
outputWindow.innerHTML = "child-page-action: loudness<br />";
for (var key in e.data.loudness) {
outputWindow.innerHTML += key + " Loudness: " + e.data.loudness[key] + "\n";
}
outputWindow.style.border="1px black";
}
if ("sensors" in e.data){
sensors = e.data.sensors;
if (document.getElementById("sensors")){
outputWindow = document.getElementById("sensors");
} else {
var outputWindow = document.createElement("div");
outputWindow.style.border="1px dotted black";
iframeContainer.appendChild(outputWindow);
outputWindow.id = "sensors";
console.log(sensors);
}
outputWindow.innerHTML = "child-page-action: sensors<br /><br />";
for (var key in e.data.sensors.lin) {
outputWindow.innerHTML += key + " linear: " + e.data.sensors.lin[key] + "<br />";
}
for (var key in e.data.sensors.acc) {
outputWindow.innerHTML += key + " acceleration: " + e.data.sensors.acc[key] + "<br />";
}
for (var key in e.data.sensors.gyro) {
outputWindow.innerHTML += key + " gyro: " + e.data.sensors.gyro[key] + "<br />";
}
for (var key in e.data.sensors.mag) {
outputWindow.innerHTML += key + " magnet: " + e.data.sensors.mag[key] + "<br />";
}
for (var key in e.data.sensors.ori) {
outputWindow.innerHTML += key + " orientation: " + e.data.sensors.ori[key] + "<br />";
}
outputWindow.style.border="1px black";
}
});
}
function printValues( obj) {
var out = "";
for (var key in obj) {
if (typeof obj[key] === "object") {
out +="<br />";
out += printValues(obj[key]);
} else {
out +="<b>"+key+"</b>: "+obj[key]+"<br />";
}
}
return out;
}
</script>
</body>
</html>

111
examples/twitch.html Normal file
View File

@ -0,0 +1,111 @@
<html>
<head><title>Twitch + Video</title>
<meta name="viewport" content="width=device-width, initial-scale=0.7, maximum-scale=1.0, user-scalable=yes" />
<meta content="text/html;charset=utf-8" http-equiv="Content-Type" />
<style>
body{
padding:0;
margin:0;
background-color:#003;
width:100%;
height:100%;
}
iframe {
width:100%;
height:100%;
border:0;
margin:0;
padding:0;
position:absolute;
display:block;
}
input{
padding:10px;
width:80%;
font-size:1.2em;
z-index: 1000;
}
@media screen and (orientation:portrait) {
#container2{
width:100%;height:100%;display:none;
}
#container1{
width: 50vw;height: 50vh; display:none; float:left; position: fixed; top: 0; right: 0%;
}
iframe{
width:100%;
}
}
@media screen and (orientation:landscape) {
#container2{
width:60vw;height:100%;display:none;
z-index:5;
}
#container1{
width: 50vw;height: 80vh; display:none; float:left; position: fixed; top: 0; right: -10vw;
}
iframe{
max-width:60vw;
}
}
</style>
</head>
<body>
<div id="container2"></div>
<div id="container1" ></div>
<div id="clean">
<input placeholder="Enter a VDON stream ID" id="viewlink" type="text" />
<input placeholder="Enter the Twitch channel name" id="twitch" type="text" />
<button onclick="loadIframes()" style="display:block;padding:10px;margin:10px;">START</button>
</div>
<script>
window.addEventListener("orientationchange", function() {
// Announce the new orientation number
// alert(window.orientation);
}, false);
function loadIframes(url=false){
var roomname = document.getElementById("viewlink").value;
var twitch = document.getElementById("twitch").value;
document.getElementById("clean").parentNode.removeChild(document.getElementById("clean"));
document.getElementById("container1").style.display="inline-block";
document.getElementById("container2").style.display="inline-block";
var path = window.location.host+window.location.pathname.split("/").slice(0,-1).join("/");
var room1 = "https://"+path+"/?push="+roomname+"&webcam&autostart&vd=front&ad=1&transparent&noheader";
var room2 = "https://www.twitch.tv/embed/"+twitch+"/chat?parent="+location.hostname;
var iframe = document.createElement("iframe");
iframe.allow = "autoplay;camera;microphone;fullscreen;picture-in-picture;";
iframe.src = room1;
var iframeContainer = document.createElement("div");
iframeContainer.appendChild(iframe);
document.getElementById("container1").appendChild(iframeContainer);
setTimeout(function(){
var iframe = document.createElement("iframe");
iframe.allow = "autoplay;camera;microphone;fullscreen;picture-in-picture;";
iframe.src = room2;
var iframeContainer = document.createElement("div");
iframeContainer.appendChild(iframe);
document.getElementById("container2").appendChild(iframeContainer);
},3000);
}
</script>
</body>
</html>

View File

@ -38,9 +38,7 @@ function loadIframe(){ // this is pretty important if you want to avoid camera
var iframe = document.createElement("iframe");
var iframeContainer = document.createElement("div");
var iframesrc = document.getElementById("viewlink").value;
iframe.allow="autoplay;camera;microphone";
iframe.allowtransparency="true";
iframe.allowfullscreen ="true";
iframe.allow = "autoplay;camera;microphone;fullscreen;picture-in-picture;";
if (iframesrc==""){
iframesrc="./";
@ -163,8 +161,21 @@ function loadIframe(){ // this is pretty important if you want to avoid camera
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Request Stats";
button.onclick = function(){iframe.contentWindow.postMessage({"getStats":true}, '*');};
button.innerHTML = "Pan Left";
button.title = "Requires &panning to be added to the view link";
button.onclick = function(){iframe.contentWindow.postMessage({"panning":0}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Pan right";
button.title = "Requires &panning to be added to the view link";
button.onclick = function(){iframe.contentWindow.postMessage({"panning":180}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Pan Center";
button.title = "Requires &panning to be added to the view link";
button.onclick = function(){iframe.contentWindow.postMessage({"panning":90}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
@ -394,7 +405,7 @@ function printValues( obj) {
</head>
<body>
<input placeholder="Enter an OBS.Ninja View URL here" id="viewlink" />
<input placeholder="Enter an VDO.Ninja View URL here" id="viewlink" />
<button onclick="loadIframe();">ADD</button>
<input type="checkbox" id="clean" checked>Clean Output
<input type="checkbox" id="transparent" checked>Transparent

View File

@ -55,7 +55,7 @@
}
</style>
<link rel="stylesheet" href="./lineawesome/css/line-awesome.min.css" />
<link rel="stylesheet" href="./main.css?ver=70" />
<link rel="stylesheet" href="./main.css?ver=72" />
<script type="text/javascript" crossorigin="anonymous" src="./thirdparty/adapter.min.js"></script>
<style id="lightbox-animations" type="text/css"></style>
</head>
@ -67,8 +67,8 @@
<span itemprop="thumbnail" itemscope itemtype="http://schema.org/ImageObject">
<link itemprop="url" href="./media/obsNinja_logo_full.png" />
</span>
<script type="text/javascript" crossorigin="anonymous" src="./thirdparty/CodecsHandler.js?ver=33"></script>
<script type="text/javascript" crossorigin="anonymous" src="./webrtc.js?ver=215"></script>
<script type="text/javascript" crossorigin="anonymous" src="./thirdparty/CodecsHandler.js?ver=34"></script>
<script type="text/javascript" crossorigin="anonymous" src="./webrtc.js?ver=291"></script>
<input id="zoomSlider" type="range" style="display: none;" />
<div id="header">
@ -89,12 +89,11 @@
<a
id="reshare"
data-drag="1"
onclick="popupMessage(event);copyFunction(this)"
onclick="copyFunction(this, event)"
class="task grabLinks"
onmousedown="copyFunction(this)"
style="font-weight: bold; color: #afa !important; cursor: grab; background-color: #0000; font-size: 115%; min-width: 335px; max-width: 800px;"
></a>
<i class="las la-paperclip" style="color: #DDD;" onclick="popupMessage(event);copyFunction(document.getElementById('reshare'));" onmouseover="this.style.cursor='pointer'"></i>
<i class="las la-paperclip" style="color: #DDD;" onclick="copyFunction(document.getElementById('reshare'), event);" onmouseover="this.style.cursor='pointer'"></i>
</div>
<div id="head4" style="display: inline-block;" class="advanced">
<font style="font-size: 68%; color: white;">
@ -143,7 +142,7 @@
<div id="screenshare2button" onmousedown="event.preventDefault(); event.stopPropagation();" title="Create a Secondary Stream" alt="Create a Secondary Stream" onclick="createIframePopup()" tabindex="20" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="float advanced" style="cursor: pointer;">
<i id="screenshare2toggle" onmousedown="event.preventDefault(); event.stopPropagation();" class="toggleSize las la-tv my-float"></i>
</div>
<div id="websitesharebutton" onmousedown="event.preventDefault(); event.stopPropagation();" title="Share a website as an embedded iFRAME" alt="Share a website as an embedded iFRAME" onclick="shareWebsite()" tabindex="21" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="float advanced" style="cursor: pointer;">
<div id="websitesharebutton" onmousedown="event.preventDefault(); event.stopPropagation();" title="Share a website as an embedded iFRAME" alt="Share a website as an embedded iFRAME" onclick="shareWebsite(false, event)" tabindex="21" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="float advanced" style="cursor: pointer;">
<i id="websitesharetoggle" onmousedown="event.preventDefault(); event.stopPropagation();" class="toggleSize las la-window-maximize my-float"></i>
</div>
@ -317,10 +316,15 @@
<button onclick="this.disabled=true;setTimeout(function(){requestBasicPermissions();},20);" id="getPermissions" style="display:none;" data-ready="false" >
<span data-translate="ask-for-permissions">Allow Access to Camera/Microphone</span>
</button>
<button onclick="publishWebcam(this)" title="start streaming" tabindex="15" id="gowebcam" class="gowebcam" alt="Start Streaming" disabled data-audioready="false" data-ready="false" >
<span data-translate="waiting-for-camera">Waiting for Camera to Load</span>
</button>
<br />
<span style="display:block;">
<button onclick="publishWebcam(this)" title="start streaming" tabindex="15" id="gowebcam" class="gowebcam" alt="Start Streaming" disabled data-audioready="false" data-ready="false" >
<span data-translate="waiting-for-camera">Waiting for Camera to Load</span>
</button>
</span>
<div id="consentWarning" class="startupWarning advanced">
<i class="las la-exclamation-circle"></i>
<p><span data-translate="privacy-disabled">Privacy warning: The director will be able to remotely change your camera and microphone.</span></p>
</div>
<span id="guestTips" style="display:none">
<p>For the best possible experience, make sure</p>
<span><i class="las la-plug"></i><span>Your device is powered</span></span>
@ -392,7 +396,7 @@
<option value="4" data-translate="digital-greenscreen">Digital greenscreen</option>
<option value="5" data-translate="virtual-background">Virtual background</option>
</select>
<span data-warnSimdNotice="true" style='display:none; font-size: 140%; margin-left:10px; vertical-align: middle; cursor:pointer' title="Improve performance and quality with this tip" onclick="warnUser(`For improved performance, use Chrome v87 or newer with SIMD support enabled.<br />Enable SIMD here: <a href='chrome://flags/#enable-webassembly-simd' target='_blank' onclick='popupMessage(event);copyFunction(this)' >chrome://flags/#enable-webassembly-simd</a>`);">
<span data-warnSimdNotice="true" style='display:none; font-size: 140%; margin-left:10px; vertical-align: middle; cursor:pointer' title="Improve performance and quality with this tip" onclick="warnUser(`For improved performance, use Chrome v87 or newer with SIMD support enabled.<br />Enable SIMD here: <a href='chrome://flags/#enable-webassembly-simd' target='_blank' onclick='copyFunction(this,event)' >chrome://flags/#enable-webassembly-simd</a>`);">
<i class="las la-info-circle"></i>
</span>
<span data-effectsNotice="true" style='display:none; font-size: 140%; margin-left:10px; vertical-align: middle; cursor:pointer' title="Improve performance and quality with this tip" onclick="warnUser('Use a Chromium Based Browser');">
@ -429,12 +433,14 @@
margin: 0 6px;"/>
</span>
<div id="SafariWarning">
<div id="SafariWarning" class="startupWarning advanced">
<i class="las la-exclamation-circle"></i>
<p><span data-translate="use-chrome-instead">Consider using a Chromium-based browser instead.<br />
Safari is more prone to having audio issues</span></p>
</div>
</div>
<div class="outer close">
<div class="inner">
@ -461,7 +467,7 @@
<i class="las la-cog" style="font-size: 170%; vertical-align: middle;" aria-hidden="true"></i>
</span>
<center>
<span id="videoSettings2" style="margin: auto auto; display: none; background-color: white; vertical-aligh: middle; border: 3px solid #ccc; max-width: 500px; padding: 10px 10px 5px 10px; margin: 10px 0 5px 0;">
<span id="videoSettings2" style="margin: auto auto; display: none; background-color: white; vertical-aligh: middle; border: 3px solid #ccc; max-width: 500px; padding: 10px 10px 5px 10px; margin: 0px 0px 10px;">
<form id="webcamquality2">
<input type="radio" id="fullhd2" name="resolution2" value="0" />
<label for="fullhd">
@ -480,8 +486,11 @@
<div id="webcamstats2" style="padding: 5px 0 0 0;"></div>
</form>
</span>
<br />
</center>
<div id="consentWarning2" class="startupWarning advanced">
<i class="las la-exclamation-circle"></i>
<p><span data-translate="privacy-disabled">Privacy warning: The director will be able to remotely access your camera and microphone if you continue.</span></p>
</div>
<p id="audioScreenShare1">
<i class="las la-microphone-alt"></i>
<span data-translate="audio-sources">Audio Sources</span>
@ -495,12 +504,10 @@
</select>
</p>
<br />
<span id="headphonesDiv2" style="background-color: #f3f3f3; min-width: 270px; display: none; padding: 5px 10px; border: 1px solid #ccc; vertical-align: middle;">
<span id="headphonesDiv2">
<i class="las la-headphones"></i>
<span data-translate="select-output-source"> Audio Output Destination: <button onclick="playtone(true)" class="white" style="padding:3px 5px 2px 5px; margin:0; margin-left:15px; position: relative; " type="button">Test</button></span>
<br />
<select id="outputSourceScreenshare" style="background-color: #FFF; padding:10px 5px; min-width: 268px; display:inline-block; vertical-align: middle;" onclick="requestOutputAudioStream();">
<select id="outputSourceScreenshare" style="background-color: #FFF; margin-top:5px; padding:10px 5px; width:100%; display:inline-block; vertical-align: middle;" onclick="requestOutputAudioStream();">
<option value="default">
Default Device
</option>
@ -740,7 +747,7 @@
👋 👀 Welcome to VDO Ninja! We've rebranded! 📼 Nothing else is changing and we're staying 100% free.
</h4>
<br />
🌻 Site Updated on July XXth. The <a href="https://docs.vdo.ninja/release-notes/v18_3">v18.3 release notes are here</a>. If new issues occur, the previous version can also be <a href="https://vdo.ninja/v18/">found here</a>.
🌻 Site Updated on August 29th. The <a href="https://docs.vdo.ninja/release-notes/v19">v19.0 release notes are coming soon</a>. If new issues occur, the previous version can also be <a href="https://vdo.ninja/v183/">found here</a>.
<br />
<br />
@ -811,7 +818,7 @@
<div class='directorBlock'>
<h2 title="Invite a guest or camera source to publish into the group room" style="margin-top: 5px;"><i class="las la-video director-link-icons" ></i> INVITE A GUEST</h2>
<span style="margin:5px; line-height: 1.6;" data-translate='invite-users-to-join'>Guests can use the link to join the group room</span>
<a onclick='popupMessage(event);copyFunction(this)' id="director_block_1" onmousedown='copyFunction(this)' class='task grabLinks' style='cursor:copy;background-color: #0003;'></a>
<a onclick='copyFunction(this,event)' id="director_block_1" class='task grabLinks' style='cursor:copy;background-color: #0003;'></a>
<span style="display:block;">
<span style="bottom: 0; margin: 0 0 0 10px; top: 22px; position: relative;">
<label class="switch" title="If disabled, the invited guest will not be able to see or hear anyone in the room.">
@ -820,7 +827,7 @@
</label>
<span data-translate="guests-hear-others">Guests hear others</span>
</span>
<button class='pull-right grey' style='font-size:1.15em' onclick='popupMessage(event);copyFunction(getById("director_block_1"))'><i class='las la-copy'></i>Copy link</button>
<button class='pull-right grey' style='font-size:1.15em' onclick='copyFunction(getById("director_block_1"),event)'><i class='las la-copy'></i>Copy link</button>
<button class='pull-right grey' style='font-size:1.15em' id="showCustomizerButton1" onclick='showCustomizer(1,this)'><i class='las la-tools'></i>Customize</button>
<span>
</div>
@ -830,7 +837,7 @@
<div class='directorBlock' style="background-color: var(--green-accent);" >
<h2 title="Use this link in the OBS Browser Source to capture the video or audio" style="margin-left: 1px;margin-top: 5px;"><i class="las la-th-large director-link-icons" style="margin-right: 6px;" ></i> <span data-translate="capture-a-group-scene">CAPTURE A GROUP SCENE</span></h2>
<span style="margin:5px; line-height: 1.6;" data-translate='this-is-obs-browser-source-link'>Use in OBS or other studio software to capture the group video mix</span>
<a onclick='popupMessage(event);copyFunction(this)' id="director_block_3" onmousedown='copyFunction(this)' class='task grabLinks' style='cursor:copy;background-color: #0003;'></a>
<a onclick='copyFunction(this,event)' id="director_block_3" class='task grabLinks' style='cursor:copy;background-color: #0003;'></a>
<span style="display:block;">
<span style="bottom: 0; margin: 0 0 0 10px; top: 22px; position: relative;">
<label class="switch" title="If disabled, you must manually add a video to a scene for it to appear.">
@ -839,7 +846,7 @@
</label>
<span data-translate="auto-add-guests">Auto-add guests</span>
</span>
<button class='pull-right grey' style='font-size:1.15em' onclick='popupMessage(event);copyFunction(getById("director_block_3"))'><i class='las la-copy'></i>Copy link</button>
<button class='pull-right grey' style='font-size:1.15em' onclick='copyFunction(getById("director_block_3"),event)'><i class='las la-copy'></i>Copy link</button>
<button class='pull-right grey' style='font-size:1.15em' id="showCustomizerButton3" onclick='showCustomizer(3,this)'><i class='las la-tools'></i>Customize</button>
<span>
</div>
@ -914,19 +921,17 @@
</label>
<span data-translate="auto-select-camera">Auto-select default camera</span>
<Br />
<label class="switch" title="The camera will load in a default safe-mode that may work if other modes fail.">
<input type="checkbox" data-param="&safemode" onchange="updateLink(1,this);">
<span class="slider"></span>
</label>
<span data-translate="compatibility-mode">Compatibility Mode</span>
<Br />
<label class="switch" title="The guest won't have access to changing camera settings or screenshare">
<input type="checkbox" data-param="&ns" onchange="updateLink(1,this);">
<span class="slider"></span>
</label>
<span data-translate="hide-setting-buttons">Hide settings button</span>
<Br />
<label class="switch" title="The guest's self-video preview will appear tiny in the top right">
<input type="checkbox" data-param="&mini" onchange="updateLink(1,this);">
<span class="slider"></span>
</label>
<span data-translate="mini-self-preview">Mini self-preview</span>
<Br />
<label class="switch" title="Allow the guests to pick a virtual backscreen effect">
<input type="checkbox" data-param="&effects" onchange="updateLink(1,this);">
@ -935,7 +940,6 @@
<font class="tooltip" style='cursor: help;position:relative;bottom:2px;font-family:"Noto Color Emoji", "Apple Color Emoji", "Segoe UI Emoji", Times, Symbola, Aegyptus, Code2000, Code2001, Code2002, Musica, serif, LastResort;'><span class="tooltiptext">Uses more CPU and freezes the video if the guest doesn't keep the tab visible.</span></font> <span data-translate="virtual-backgrounds">Virtual backgrounds</span>
<br />
<label class="switch" title="Videos use an animated transition when being remixed">
<input type="checkbox" data-param="&animate" onchange="updateLink(1,this);">
<span class="slider"></span>
@ -991,8 +995,16 @@
<input type="checkbox" data-param="&webp" onchange="updateLinkWebP(1,this);">
<span class="slider"></span>
</label>
<font class="tooltip" style='cursor: help;position:relative;bottom:2px;font-family:"Noto Color Emoji", "Apple Color Emoji", "Segoe UI Emoji", Times, Symbola, Aegyptus, Code2000, Code2001, Code2002, Musica, serif, LastResort;'><span class="tooltiptext">You must keep the director's tab open and visible for this to work, or use the electron capture app.</span></font>
<font class="tooltip" style='cursor: help;position:relative;bottom:2px;font-family:"Noto Color Emoji", "Apple Color Emoji", "Segoe UI Emoji", Times, Symbola, Aegyptus, Code2000, Code2001, Code2002, Musica, serif, LastResort;'><span class="tooltiptext">Pretty experimental and has Lo-Fi quality, though relatively low CPU usage.</span></font>
<span data-translate="low-cpu=broadcast-codec">Low-CPU broadcast codec</span>
<Br />
<label class="switch" title="The guest's self-video preview will appear tiny in the top right">
<input type="checkbox" data-param="&mini" onchange="updateLink(1,this);">
<span class="slider"></span>
</label>
<span data-translate="mini-self-preview">Mini self-preview</span>
<Br />
<label class="switch" title="The guest can only see the Director's video, if provided">
<input type="checkbox" data-param="&broadcast" id="broadcastSlider" onchange="updateLink(1,this);">
@ -1143,6 +1155,7 @@
<h4 style='color:#CCC;margin:20px 20px 0 20px;' data-translate='more-than-four-can-join' >These four guest slots are just for demonstration. More than four guests can actually join a room.</h4>
</div></div>
</div>
<div id="hiddenElements"></div>
<div id="overlayClockContainer" data-initial="600" class="advanced"><span id="overlayClock"></span></div>
<div id="overlayMsgs" onclick="function(e){e.target.innerHTML = '';}" style="display:none"></div>
<div id="bigPlayButton" onclick="function(e){e.target.innerHTML = '';}" style="display:none"></div>
@ -1178,9 +1191,10 @@
</button>
<!---- /////// BREAK //////// -->
<span style="user-select: none;grid-column: 1;width:100%;margin:5px 0 ;font-size:80%; cursor: pointer;" onclick="toggleByDataset('1');getById('chevarrow3').classList.toggle('bottom');getById('chevarrow3').classList.toggle('right');"><i id="chevarrow3" style="padding:0px 7px 0 3px;" class="chevron right" aria-hidden="true"></i><span data-translate="More-scene-options">More scene options</span></span>
<span class="hideDropMenu" onclick="toggleByDataset('1');getById('chevarrow3').classList.toggle('bottom');getById('chevarrow3').classList.toggle('right');"><i id="chevarrow3" style="padding:0px 7px 0 3px;" class="chevron right" aria-hidden="true"></i><span data-translate="More-scene-options">More scene options</span></span>
<span class="hideDropMenu" style="grid-column: 2;"></span>
<button data-action-type="addToScene" class="hidden" data-cluster="1" style="grid-column: 1;" data-scene="2" title="Add this Video to any remote '&scene=2'" onclick="directEnable(this, event, 2);">
<button data-action-type="addToScene" class="hidden" data-cluster="1" data-scene="2" title="Add this Video to any remote '&scene=2'" onclick="directEnable(this, event, 2);">
<i class="las la-plus-square" style="color:#060"></i>
<span data-translate="add-to-scene2">add to scene 2</span>
</button>
@ -1190,7 +1204,7 @@
<span data-translate="mute-scene" >mute in scene</span>
</button>
<span class="hidden" data-cluster="1" >
<span class="hidden" data-cluster="1" data-action-type="sceneCluster1">
<button style="width: 35.2px" data-action-type="addToScene" data-scene="3" data-action-type="add-scene-3" title="Add to Scene 3" onclick="directEnable(this, event, 3);">
<span >S3</span>
</button>
@ -1202,7 +1216,7 @@
</button>
</span>
<span class="hidden" data-cluster="1">
<span class="hidden" data-cluster="1" data-action-type="sceneCluster2">
<button style="width: 35.2px" data-action-type="addToScene" data-scene="6" data-action-type="add-scene-6" title="Add to Scene 6" onclick="directEnable(this, event, 6);">
<span >S6</span>
</button>
@ -1214,7 +1228,7 @@
</button>
</span>
<button class="hidden" data-cluster="1" data-action-type="force-keyframe" style="grid-column: 1; background-image: linear-gradient(90deg, #C9F0FF 0%, #FFDFB9 39%, #FFDFDF 70%, #D9FFEC 100%);" data-value="0" title="Force the remote sender to issue a keyframe to all scenes, fixing Pixel Smearing issues." onclick="requestKeyframeScene(this, event);">
<button class="hidden" data-cluster="1" data-action-type="force-keyframe" style=" background-image: linear-gradient(90deg, #C9F0FF 0%, #FFDFB9 39%, #FFDFDF 70%, #D9FFEC 100%);" data-value="0" title="Force the remote sender to issue a keyframe to all scenes, fixing Pixel Smearing issues." onclick="requestKeyframeScene(this);">
<span data-translate="force-keyframe">Rainbow Puke Fix</span>
</button>
@ -1224,10 +1238,11 @@
</button>
<!---- /////// BREAK //////// -->
<span style="user-select: none;grid-column: 1;width:100%;margin:5px 0 ;font-size:80%;cursor: pointer;" onclick="toggleByDataset('2');getById('chevarrow4').classList.toggle('bottom');getById('chevarrow4').classList.toggle('right');"><i id="chevarrow4" class="chevron right" aria-hidden="true" style="padding:0px 7px 0 3px;" ></i><span data-translate="additional-controls">Additional controls</span></span>
<span class="hideDropMenu" onclick="toggleByDataset('2');getById('chevarrow4').classList.toggle('bottom');getById('chevarrow4').classList.toggle('right');"><i id="chevarrow4" class="chevron right" aria-hidden="true" style="padding:0px 7px 0 3px;" ></i><span data-translate="additional-controls">Additional controls</span></span>
<span class="hideDropMenu" style="grid-column: 2;" ></span>
<button class="hidden" data-cluster="2" data-action-type="solo-video" style="grid-column: 1; text-shadow: 0px 0px yellow;" data-value="0" title="Solo this video everywhere" onclick="requestInfocus(this);">
<button class="hidden" data-cluster="2" data-action-type="solo-video" style="text-shadow: 0px 0px yellow;" data-value="0" title="Solo this video everywhere" onclick="requestInfocus(this);">
<i class="las la-user"></i>
<span data-translate="solo-video">Highlight guest</span>
</button>
@ -1245,7 +1260,7 @@
<i class="las la-eye-slash"></i> <span data-translate="toggle-remote-display">Blind Guest</span>
</button>
<span class="hidden" data-cluster="2">
<span class="hidden" data-cluster="2" data-action-type="ordering">
<button style="width:34px;" data-action-type="order-down" title="Shift this Video Down in Order" onclick="changeOrder(-1,this.dataset.UUID);">
<span data-translate="order-down"><i class="las la-minus"></i></span>
</button>
@ -1294,7 +1309,7 @@
<input data-action-type="volume" type="range" min="0" max="200" value="100" title="Remotely change the volume of this guest" oninput="remoteVolumeUI(this)" ondblclick="this.value=100;remoteVolume(this);remoteVolumeUI(this);" onchange="remoteVolume(this);" style="grid-column: 2; margin:5px; width: 93%; position: relative;top: 0.6em; background-color:#fff0;"/><span class="tooltiptext" style='float: right; overflow: auto; left: 40px; width: 2.5em; top: -13px; margin: 0; position:relative;font-family:"Noto Color Emoji", "Apple Color Emoji", "Segoe UI Emoji", Times, Symbola, Aegyptus,Code2000, Code2001, Code2002, Musica, serif, LastResort;' >100</span>
</font>
<span class="hidden" data-cluster="2">
<span class="hidden" data-cluster="2" data-action-type="change-quality">
<button style="width: 35.2px" data-action-type="change-quality1" title="Disable Video Preview" onclick="toggleQualityDirector(0, this.dataset.UUID, this);">
<span data-translate="change-to-low-quality">&nbsp;&nbsp;<i class="las la-video-slash"></i></span>
</button>
@ -1386,7 +1401,7 @@
<button data-action-type="solo-video" data-value="0" title="Solo this video everywhere" onclick="requestInfocus(this);">
<i class="las la-user"></i>
<span data-translate="solo-video">Highlight guest</span>
<span data-translate="solo-video">Highlight</span>
</button>
<button data-action-type="recorder-local" title="Start Recording this remote stream to this local drive. *experimental*'" onclick="recordLocalVideoToggle();">
@ -1476,7 +1491,7 @@
<option value="4" data-translate="digital-greenscreen">Digital greenscreen</option>
<option value="5" data-translate="virtual-background">Virtual background</option>
</select>
<span data-warnSimdNotice="true" style='display:none; font-size: 140%; margin-left:10px; vertical-align: middle; cursor:pointer' title="Improve performance and quality with this tip" onclick="warnUser(`For improved performance, use Chrome v87 or newer with SIMD support enabled.<br />Enable SIMD here: <a href='chrome://flags/#enable-webassembly-simd' onclick='popupMessage(event);copyFunction(this)' target='_blank'>chrome://flags/#enable-webassembly-simd</a>`);">
<span data-warnSimdNotice="true" style='display:none; font-size: 140%; margin-left:10px; vertical-align: middle; cursor:pointer' title="Improve performance and quality with this tip" onclick="warnUser(`For improved performance, use Chrome v87 or newer with SIMD support enabled.<br />Enable SIMD here: <a href='chrome://flags/#enable-webassembly-simd' onclick='copyFunction(this,event)' target='_blank'>chrome://flags/#enable-webassembly-simd</a>`);">
<i class="las la-info-circle"></i>
</span>
<div id="selectImageTFLITE3" style="display:none;margin-top:10px;">
@ -1517,13 +1532,41 @@
<li class="context-menu__item">
<a href="#" class="context-menu__link" data-action="Open">
<i class="las la-external-link"></i>
<span data-translate="open-in-new-tab">Open in new Tab</span>
<span data-translate="open-in-new-tab">Open in new tab</span>
</a>
</li>
<li class="context-menu__item">
<a href="#" class="context-menu__link" data-action="Copy">
<i class="las la-paperclip"></i>
<span data-translate="copy-to-clipboard" >Copy to Clipboard</span>
<span data-translate="copy-to-clipboard" >Copy to clipboard</span>
</a>
</li>
</ul>
</nav>
<nav id="context-menu-video" class="context-menu">
<ul class="context-menu__items">
<li class="context-menu__item">
<a href="#" class="context-menu__link" data-action="Mirror">
<i class="las la-external-link"></i>
<span data-translate="mirror-video">Mirror</span>
</a>
</li>
<li class="context-menu__item">
<a href="#" class="context-menu__link" data-action="Controls">
<i class="las la-external-link"></i>
<span data-translate="toggle-control-video">Toggle control bar</span>
</a>
</li>
<li class="context-menu__item">
<a href="#" class="context-menu__link" data-action="PiP">
<i class="las la-external-link"></i>
<span data-translate="picture-in-picture">Picture-in-picture</span>
</a>
</li>
<li class="context-menu__item">
<a href="#" class="context-menu__link" data-action="Cast">
<i class="las la-external-link"></i>
<span data-translate="chrome-cast">Cast..</span>
</a>
</li>
</ul>
@ -1549,7 +1592,10 @@
<label title="Increase this at your peril. Changes the total inbound video bitrate per guest; mobile devices excluded. Webp-mode also excluded." for="trbSettingInput">Change room video quality:</label>
<span style="margin-left: 6px;" id="trbSettingInputFeedback"></span>-kbps
<input id="trbSettingInput" type="range" min="0" max="4000" value="500" onchange="changeTRB(this);" oninput="getById('trbSettingInputFeedback').innerHTML = this.value;" style="width:100%;display:block;" />
<span style="margin: 20px 0 0 0;display:block" id='highlightDirectorSpan'>
<label for="highlightDirector">Highlight Director</label>
<input id="highlightDirector" style="width: 15px; height: 15px; margin:10px;" name="highlightDirector" data-value="0" data-action-type="solo-video" type="checkbox" onchange="requestInfocus(this);" />
</span>
</div>
</div>
@ -1697,7 +1743,7 @@
var session = WebRTC.Media; // session is a required global variable if configuring manually. Run before loading main.js but after webrtc.js.
session.version = "18.4b";
session.version = "19.0";
session.streamID = session.generateStreamID(); // randomly generates a streamID for this session. You can set your own programmatically if needed
session.defaultPassword = "someEncryptionKey123"; // Change this password if self-deploying for added security/privacy
@ -1774,11 +1820,11 @@
// session.title // "zzzz"
</script>
<script type="text/javascript" crossorigin="anonymous" src="./thirdparty/aes.js"></script>
<script type="text/javascript" crossorigin="anonymous" id="lib-js" src="./lib.js?ver=14"></script>
<script type="text/javascript" crossorigin="anonymous" id="lib-js" src="./lib.js?ver=58"></script>
<!--
// If you wish to change branding, blank offers a good clean start.
<script type="text/javascript" id="main-js" src="./main.js" data-translation="blank"></script>
-->
<script type="text/javascript" crossorigin="anonymous" id="main-js" src="./main.js?ver=237"></script>
<script type="text/javascript" crossorigin="anonymous" id="main-js" src="./main.js?ver=248"></script>
</body>
</html>

3715
lib.js

File diff suppressed because it is too large Load Diff

View File

@ -11,6 +11,7 @@
--fit-style: contain;
--fadein-speed: 0;
--video-margin: 0px;
--video-rounded: 0px;
}
* {
@ -27,6 +28,15 @@ table {
margin:10px;
}
.hideDropMenu{
user-select: none;
grid-column: 1;
width:100%;
margin:5px 0;
font-size:80%;
cursor: pointer;
}
#bigPlayButton {
margin:0 auto;
background-color: #0000;
@ -214,7 +224,6 @@ button.white:active {
color: white;
border: 1px solid black;
}
#header {
width: 100%;
padding: 1px;
@ -227,8 +236,9 @@ button.white:active {
color: white;
text-align: right;
margin-right: 10px;
pointer-events: none;
cursor: help;
float: right;
font-size: 90%;
}
#head6 {
display: inline-block;
@ -425,9 +435,9 @@ hr {
width: 100%;
height: 100%;
border: 0;
padding: 0;
overflow: hidden;
margin: var(--video-margin);
padding: var(--video-margin);
border-radius: var(--video-rounded);
}
#gridlayout {
@ -626,22 +636,16 @@ button.glyphicon-button.active.focus {
@media only screen and (max-height: 400px){
#obsState {
transform: scale(0.5);
display:none!important;
opacity:0;
}
}
@media only screen and (max-height: 300px){
#obsState {
transform: scale(0.4);
display:none!important;
opacity:0;
}
}
@media only screen and (max-height: 200px){
#obsState {
transform: scale(0.3);
display:none!important;
opacity:0;
}
}
@ -857,11 +861,6 @@ body {
transition: opacity .1s linear;
}
.hidden {
visibility: hidden;
opacity: 0;
}
.previewWebcam {
max-width: 640px;
max-width: 83vw;
@ -943,6 +942,10 @@ body {
background-color:#ddeeff;
}
.green {
background-color:#474!important;
}
/*https://css-tricks.com/styling-cross-browser-compatible-range-inputs-css/*/
input[type=range] {
-webkit-appearance: none;
@ -1219,7 +1222,7 @@ input[type=range]:focus::-ms-fill-upper {
#audioSourceScreenshare {
display:block;
height: 60px;
min-width: 290px;
width: 100%;
overflow: auto;
padding: 5px;
resize: both;
@ -1231,6 +1234,17 @@ p#audioScreenShare1 {
background: #f3f3f3;
padding: 4px 10px 10px 10px;
text-align: left;
min-width:350px;
}
#headphonesDiv2{
background-color: #f3f3f3;
min-width: 350px;
display: none;
padding: 4px 10px 10px 10px;
border: 1px solid #ccc;
vertical-align: middle;
margin: 10px 0;
text-align: left;
}
#audioScreenShare1 > i {
@ -1990,10 +2004,11 @@ audio.fileshare::-webkit-media-controls-play-button, video.fileshare::-webkit-me
}
.hidden {
display:none;
display:none!important;
visibility: hidden;
width:0px;
height:0px;
opacity: 0;
}
/* visited link */
.grabLinks a:visited {
@ -2374,8 +2389,7 @@ input[type=checkbox] {
.directorBlock button i {
margin-right: 5px;
}
.task {
color: #808080;
a.task {
width: 100%;
margin-top: 10px;
}
@ -2385,6 +2399,18 @@ input[type=checkbox] {
margin-left: 5px;
font-size:1.2em;
}
.shift {
display: inline-block;
position: relative;
margin: 7px 0 0 4px;
padding: 0;
width: 27px;
font-size: 0.8em;
top: -7px;
}
.shift>i {
cursor:pointer;
}
#toggleroomnotes {
grid-column: 4;
grid-row: 1;
@ -2436,10 +2462,14 @@ i.las.la-circle {
}
.streamID {
text-align: right;
margin: 5px;
font-size: 0.7em;
text-overflow: ellipsis;
margin: 5px 5px 5px 0px;
font-size: 0.7em;
text-overflow: ellipsis;
overflow: hidden;
/* left: 22px; */
position: relative;
width: 230px;
display: inline-block;
}
.streamID i {
margin-left: 5px;
@ -2471,7 +2501,14 @@ div#guestFeeds {
div#guestFeeds:empty {
display:none;
}
#hiddenElements{
visibility:hidden;
position: absolute;
left:-9999;
top:-9999;
width:0px;
height:0px;
}
#press2talk[data-enabled="true"] {
background: #1e0000;
-webkit-box-shadow: inset 0px 0px 1px #b90000;
@ -2882,9 +2919,9 @@ input:checked + .slider:before {
border: 10px dashed rgb(64 65 62)
}
#SafariWarning{
.startupWarning{
max-width:100%;
display:none;
display:block;
width: 450px;
border-left: 4px solid #eff150;
background: #fffded;
@ -2895,18 +2932,26 @@ input:checked + .slider:before {
box-shadow: 0px 5px 10px -5px #a9a9a9;
text-align: left;
}
#SafariWarning > p {
.startupWarning > p {
text-align: left;
display:inline-block;
padding-left: 38px;
}
#SafariWarning > i {
.startupWarning > i {
position: absolute;
font-size: 2em;
padding: 2px 0 0 0;
}
#consentWarning{
margin: 0 auto 20px auto;
}
#consentWarning2{
margin: 0px auto 10px auto;
}
#alertModal {
position: absolute;
background-color: rgb(221 221 221);

525
main.js
View File

@ -129,6 +129,8 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
window.prompt = function(title, val){
return ipcRenderer.sendSync('prompt', {title, val});
};
ipcRenderer.sendSync('prompt', {title, val});
} catch(e){}
}
@ -156,7 +158,10 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
if (urlParams.has('nomicbutton') || urlParams.has('nmb')) {
getById("mutebutton").style.setProperty("display", "none", "important");
}
if (urlParams.has('nomouseevents') || urlParams.has('nme')) {
session.disableMouseEvents = true;
}
if (urlParams.has('novideobutton') || urlParams.has('nvb')) {
getById("mutevideobutton").style.setProperty("display", "none", "important");
@ -205,7 +210,7 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
log("MAKE DRAGGABLE");
delayedStartupFuncs.push([makeDraggableElement, document.getElementById("subControlButtons")]);
if (safariVersion() && !getChromeVersion()){ // if desktop Safari, so macOS, give a note saying it sucks
getById("SafariWarning").style.display = "block";
getById("SafariWarning").classList.remove("advanced");
}
}
@ -222,7 +227,7 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
session.style = 1;
//getById("header").style.display = "none";
//getById("header").style.opacity = 0;
session.showList=false;
session.showList=true;
}
if (urlParams.has('showlist')) {
@ -310,6 +315,14 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
session.midiHotkeys = urlParams.get('midi') || urlParams.get ('hotkeys') || 1;
session.midiHotkeys = parseInt(session.midiHotkeys);
}
if (urlParams.has('midiremote') || urlParams.has('remotemidi')){
if (session.director!==false){
session.midiRemote = urlParams.get('midiremote') || urlParams.get ('remotemidi') || 4;
} else {
session.midiRemote = urlParams.get('midiremote') || urlParams.get ('remotemidi') || 1;
}
}
if (urlParams.has('midipush') || urlParams.has('midiout') || urlParams.has('mo')){
session.midiOut = urlParams.get('midipush') || urlParams.get('midiout') || urlParams.get('mo') || true;
@ -341,7 +354,6 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
getById("container-6").classList.add("skip-animation");
getById("container-6").classList.remove('pointer');
session.website = urlParams.get('website') || urlParams.get('iframe') || false;
if (session.website){
if (session.director){
delayedStartupFuncs.push([shareWebsite, session.website]);
@ -349,6 +361,23 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
delayedStartupFuncs.push([session.publishIFrame, session.website]);
}
}
} else if (urlParams.has('webcam2') || urlParams.has('wc2')) {
session.webcamonly = true;
screensharebutton = false;
session.introButton = true;
} else if (urlParams.has('screenshare2') || urlParams.has('ss2')) {
session.screenshare = true;
session.introButton = true;
if (urlParams.get('screenshare2') || urlParams.get('ss2')){
session.screenshare = urlParams.get('screenshare2') || urlParams.get('ss2');
}
}
if (urlParams.has('intro') || urlParams.has('ib')) {
session.introButton = true;
}
if (urlParams.has('hidesolo') || urlParams.has('hs')){
session.hidesololinks=true;
}
if (urlParams.has('ssb')) {
@ -358,6 +387,10 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
if (urlParams.has('mute') || urlParams.has('muted') || urlParams.has('m')) {
session.muted = true;
}
if (urlParams.has('safemode')) {
session.safemode = true;
}
if (urlParams.has('videomute') || urlParams.has('videomuted') || urlParams.has('vm')) {
session.videoMutedFlag = true;
@ -410,9 +443,14 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
}
if (session.screenshare !== false) {
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 (session.introButton){
getById("container-3").className = 'column columnfade advanced'; // Hide screen share
getById("head1").className = 'advanced';
} else {
getById("container-3").className = 'column columnfade advanced'; // Hide webcam
getById("container-2").classList.add("skip-animation");
getById("container-2").classList.remove('pointer');
}
}
if (urlParams.has('manual')) {
@ -470,7 +508,6 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
session.scene = session.scene.replace(/[\W]+/g, "_");
} else {
session.scene = (parseInt(session.scene) || 0) + "";
}
session.disableWebAudio = true;
session.audioEffects = false;
@ -479,6 +516,10 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
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('preloadbitrate')) {
session.hiddenSceneViewBitrate = parseInt(urlParams.get('preloadbitrate')) || 500;
}
if (urlParams.has('scenetype') || urlParams.has('type')) {
session.sceneType = parseInt(urlParams.get('scenetype')) || parseInt(urlParams.get('type')) || false;
@ -499,10 +540,15 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
}
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');
delayedStartupFuncs.push([previewWebcam]);
if (session.introButton){
getById("container-2").className = 'column columnfade advanced'; // Hide screen share
getById("head3").className = 'advanced';
} else {
getById("container-2").className = 'column columnfade advanced'; // Hide screen share
getById("container-3").classList.add("skip-animation");
getById("container-3").classList.remove('pointer');
delayedStartupFuncs.push([previewWebcam]);
}
}
if (urlParams.has('css')){
@ -588,7 +634,7 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
session.taintedSession = false;
session.hash = hash;
}
});
}).catch(errorlog);
}
if (session.defaultPassword !== false) {
@ -697,6 +743,21 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
}
if (urlParams.has('animated') || urlParams.has('animate')){
session.animatedMoves = urlParams.get('animated') || urlParams.get('animate');
if (session.animatedMoves === "false") {
session.animatedMoves = false;
} else if (session.animatedMoves === "0") {
session.animatedMoves = false;
} else if (session.animatedMoves === "no") {
session.animatedMoves = false;
} else if (session.animatedMoves === "off") {
session.animatedMoves = false;
} else {
session.animatedMoves=true;
}
} else if (session.mobile){
session.animatedMoves=false;
} else {
session.animatedMoves=true;
}
@ -704,7 +765,7 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
if (urlParams.has('pie')){
session.customWSS = urlParams.get('pie') || false; // If session.customWSS == true, then there is no need to set parameters via URL
if (session.customWSS){
session.wss = "wss://us-nyc-1.websocket.me/v3/1?api_key="+session.customWSS; // if URL param is set, it will use the API key.
session.wss = "wss://free3.piesocket.com/v3/1?api_key="+session.customWSS; // if URL param is set, it will use the API key.
}
}
@ -877,6 +938,10 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
// session.view = true;
// }
//}
if (urlParams.has('ruler') || urlParams.has('grid') || urlParams.has('thirds')) {
session.ruleOfThirds=true;
}
if (urlParams.has('nopreview') || urlParams.has('np')) {
log("preview OFF");
@ -933,13 +998,14 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
}
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('tips')){
getById("guestTips").style.display="flex";
@ -950,7 +1016,7 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
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;
@ -1067,14 +1133,22 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
}
if (urlParams.has('margin')) {
if (urlParams.get('margin') || 10){
try {
var videoMargin = urlParams.get('margin') || 10;
videoMargin = parseInt(videoMargin);
videoMargin+="px";
document.querySelector(':root').style.setProperty('--video-margin', videoMargin);
} catch(e){errorlog("variable css failed");}
}
try {
var videoMargin = urlParams.get('margin') || 10;
videoMargin = parseInt(videoMargin);
videoMargin+="px";
document.querySelector(':root').style.setProperty('--video-margin', videoMargin);
} catch(e){errorlog("variable css failed");}
}
if (urlParams.has('rounded')) {
try {
var videoRounded = urlParams.get('rounded') || 50;
videoRounded = parseInt(videoRounded);
videoRounded+="px";
document.querySelector(':root').style.setProperty('--video-rounded', videoRounded);
} catch(e){errorlog("variable css failed");}
}
if (urlParams.has('fadein')) {
@ -1205,14 +1279,24 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
getById("flipcamerabutton").classList.remove("advanced");
}
}
if (urlParams.has('consent')){
session.consent = true;
getById("consentWarning").classList.remove("advanced");
getById("consentWarning2").classList.remove("advanced");
}
if (urlParams.has('autojoin') || urlParams.has('autostart') || urlParams.has('aj') || urlParams.has('as')) {
session.autostart = true;
if (session.screenshare!==false) {
delayedStartupFuncs.push([publishScreen]);
}
}
if (session.consent){
setTimeout(function(){
warnUser("⚠ Privacy warning: The director of this room can remotely switch your camera or microphone without permission.", 8000);
}, 1500);
}
}
if (urlParams.has('noiframe') || urlParams.has('noiframes') || urlParams.has('nif') || urlParams.has('nowebsite') ) {
@ -1240,8 +1324,7 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
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');
@ -1372,11 +1455,21 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
getById("helpbutton").style.opacity = 0;
getById("reportbutton").style.display = "none";
getById("reportbutton").style.opacity = 0;
getById("calendarButton").style.display = "none";
getById("calendarButton").style.opacity = 0;
getById("chatBody").innerHTML = "";
}
if (urlParams.has('beep') || urlParams.has('notify') || urlParams.has('tone')) {
session.beepToNotify = true;
var addtone = createAudioElement();
addtone.id = "jointone";
addtone.src = "./media/join.mp3";
getById("testtone").parentNode.insertBefore(addtone, getById("testtone").nextSibling)
var addtone = createAudioElement();
addtone.id = "leavetone";
addtone.src = "./media/leave.mp3";
getById("testtone").parentNode.insertBefore(addtone, getById("testtone").nextSibling)
}
if (urlParams.has('r2d2')) {
getById("testtone").innerHTML = "";
@ -1512,6 +1605,12 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
getById("credits").style.display = "none";
getById("header").style.display = "none";
getById("controlButtons").style.display = "none";
getById("helpbutton").style.display = "none";
getById("helpbutton").style.opacity = 0;
getById("reportbutton").style.display = "none";
getById("reportbutton").style.opacity = 0;
getById("calendarButton").style.display = "none";
getById("calendarButton").style.opacity = 0;
var styleTmp = document.createElement('style');
styleTmp.innerHTML = `
video {
@ -1522,6 +1621,21 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
}
getById("credits").innerHTML = "Version: " + session.version + " - " + getById("credits").innerHTML;
if (urlParams.has('minidirector')) {
try {
var cssStylesheet = document.createElement('link');
cssStylesheet.rel = 'stylesheet';
cssStylesheet.type = 'text/css';
cssStylesheet.media = 'screen';
cssStylesheet.href = 'minidirector.css';
document.getElementsByTagName('head')[0].appendChild(cssStylesheet);
} catch (e) {
errorlog(e);
}
}
if (urlParams.has('cleanish')) {
session.cleanish = true;
}
@ -1605,19 +1719,35 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
}
if (urlParams.has('buffer')) { // needs to be before sync
session.buffer = parseFloat(urlParams.get('buffer')) || 0;
log("buffer Changed: " + session.buffer);
session.sync = 0;
if ((getChromeVersion() > 50) && (getChromeVersion()< 78)){
} else {
session.buffer = parseFloat(urlParams.get('buffer')) || 0;
log("buffer Changed: " + session.buffer);
session.sync = 0;
session.audioEffects = true;
}
}
if (urlParams.has('panning') || urlParams.has('pan')) {
session.panning=true;
if (urlParams.get('panning') || urlParams.get('pan')){
session.panning = urlParams.get('panning') || urlParams.get('pan');
}
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 ((getChromeVersion() > 50) && (getChromeVersion()< 78)){
} else {
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;
}
}
}
@ -1722,7 +1852,7 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
for (let i = 0; i < elementsTmp.length; i++) {
elementsTmp[i].style.display = "inline-block";
}
}
}
if (urlParams.has('viewereffect') || urlParams.has('viewereffects') || urlParams.has('ve')) {
@ -1734,11 +1864,18 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
session.audioEffects = true;
session.audioMeterGuest = true;
setInterval(function(){activeSpeaker(false);},100);
} else if (urlParams.has('noisegate')){
session.quietOthers = true;
session.audioEffects = true;
session.audioMeterGuest = true;
setInterval(function(){activeSpeaker(false);},100);
}
if (urlParams.has('meter') || urlParams.has('meterstyle')){
session.meterStyle = urlParams.get('meter') || urlParams.get('meterstyle') || 1;
}
if (urlParams.has('directorchat') || urlParams.has('dc')){
session.directorChat = true;
@ -1950,6 +2087,13 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
session.wss = "wss://" + urlParams.get('wss');
}
}
if (urlParams.has('osc') || urlParams.has('api')) {
if (urlParams.get('osc') || urlParams.get('api')) {
session.api = urlParams.get('osc') || urlParams.get('api');
setTimeout(function(){oscClient();},1000);
}
}
if (urlParams.has('queue')) {
session.queue = true;
@ -2012,6 +2156,15 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
}
}
if (urlParams.has('hidescreenshare') || urlParams.has('hidess') || urlParams.has('sshide') || urlParams.has('screensharehide')) { // this way I don't need to remember what it's called. I can just guess. :D
session.screenShareElementHidden = true;
}
if (urlParams.has('zoomedbitrate') || urlParams.has('zb')) { // this way I don't need to remember what it's called. I can just guess. :D
session.zoomedBitrate = urlParams.get('zoomedbitrate') || urlParams.get('zb') || 2500;
session.zoomedBitrate = parseInt(session.zoomedBitrate) ;
}
if (urlParams.has('screenshareid') || urlParams.has('ssid')) {
if (urlParams.get('screenshareid') || urlParams.get('ssid')) {
session.screenshareid = urlParams.get('screenshareid') || urlParams.get('ssid');
@ -2130,10 +2283,8 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
getById("mainmenu").style.display = "none";
getById("translateButton").style.display = "none";
log("Update Mixer Event on REsize SET");
window.addEventListener("resize", updateMixer);
window.addEventListener("orientationchange", function() {
setTimeout(updateMixer, 200);
});
window.onresize = updateMixer;
window.onorientationchange = function(){setTimeout(updateMixer, 200);};
joinRoom(session.roomid); // this is a scene, so we want high resolutions
getById("main").style.overflow = "hidden";
@ -2168,7 +2319,7 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
log("dir room hash is " + hash);
session.directorHash = hash;
return;
});
}).catch(errorlog);
} else {
session.directorPassword = false;
}
@ -2191,10 +2342,8 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
}
log("Update Mixer Event on REsize SET");
getById("translateButton").style.display = "none";
window.addEventListener("resize", updateMixer);
window.addEventListener("orientationchange", function() {
setTimeout(updateMixer, 200);
});
window.onresize = updateMixer;
window.onorientationchange = function(){setTimeout(updateMixer, 200);};
getById("main").style.overflow = "hidden";
if (session.chatbutton === true) {
@ -2340,7 +2489,6 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
closeModal();
async function loadModel(){
model = await faceLandmarksDetection.load(faceLandmarksDetection.SupportedPackages.mediapipeFacemesh);
}
loadModel();
}
@ -2352,7 +2500,31 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
script.type = 'text/javascript';script2.type = 'text/javascript';script3.type = 'text/javascript';script4.type = 'text/javascript';
document.head.appendChild(script);
}
} else if (session.effects==9){
var script = document.createElement('script');
script.onload = function() {
effectsEngine();
}
script.src = "./filters/sample.js";
document.head.appendChild(script);
warnUser("Loading custom effects model...",1000);
} else if (session.effects==10){
var script = document.createElement('script');
script.onload = function() {
effectsEngine();
}
script.src = "./filters/cube.js";
document.head.appendChild(script);
warnUser("Loading custom effects model...",1000);
} else if (session.effects==11){
var script = document.createElement('script');
script.onload = function() {
effectsEngine();
}
script.src = "./filters/anon.js";
document.head.appendChild(script);
warnUser("Loading custom effects model...",1000);
}
if (location.protocol !== 'https:') {
if (!(session.cleanOutput)) {
@ -2370,7 +2542,7 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
errorlog(e);
}
if (isIFrame) { // reduce CPU load if not needed.
if (isIFrame) { // reduce CPU load if not needed. //iframe API
window.onmessage = function(e) { // iFRAME support
log(e);
try {
@ -2390,9 +2562,27 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
} catch (err) {
errorlog(err);
}
if ("sendData" in e.data) { // send generic data via p2p. Send whatever you want I guess; there is a max chunk size of course. Use filetransfer for large files?
var UUID = false;
var streamID = false;
var type = false;
if (e.data.UUID){
UUID = e.data.UUID;
} else if (e.data.streamID){
streamID = e.data.streamID;
}
if (e.data.type){
type = e.data.type;
}
var ret = session.sendGenericData(e.data.sendData, UUID, streamID, type); // comes out the other side as: ("dataReceived", data, UUID);
if (!ret){warnlog("Not connected yet or no peers available");}
return;
}
if ("sendChat" in e.data) {
sendChat(e.data.sendChat); // sends to all peers; more options down the road
return;
}
// Chat out gets called via getChatMessage function
// Related code: parent.postMessage({"chat": {"msg":-----,"type":----,"time":---} }, "*");
@ -2475,6 +2665,25 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
}
}
}
if ("panning" in e.data){
if ("UUID" in e.data){
try {
adjustPan(UUID, e.data.panning);
} catch (e) {
errorlog(e);
}
} else {
for (var i in session.rpcs) {
try {
adjustPan(i, e.data.panning);
} catch (e) {
errorlog(e);
}
}
}
}
if ("bitrate" in e.data) {
for (var i in session.rpcs) {
@ -2506,9 +2715,21 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
changeAudioDevice(e.data.changeAudioDevice);
}
if ("changeAudioOutputDevice" in e.data) {
warnlog(e.data.changeAudioOutputDevice);
changeAudioOutputDeviceById(e.data.changeAudioOutputDevice);
}
if ("getDeviceList" in e.data) {
warnlog(e.data.getDeviceList);
enumerateDevices().then(function(deviceInfos) {
parent.postMessage({
"deviceList": deviceInfos
}, "*");
});
}
if ("sceneState" in e.data) { // TRUE OR FALSE - tells the connected peers if they are live or not via a tally light change.
if (session.obsState.visibility !== e.data.sceneState) { // only move forward if there is a change; the event likes to double fire you see.
session.obsStateSync();
}
@ -2548,6 +2769,7 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
if (stat.kind == "video") {
if ("qualityLimitationReason" in stat) {
session.pcs[UUID].stats.quality_limitation_reason = stat.qualityLimitationReason;
}
if ("framesPerSecond" in stat) {
@ -2628,6 +2850,21 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
session.pushLoudness = false;
}
}
if ("getEffectsData" in e.data) {
log("GOT getEffects Data REQUESTed"); // face tracking info, etc.
if (e.data.getEffectsData !== false) {
session.pushEffectsData = e.data.getEffectsData; // which effect do you want the data from? it won't enable the effect necessarily; just the ML pipeline
//parent.postMessage({
// "effectsData": loudness,
// "effectsID": session.pushEffectsData
//}, "*");
} else {
session.pushEffectsData = false;
}
}
if ("getStreamIDs" in e.data) {
if (e.data.getStreamIDs == true) {
@ -2775,76 +3012,9 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
var input = WebMidi.inputs[i];
input.addListener('noteon', "all", function(e) {
log(e);
if (session.midiHotkeys==1){
log(e);
var note = e.note.name + e.note.octave;
if (note == "G3") { // open and close the chat window
toggleChat();
} else if (note == "A3") { // mute your audio output
toggleMute();
} else if (note == "B3") { // mute your video output
toggleVideoMute();
} else if (note == "C4") { // enable / disable screenshare
toggleScreenShare();
} else if (note == "D4") { // completely kill your connection/session
hangup();
} else if (note == "E4") { // raise your hand; director sees this
raisehand();
} else if (note == "F4") { // start/stop local recording
recordLocalVideoToggle();
} else if (note == "G4") { // Director Enables their Audio output
press2talk(true);
} else if (note == "A4") { // Director cut's their audio/video output
hangup2();
}
} else if (session.midiHotkeys==2){
log(e);
var note = e.note.name + e.note.octave;
if (note == "G1") { // open and close the chat window
toggleChat();
} else if (note == "A1") { // mute your audio output
toggleMute();
} else if (note == "B1") { // mute your video output
toggleVideoMute();
} else if (note == "C2") { // enable / disable screenshare
toggleScreenShare();
} else if (note == "D2") { // completely kill your connection/session
hangup();
} else if (note == "E2") { // raise your hand; director sees this
raisehand();
} else if (note == "F2") { // start/stop local recording
recordLocalVideoToggle();
} else if (note == "G2") { // Director Enables their Audio output
press2talk(true);
} else if (note == "A2") { // Director cut's their audio/video output
hangup2();
}
} else if (session.midiHotkeys==3){
log(e);
var note = e.note.name + e.note.octave;
var velocity = e.velocity;
if (note == "C1"){
if (velocity == "0") { // open and close the chat window
toggleChat();
} else if (note == "1") { // mute your audio output
toggleMute();
} else if (note == "2") { // mute your video output
toggleVideoMute();
} else if (note == "3") { // enable / disable screenshare
toggleScreenShare();
} else if (note == "4") { // completely kill your connection/session
hangup();
} else if (note == "5") { // raise your hand; director sees this
raisehand();
} else if (note == "6") { // start/stop local recording
recordLocalVideoToggle();
} else if (note == "7") { // Director Enables their Audio output
press2talk(true);
} else if (note == "8") { // Director cut's their audio/video output
hangup2();
}
}
}
var note = e.note.name + e.note.octave;
var velocity = e.velocity || false;
midiHotkeysNote(node,velocity);
});
input.addListener('controlchange', "all", function(e) {
@ -2865,76 +3035,7 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
var command = e.controller.number;
var value = e.value;
if (command == 110){
if (value == 0) { // open and close the chat window
toggleChat();
} else if (value == 1) { // mute your audio output
toggleMute();
} else if (value == 2) { // mute your video output
toggleVideoMute();
} else if (value == 3) { // enable / disable screenshare
toggleScreenShare();
} else if (value == 4) { // completely kill your connection/session
hangup();
} else if (value == 5) { // raise your hand; director sees this
raisehand();
} else if (value == 6) { // start/stop local recording
recordLocalVideoToggle();
} else if (value == 7) { // Director Enables their Audio output
press2talk(true);
} else if (value == 8) { // Director cut's their audio/video output
hangup2();
}
} else if (command > 110){
var guestslot = command-111;
if (value == 0) {
var elements = document.querySelectorAll('[data-action-type="forward"][data--u-u-i-d]');
if (elements[guestslot]) {
directMigrate(elements[guestslot], true);
}
} else if (value == 1) {
var elements = document.querySelectorAll('[data-action-type="addToScene"][data--u-u-i-d]');
if (elements[guestslot]) {
directEnable(elements[guestslot], true);
}
} else if (value == 2) {
var elements = document.querySelectorAll('[data-action-type="mute-scene"][data--u-u-i-d]');
if (elements[guestslot]) {
directMute(elements[guestslot], true);
}
} else if (value == 3) {
var elements = document.querySelectorAll('[data-action-type="mute-guest"][data--u-u-i-d]');
if (elements[guestslot]) {
remoteMute(elements[guestslot], true);
}
} else if (value == 4) {
var elements = document.querySelectorAll('[data-action-type="hangup"][data--u-u-i-d]');
if (elements[guestslot]) {
directHangup(elements[guestslot], true);
}
} else if (value == 5) {
var elements = document.querySelectorAll('[data-action-type="solo-chat"][data--u-u-i-d]');
if (elements[guestslot]) {
session.toggleSoloChat(elements[guestslot].dataset.UUID);
}
} else if (value == 6) {
var elements = document.querySelectorAll('[data-action-type="toggle-remote-speaker"][data--u-u-i-d]');
if (elements[guestslot]) {
remoteSpeakerMute(elements[guestslot]);
}
} else if (value == 7) {
var elements = document.querySelectorAll('[data-action-type="toggle-remote-display"][data--u-u-i-d]');
if (elements[guestslot]) {
remoteDisplayMute(elements[guestslot]);
}
} else if ((value => 27)) {
var elements = document.querySelectorAll('[data-action-type="volume"][data--u-u-i-d]');
if (elements[guestslot]) {
elements[guestslot].value = parseInt(value-27);
remoteVolume(elements[guestslot]);
}
}
}
midiHotkeys(command, value)
}
});
}
@ -2958,8 +3059,6 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
}
var languages = getById('languagesList').querySelectorAll('li a');
var timezones = [];
@ -3022,9 +3121,11 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
}
try {
var Connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
session.stats.network_type = Connection.effectiveType + " / " + Connection.type;
Connection.addEventListener('change', updateConnectionStatus);
} catch (e) {warnlog(e);}
if (Connection){
session.stats.network_type = Connection.effectiveType + " / " + Connection.type;
Connection.addEventListener('change', updateConnectionStatus);
}
} catch (e) {log(e);} // effectiveType is not yet supported by Firefox or Safari; 2021
setInterval(function() {
@ -3095,6 +3196,16 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
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) {
if (!session.noExitPrompt && !session.cleanOutput && (session.permaid!==false || session.director)){
(e || window.event).returnValue = "Are you sure you want to exit?"; //Gecko + IE
return "Are you sure you want to exit?";
} else {
//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.
}
});
window.addEventListener("unload", function(e) {
try {
session.ws.close();
if (session.videoElement.recording) {
@ -3109,18 +3220,11 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
}
}
}
session.hangup();
} catch (e) {}
if (!session.noExitPrompt && !session.cleanOutput && (session.permaid!==false || session.director)){
(e || window.event).returnValue = "Are you sure you want to exit?"; //Gecko + IE
return "Are you sure you want to exit?";
} else {
//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.
}
});
};
var lastTouchEnd = 0;
document.addEventListener('touchend', function(event) {
var now = (new Date()).getTime();
@ -3169,8 +3273,6 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
toggleVideoMute();
}
}
});
document.addEventListener("keyup", event => {
@ -3188,6 +3290,11 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
if (!(event.altKey)) {
AltPressed = false;
}
if (event.altKey && event.shiftKey && event.keyCode === 67 /* C */) {
toggleControlBar();
}
});
}

BIN
media/join.mp3 Normal file

Binary file not shown.

BIN
media/join.ogg Normal file

Binary file not shown.

BIN
media/join.wav Normal file

Binary file not shown.

BIN
media/leave.mp3 Normal file

Binary file not shown.

BIN
media/leave.ogg Normal file

Binary file not shown.

BIN
media/leave.wav Normal file

Binary file not shown.

11
media/thirds.svg Normal file
View File

@ -0,0 +1,11 @@
<svg width="800" height="600" xmlns="http://www.w3.org/2000/svg">
<g>
<title>Layer 1</title>
<line stroke-width="2" stroke-dasharray="5,5" stroke="#000" stroke-linecap="undefined" stroke-linejoin="undefined" id="svg_1" y2="200.2" x2="798.00001" y1="200.2" x1="2.99999" fill="none"/>
<line stroke="#000" stroke-linecap="undefined" stroke-linejoin="undefined" id="svg_3" y2="616.2" x2="266" y1="2.2" x1="266" stroke-dasharray="5,5" stroke-width="2" fill="none"/>
<line stroke-width="2" stroke-dasharray="5,5" stroke="#000" stroke-linecap="undefined" stroke-linejoin="undefined" id="svg_4" y2="200.2" x2="798.00001" y1="200.2" x1="2.99999" fill="none"/>
<line stroke="#000" stroke-linecap="undefined" stroke-linejoin="undefined" id="svg_5" y2="616.2" x2="534" y1="2.2" x1="534" stroke-dasharray="5,5" stroke-width="2" fill="none"/>
<ellipse ry="132" rx="135" id="svg_6" cy="200.43469" cx="400" stroke-dasharray="5,5" stroke-width="2" stroke="#000" fill="none"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 985 B

View File

@ -360,11 +360,29 @@
updateData("retransmit", 0, UUID);
}
if (e.data.remoteStats[UUID].info.label){
if (!document.getElementById("label_"+UUID)){
document.getElementById(UUID).innerHTML = "<h1 class='small' id='label_"+UUID+"'></h1>" + document.getElementById(UUID).innerHTML;
var streamID = false;
if ("streamID" in e.data) {
streamID = e.data.streamID;
}
if (document.getElementById(UUID)){
if ("label" in e.data.remoteStats[UUID].info){
if (e.data.remoteStats[UUID].info.label){
if (!document.getElementById("label_"+UUID)){
document.getElementById(UUID).innerHTML = "<h1 class='small' id='label_"+UUID+"'></h1>" + document.getElementById(UUID).innerHTML;
}
document.getElementById("label_"+UUID).innerText = e.data.remoteStats[UUID].info.label + ", - a remote viewer of stream ID: "+streamID;
} else if (streamID){
if (!document.getElementById("label_"+UUID)){
document.getElementById(UUID).innerHTML = "<h1 class='small' id='label_"+UUID+"'></h1>" + document.getElementById(UUID).innerHTML;
}
document.getElementById("label_"+UUID).innerText = "Stats for a remote viewer of stream ID: "+streamID;
} else {
if (!document.getElementById("label_"+UUID)){
document.getElementById(UUID).innerHTML = "<h1 class='small' id='label_"+UUID+"'></h1>" + document.getElementById(UUID).innerHTML;
}
document.getElementById("label_"+UUID).innerText = "";
}
}
document.getElementById("label_"+UUID).innerText = e.data.remoteStats[UUID].info.label;
}

View File

@ -65,11 +65,14 @@
Testing location: <select name="turnlist" id="turnlist" onchange="reloadTurn();" title="Select an exact location to test against">
<option selected value="">Automatic</option>
<option value="de1">Saarbruecken, Germany</option>
<option value="de2">Frankfurt, Germany</option>
<option value="fr1">Strasbourg, France</option>
<option value="bra1">São Paulo, Brazil</option>
<option value="cae1">Montreal, Canada</option>
<option value="usc1">Chicago, USA</option>
<option value="usw1">Los Angeles, USA</option>
<option value="aus1">Sidney, Australia</option>
<option value="usw1">Los Angeles 1, USA</option>
<option value="usw2">Los Angeles 2, USA</option>
<option value="aus1">Sydney, Australia</option>
<option value="jap1">Tokyo, Japan</option>
<option value="sing1">Singapore</option>
</select>

File diff suppressed because one or more lines are too long