minor fixes and a couple new URL options

This commit is contained in:
Steve Seguin 2022-08-22 21:48:18 -04:00
parent d30e1a1f35
commit e5f07b0b18
10 changed files with 1270 additions and 634 deletions

View File

@ -64,7 +64,7 @@
var button = document.createElement("a");
button.innerHTML = "Invite "+listOfStreamIDs[i];
button.target = "_blank";
button.href = "../?room=teststeve123&password=1234&broadcast&transparent&autostart&nmb&nvb&gain=0&webcam&l=stevetest&push="+listOfStreamIDs[i]+"_pov";
button.href = "../?room=teststeve123&password=1234&broadcast&transparent&autostart&nmb&nvb&gain=0&webcam&l=stevetest&push="+listOfStreamIDs[i];
iframeContainer.appendChild(button);
var button = document.createElement("button");
@ -74,12 +74,12 @@
iframe.contentWindow.postMessage({
action: "addScene",
value: "1",
target: listOfStreamIDs[i],
target: this.dataset.sid
}, '*');
iframe.contentWindow.postMessage({
action: "mic",
value: true,
target: listOfStreamIDs[i],
target: this.dataset.sid
}, '*');
}; // target can be a stream ID or * for all.
@ -126,7 +126,19 @@
<div id="container">
<button onclick="loadIframe();">Go to Directors Room</button>
<br />
The password for guests is 1234<br />
<br />
<br />
Custom guest invites and toggles for add/removing from scene=1 are on the bottom.
<br />
<br />
Scene=1 link: <a target="_blank" href="https://vdo.ninja/?scene=1&room=teststeve123&password=1234">https://vdo.ninja/?scene=1&room=teststeve123&password=1234</a>
</div>
</body>

View File

@ -88,6 +88,22 @@
page;
Picture-in-Picture style</div>
</div>
<div>
<h2><a href='multi.html?rooms=room1xx,room2xx,room3xx'>Multiple rooms</a></h2>
<div class="description">how to have multiple director rooms open in a single tab; note the URL's ?rooms=xx,yy command</div>
</div>
<div>
<h2><a href='https://versus.cam'>versus.cam</a></h2>
<div class="description">How to use the IFRAME API to transport audio and video to the parent frame in Chrome</div>
</div>
<div>
<h2><a href='addtoscene.html'>add to scene</a></h2>
<div class="description">How to use the IFRAME API to add/remove guests to a scene remotely</div>
</div>
<div>
<h2><a href='bigmutebutton.html'>big mute button</a></h2>
<div class="description">Mobile-friendly big-button for muting yourself easily</div>
</div>
<div>
<h2><a href='sensors.html'>sensors</a></h2>
<div class="media">
@ -99,6 +115,12 @@
it to canvas.
</div>
</div>
<div>
<h2><a href='sensoroverlay.html'>sensor overlay</a></h2>
<div class="description">Overlay the incoming speed from remote mobile sensor data onto your video
</div>
</div>
<div>
<h2><a href='../midi.html'>midi</a></h2>
<div class="media">

View File

@ -57,7 +57,7 @@
<meta property="twitter:image" content="./media/vdoNinja_logo_full.png" />
<meta name="msapplication-TileColor" content="#da532c" />
<meta name="theme-color" content="#ffffff" />
<link rel="stylesheet" href="./main.css?ver=174" />
<link rel="stylesheet" href="./main.css?ver=176" />
<script type="text/javascript" crossorigin="anonymous" src="./thirdparty/adapter.min.js"></script>
<style id="lightbox-animations" type="text/css"></style>
<!-- <link rel="manifest" href="manifest.json" /> -->
@ -82,7 +82,7 @@
<link itemprop="url" href="./media/vdoNinja_logo_full.png" />
</span>
<script type="text/javascript" crossorigin="anonymous" src="./thirdparty/CodecsHandler.js?ver=37"></script>
<script type="text/javascript" crossorigin="anonymous" src="./webrtc.js?ver=495"></script>
<script type="text/javascript" crossorigin="anonymous" src="./webrtc.js?ver=503"></script>
<input id="zoomSlider" type="range" style="display: none;" />
<span id="electronDragZone" style="pointer-events: none; z-index:-10; position:absolute;top:0;left:0;width:100%;height:2%;-webkit-app-region: drag;min-height:20px;"></span>
<div id="header">
@ -159,13 +159,13 @@
<div id="mutevideobutton" onmousedown="event.preventDefault(); event.stopPropagation();" title="Disable the Camera" alt="Disable the Camera" onclick="toggleVideoMute()" tabindex="19" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="hidden float" style="cursor: pointer;">
<i id="mutevideotoggle" onmousedown="event.preventDefault(); event.stopPropagation();" class="toggleSize las la-video my-float"></i>
</div>
<div id="screensharebutton" onmousedown="event.preventDefault(); event.stopPropagation();" title="Share a Screen with others" alt="Share a Screen with others" onclick="screenshareTypeDecider(1)" tabindex="20" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="float hidden" style="cursor: pointer;">
<div id="screensharebutton" onmousedown="event.preventDefault(); event.stopPropagation();" title="Share a Screen with others" alt="Share a Screen with others" onclick="screenshareTypeDecider(1)" tabindex="20" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="float hidden task" data-menu="context-menu-screen-share" style="cursor: pointer;">
<i id="screensharetoggle" onmousedown="event.preventDefault(); event.stopPropagation();" class="toggleSize las la-desktop my-float"></i>
</div>
<div id="screenshare2button" onmousedown="event.preventDefault(); event.stopPropagation();" title="Create a Secondary Stream" alt="Create a Secondary Stream" onclick="screenshareTypeDecider(2)" tabindex="20" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="float hidden" style="cursor: pointer;">
<div id="screenshare2button" onmousedown="event.preventDefault(); event.stopPropagation();" title="Create a Secondary Stream" alt="Create a Secondary Stream" onclick="screenshareTypeDecider(2)" tabindex="20" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="float hidden task" data-menu="context-menu-screen-share" style="cursor: pointer;">
<i id="screenshare2toggle" onmousedown="event.preventDefault(); event.stopPropagation();" class="toggleSize las la-tv my-float"></i>
</div>
<div id="screenshare3button" onmousedown="event.preventDefault(); event.stopPropagation();" title="Create a Third Stream" alt="Create a Third Stream" onclick="screenshareTypeDecider(3)" tabindex="20" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="float hidden" style="cursor: pointer;">
<div id="screenshare3button" onmousedown="event.preventDefault(); event.stopPropagation();" title="Create a Third Stream" alt="Create a Third Stream" onclick="screenshareTypeDecider(3)" tabindex="20" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="float hidden task" data-menu="context-menu-screen-share" style="cursor: pointer;">
<i id="screenshare3toggle" 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 with your guests (IFRAME)" alt="Share a website with your guests (IFRAME)" onclick="shareWebsite(false, event)" tabindex="21" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="float hidden" style="cursor: pointer;">
@ -962,7 +962,7 @@
<label class="switch" title="Disables Echo Cancellation and improves audio quality">
<input type="checkbox" data-param="&s" onchange="updateLink(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" style="width: 16em;">This can cause guests to be too quiet or cause feedback issues</span></font> <span data-translate="pro-audio-mode">Pro-audio mode</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" style="width: 16em;">This can cause guests to be too quiet or have feedback/echo issues</span></font> <span data-translate="pro-audio-mode">Pro-audio mode</span>
<Br />
<label class="switch" title="Audio-only sources are visually hidden from scenes">
<input type="checkbox" data-param="&st" onchange="updateLink(1,this);">
@ -1015,7 +1015,7 @@
<input type="checkbox" data-param="&q" onchange="updateLink(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">This can cause video playback to lag</span></font> 1080p60 Video if Available
<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">This can cause video playback to lag</span></font><span data-translate="1080p60-if-available">1080p60 Video if Available</span>
<Br />
<label class="switch" title="The default microphone will be pre-selected for the guest">
<input type="checkbox" data-param="&ad" onchange="updateLink(1,this);">
@ -1175,29 +1175,27 @@
<div style="display:inline-block;margin-top: 12px; position: relative;">
<label class="switch">
<input type="checkbox" data-param="&st" onchange="updateLink(3,this);">
<input type="checkbox" data-param="&st" onchange="updateLink(3,this, true);">
<span class="slider"></span>
</label>
<span data-translate="hide-audio-only-sources">Hide audio-only sources</span>
<br />
<label class="switch">
<input type="checkbox" data-param="&s" onchange="updateLink(3,this);">
<input type="checkbox" data-param="&s" onchange="updateLink(3,this, true);">
<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" style="width: 10em;">This can cause audio clicking issues</span></font>
<span data-translate="pro-audio-mode">Pro-audio mode</span>
<br />
<label class="switch">
<input type="checkbox" id="codech264toggle" data-param="&codec=h264" onchange="updateLink(3,this);">
<input type="checkbox" id="codech264toggle" data-param="&codec=h264" onchange="updateLink(3,this, true);">
<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" style="width: 10em; background-color: #77C"><span data-translate="this-can-reduce-packet-loss">Can reduce packet loss video corruption in OBS on PC</span></span></font>
<span data-translate="use-h264-codec">Use H264 codec</span>
</div>
<div style="display:inline-block;margin-top: 12px; position: relative; margin-right:10px; ">
<div style="display:inline-block;margin-top: 12px; position: relative; margin-right:10px; ">
<label class="switch" title="The active speakers are made visible automatically">
<input type="checkbox" data-param="&sas" onchange="updateLink(3,this);">
@ -1207,19 +1205,17 @@
<br />
<label class="switch" title="Set the background color to bright green">
<input type="checkbox" data-param="&chroma" onchange="updateLink(3,this);">
<input type="checkbox" data-param="&chroma" onchange="updateLink(3,this, true);">
<span class="slider"></span>
</label>
<span data-translate="green-background">Green background</span>
<br />
<label class="switch" title="Fade videos in over 500ms">
<input type="checkbox" data-param="&fadein" onchange="updateLink(3,this);">
<input type="checkbox" data-param="&fadein" onchange="updateLink(3,this, true);">
<span class="slider"></span>
</label>
<span data-translate="fade-videos-in">Fade-in videos</span>
<br />
<label class="switch" title="Videos use an animated transition when being remixed">
@ -1232,7 +1228,7 @@
<div style="display:inline-block;margin-top: 12px; position: relative; margin-right:10px;">
<label class="switch">
<input type="checkbox" data-param="&sl" onchange="updateLink(3,this);">
<input type="checkbox" data-param="&sl" onchange="updateLink(3,this, true);">
<span class="slider"></span>
</label>
<span data-translate="show-display-names">Show display names</span>
@ -1244,36 +1240,40 @@
</label>
<span data-translate="add-margin">Add margin to videos</span>
<br />
<label class="switch">
<input type="checkbox" data-param="&vb=20000" onchange="updateLink(3,this);">
<input type="checkbox" data-param="&vb=20000" onchange="updateLink(3,this, true);">
<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">This can cause video playback to lag</span></font> Unlock Video Bitrate
<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">This can cause video playback to lag</span></font>
<span data-translate="unlock-video-bitrate">unlock Video Bitrate</span>
<br />
<label class="switch" title="Disable fit-to-window optmized video scaling for added sharpness; increases CPU / Network load though.">
<input type="checkbox" data-param="&scale=100" onchange="updateLink(3,this);">
<input type="checkbox" data-param="&scale=100" onchange="updateLink(3,this, true);">
<span class="slider"></span>
</label>
<span data-translate="disable-downscaling">Viewer-side downscaling</span>
<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">This can cause higher CPU load for everyone in the room</span></font>
<span data-translate="disable-downscaling">Increase sharpness</span>
</div>
<div style="display:inline-block;margin-top: 12px; position: relative; ">
<label class="switch" title="Playback the video with mono-channel audio">
<input type="checkbox" data-param="&mono" onchange="updateLink(3,this);">
<input type="checkbox" data-param="&mono" onchange="updateLink(3,this, true);">
<span class="slider"></span>
</label>
<span data-translate="force-mono-audio">Force mono audio</span>
<br />
<label class="switch" title="Have the videos fit their respective areas, even if it means cropping a bit">
<input type="checkbox" data-param="&cover" onchange="updateLink(3,this);">
<input type="checkbox" data-param="&cover" onchange="updateLink(3,this, true);">
<span class="slider"></span>
</label>
<span data-translate="fill-video-space">Crop video to fit</span>
<br />
<label class="switch" title="Have videos be aligned with sizing designed for vertical video">
<input type="checkbox" data-param="&916" onchange="updateLink(3,this);">
<input type="checkbox" data-param="&916" onchange="updateLink(3,this, true);">
<span class="slider"></span>
</label>
<span data-translate="vertical-aspect-ratio">Vertical video mode</span>
@ -1794,6 +1794,34 @@
</li>
</ul>
</nav>
<nav id="context-menu-screen-share" class="context-menu">
<ul class="context-menu__items">
<li class="context-menu__item">
<a href="#" class="context-menu__link" data-action="SSNewTab">
<i class="las la-external-link"></i>
<span data-translate="open-ss-in-new-tab">Share from a new tab</span>
</a>
</li>
<li class="context-menu__item hidden">
<a href="#" class="context-menu__link" data-action="ss1">
1📷
<span data-translate="ss-mode-1" >Screen Share Mode 1</span>
</a>
</li>
<li class="context-menu__item hidden">
<a href="#" class="context-menu__link" data-action="ss2">
2📷
<span data-translate="ss-mode-2" >Screen Share Mode 2</span>
</a>
</li>
<li class="context-menu__item hidden">
<a href="#" class="context-menu__link" data-action="ss3">
3📷
<span data-translate="ss-mode-3" >Screen Share Mode 3</span>
</a>
</li>
</ul>
</nav>
<nav id="context-menu-video" class="context-menu">
<ul class="context-menu__items">
<li class="context-menu__item">
@ -2140,7 +2168,7 @@
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
session.salt = location.hostname; // used only if password is not == False. You can change to "session.salt = location.hostname+location.pathname;" for greater deployment isolation
// session.salt = location.hostname; // used only if password is not == False. You can change to "session.salt = location.hostname+location.pathname;" for greater deployment isolation
session.stunServers = [{ urls: ["stun:stun.l.google.com:19302", "stun:stun4.l.google.com:19302"]}]; // google stun servers. default
@ -2208,11 +2236,11 @@
// session.defaultBackgroundImages = ["./media/bg_sample1.webp", "./media/bg_sample2.webp"]; // for &effects=5 (virtual backgrounds)
</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=390"></script>
<script type="text/javascript" crossorigin="anonymous" id="lib-js" src="./lib.js?ver=405"></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=404"></script>
<script type="text/javascript" crossorigin="anonymous" id="main-js" src="./main.js?ver=417"></script>
</body>
</html>

1048
lib.js

File diff suppressed because it is too large Load Diff

View File

@ -2534,7 +2534,9 @@ button.toggleSettings{
#minipreview > #videosource {
height:auto!important;
width:auto!important;;
width:auto!important;
max-height:100%!important;
max-width:100%!important;
}
#videoSourceSelect {
@ -3445,7 +3447,18 @@ div.message-card a {
border-left: 4px solid #aacefd;
background: #e6e8f0;
}
.darktheme #guestTips {
background-color: #414141;
}
.darktheme #guestTips .las {
color: #FFF;
}
.darktheme .message-card {
background-color: #000;
}
.darktheme input[type='file'] {
background-color: #000;
}
.message-card h1 {
display: block;
font-size: 110%;
@ -3689,9 +3702,8 @@ input:checked + .slider:before {
border-radius: 10px;
font-weight: bold;
z-index:32;
min-width:360px;
max-width:90%;
overflow-wrap: break-word;
width: 240px;
}
#connectUsers{
@ -3781,12 +3793,6 @@ input:checked + .slider:before {
font-size: 100%;
}
@media only screen and (max-width: 390px) {
.alertModal {
width: 90%;
}
}
.iframeDetails {
margin: 10px;
@ -4040,7 +4046,7 @@ input:checked + .slider:before {
content: "\f0c0"; }
.la-spinner:before {
content: "\f110"; }
.la.la-external-link:before {
.la-external-link:before {
content: "\f35d"; }
.la-pen:before {
content: "\f304"; }
@ -4115,7 +4121,8 @@ body.darktheme h2 {
body.darktheme button {
filter: brightness(0.70);
}
body.darktheme .las {
body.darktheme .column .las {
color: black;
}
body.darktheme label {
filter: brightness(0.85);

146
main.js
View File

@ -43,7 +43,7 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
getById("mainmenu").style.opacity = 1;
}
}
if (location.hostname !== "vdo.ninja" && location.hostname !== "backup.vdo.ninja" && location.hostname !== "obs.ninja") {
if (location.hostname !== "vdo.ninja" && location.hostname !== "backup.vdo.ninja" && location.hostname !== "proxy.vdo.ninja" && location.hostname !== "obs.ninja") {
if (location.hostname === "rtc.ninja"){
try {
if (session.label === false) {
@ -168,7 +168,11 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
session.safemode = true; // load defa
} else {
session.store = {};
loadSettings();
try {
loadSettings();
} catch(e){
errorlog(e);
}
}
if (navigator.userAgent.toLowerCase().indexOf(' electron/') > -1) {
@ -436,7 +440,7 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
session.slotmode = false; // temporary; remove in the future TODO: ## -----------------------
if (urlParams.has('slotmode')){
session.slotmode = true;
session.slotmode = parseInt(urlParams.get('slotmode')) || 1;
}
@ -530,6 +534,11 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
if (urlParams.has('nohangupbutton') || urlParams.has('nohub')){
getById("hangupbutton").style.display = "none";
session.hangupbutton = false;
}
if (urlParams.has('hangupbutton') || urlParams.has('hub')){
session.hangupbutton = true;
}
if (urlParams.has('socialstream')){
@ -669,13 +678,16 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
if (urlParams.has('layout')) {
try {
session.layout = JSON.parse(decodeURIComponent(urlParams.get('layout'))) || JSON.parse(urlParams.get('layout')) || false;
session.layout = JSON.parse(decodeURIComponent(urlParams.get('layout'))) || JSON.parse(urlParams.get('layout')) || {};
} catch(e){
session.layout = null
try {
session.layout = JSON.parse(urlParams.get('layout')) || {};
} catch(e){
session.layout = {};
}
}
}
if (urlParams.has('deaf') || urlParams.has('deafen')) {
session.directorSpeakerMuted=true; // false == true in this case.
}
@ -691,7 +703,7 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
getById("blindAllGuests").classList.remove("hidden");
}
if (urlParams.has('dpi') || urlParams.has('dpr')) {
if (urlParams.has('dpi') || urlParams.has('dpr') || urlParams.has('sharper') || urlParams.has('sharpen')) {
session.devicePixelRatio = urlParams.get('dpi') || urlParams.get('dpr') || 2.0;
session.devicePixelRatio = parseFloat(session.devicePixelRatio);
} //else if (window.devicePixelRatio && window.devicePixelRatio!==1){
@ -788,6 +800,20 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
}
}
}
if (urlParams.has('screenshareaspectratio') || urlParams.has('ssar')) { // capture aspect ratio
session.forceScreenShareAspectRatio = urlParams.get('screenshareaspectratio') || urlParams.get('ssar') || false;
if (session.forceScreenShareAspectRatio){
if ((session.forceScreenShareAspectRatio == 'portrait') || (session.forceScreenShareAspectRatio == 'vertical')){
session.forceScreenShareAspectRatio = 9.0/16.0;
} else if (session.forceScreenShareAspectRatio == 'landscape'){
session.forceScreenShareAspectRatio = 16.0/9.0;
} else if (session.forceScreenShareAspectRatio == 'square'){
session.forceScreenShareAspectRatio = 1.0;
} else {
session.forceScreenShareAspectRatio = parseFloat(session.forceScreenShareAspectRatio) || false;
}
}
}
if (urlParams.has('crop')){
var crop = parseFloat(urlParams.get('crop')) || 0;
@ -895,6 +921,16 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
} else {
session.scene = (parseInt(session.scene) || 0) + "";
}
}
if (urlParams.has('solo')){
if (session.scene===false){
session.scene = "0";
}
session.solo = true;
}
if (session.scene!==false){
session.disableWebAudio = true;
session.audioEffects = false;
session.audioMeterGuest = false;
@ -1583,11 +1619,12 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
}
if (urlParams.has('ruler') || urlParams.has('grid') || urlParams.has('thirds')) {
session.ruleOfThirds=true;
session.fullscreen = true;
if (!session.manual){
session.manual = false;
}
session.ruleOfThirds = urlParams.get('ruler') || urlParams.get('grid') || urlParams.get('thirds') || "./media/thirds.svg";
session.ruleOfThirds = decodeURIComponent(session.ruleOfThirds);
}
if (urlParams.has('smallshare')){
@ -1596,6 +1633,8 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
if (urlParams.has('proxy')) { // routes the wss traffic via an alternative network path. Not
session.proxy=true; // only works if session.wss is set to false
} else if (location.hostname === "proxy.vdo.ninja"){
session.proxy=true;
}
if (urlParams.has('nopreview') || urlParams.has('np')) {
@ -2169,6 +2208,7 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
session.alpha = true;
}
if (urlParams.has('debug')){
session.debug=true;
debugStart();
@ -2295,6 +2335,10 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
}
}
if (urlParams.has('sharperscreen')) { // sets scale to 100 for inbound screenshares only
session.sharperScreen = true;
}
if (urlParams.has('mcscale') || urlParams.has('meshcastscale')) {
session.meshcastScale = parseFloat(urlParams.get('mcscale')) || parseFloat(urlParams.get('meshcastscale')) || 100;
}
@ -2534,6 +2578,10 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
session.cleanDirector = true;
}
if (urlParams.has('hidetranslate')) {
getById("translateButton").style.display = "none";
}
if (session.cleanOutput){
getById("translateButton").style.display = "none";
getById("credits").style.display = "none";
@ -2556,6 +2604,14 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
}
getById("credits").innerHTML = "Version: " + session.version + " - " + getById("credits").innerHTML;
if (urlParams.has('hideheader') || urlParams.has('noheader') || urlParams.has('hh')) { // needs to happen the room and permaid applications
getById("header").style.display = "none";
getById("header").style.opacity = 0;
} else if (urlParams.has('showheader')) { // needs to happen the room and permaid applications
getById("header").style.display = "inherit";
getById("header").style.opacity = 1;
}
if (urlParams.has('minidirector')) {
try {
var cssStylesheet = document.createElement('link');
@ -2803,7 +2859,7 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
session.audioEffects = true;
session.audioMeterGuest = true;
session.minipreview = 2;
if (session.activeSpeaker==1){
if ((session.activeSpeaker==1) || (session.activeSpeaker==3)){
session.animatedMoves = false;
}
session.fadein=true;
@ -2970,7 +3026,7 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
if (urlParams.has('stun')) {
var stunstring = urlParams.get('stun');
stunstring = stunstring.split(";");
if (stunstring !== "false") { // false disables the TURN server. Useful for debuggin
if (stunstring[0] !== "false") { // false disables the TURN server. Useful for debuggin
var stun = {};
if (stunstring.length==3){
stun.username = stunstring[0]; // myusername
@ -3137,7 +3193,10 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
if (urlParams.has('wss')) {
session.customWSS = true;
if (urlParams.get('wss')) {
session.wss = "wss://" + urlParams.get('wss');
session.wss = urlParams.get('wss');
if (!session.wss.startsWith("wss://")){
session.wss = "wss://" + session.wss;
}
}
}
@ -3578,12 +3637,6 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
getById("header").style.opacity = 0;
}
if (urlParams.has('hideheader') || urlParams.has('noheader') || urlParams.has('hh')) { // needs to happen the room and permaid applications
getById("header").style.display = "none";
getById("header").style.opacity = 0;
}
if (session.view) {
getById("main").className = "";
@ -3999,19 +4052,19 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
if ("streamID" in session.rpcs[i]) {
if ("target" in e.data) {
if ((session.rpcs[i].streamID == e.data.target) || (e.data.target == "*")) { // specify a stream ID or let it apply to all videos
session.rpcs[i].manualBitrate = e.data.manualBitrate;
session.rpcs[i].manualBandwidth = e.data.manualBitrate;
session.requestRateLimit(false, i);
}
} else if (e.data.UUID && (e.data.UUID===i)) {
session.rpcs[i].manualBitrate = e.data.manualBitrate;
session.rpcs[i].manualBandwidth = e.data.manualBitrate;
session.requestRateLimit(false, i);
} else if (e.data.streamID) {
if (session.rpcs[i].streamID == e.data.streamID) { // specify a stream ID or let it apply to all videos
session.rpcs[i].manualBitrate = e.data.manualBitrate
session.rpcs[i].manualBandwidth = e.data.manualBitrate
session.requestRateLimit(false, i);
}
} else {
session.rpcs[i].manualBitrate = e.data.manualBitrate;
session.rpcs[i].manualBandwidth = e.data.manualBitrate;
session.requestRateLimit(false, i);
}
}
@ -4102,9 +4155,15 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
}
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();
}
var msg = {};
msg.obsState = {};
msg.obsState.visibility = e.data.sceneState || false;
msg.obsState.recording = e.data.sceneState || false;
session.sendRequest(msg);
}
if ("layouts" in e.data) {
session.layouts = e.data.layouts;
}
if ("sendMessage" in e.data) { // webrtc send to viewers
@ -4359,13 +4418,6 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
}
}
if ("previewMode" in e.data){
if ("layout" in e.data){
session.layout = e.data.layout;
}
switchModes(e.data.previewMode);
}
if ("advancedMode" in e.data){
if (e.data.advancedMode){
document.documentElement.style.setProperty('--advanced-mode', "inline-block"); // show advanced items
@ -4381,14 +4433,38 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
} // don't use if the stream is in your room (as not needed)
} // you can load a stream ID from inside a room that exists outside any room
if (("scene" in e.data) && ("layout" in e.data)){
if ("previewMode" in e.data){
if ("layout" in e.data){
session.layout = e.data.layout;
pokeIframeAPI("layout-updated", session.layout);
}
switchModes(e.data.previewMode);
} else if ("layout" in e.data){
warnlog("changing layout request via IFRAME API");
issueLayout(e.data.layout, e.data.scene);
session.layout = e.data.layout;
pokeIframeAPI("layout-updated", session.layout);
if (session.director){
session.layout = e.data.layout; // not sure this is ideal, but whatever.
updateMixer();
if ("scene" in e.data){
if ("UUID" in e.data){
issueLayout(e.data.layout, e.data.scene, e.data.UUID);
} else {
issueLayout(e.data.layout, e.data.scene);
}
} else if ("UUID" in e.data){
issueLayout(e.data.layout, false, e.data.UUID);
}
}
updateMixer();
}
if ("slotmode" in e.data){
if (session.slotmode){
session.slotmode = parseInt(e.data.slotmode);
} else {
session.slotmode = false;
}
}
//////////// manual scale. Request a specific down-scaled resolution from a remote connection
var targetWidth = false;
var targetHeight = false;

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -1,11 +1,8 @@
<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>
<svg viewBox="0 0 800 600" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="none" style="width:100%;height:100%;"><g>
<line stroke-width="2" stroke-dasharray="5,5" stroke="#FFF1" stroke-linecap="undefined" stroke-linejoin="undefined" id="svg_1" y2="200" x2="799" y1="200" x1="1" fill="none"/>
<line stroke="#FFF5" stroke-linecap="undefined" stroke-linejoin="undefined" id="svg_3" y2="616" x2="266" y1="1" x1="266" stroke-dasharray="5,5" stroke-width="2" fill="none"/>
<line stroke-width="2" stroke-dasharray="5,5" stroke="#FFF1" stroke-linecap="undefined" stroke-linejoin="undefined" id="svg_4" y2="200" x2="799" y1="200" x1="1" fill="none"/>
<line stroke="#FFF5" stroke-linecap="undefined" stroke-linejoin="undefined" id="svg_5" y2="616" x2="534" y1="1" x1="534" stroke-dasharray="5,5" stroke-width="2" fill="none"/>
<line stroke-width="2" stroke-dasharray="5,5" stroke="#FFF1" stroke-linecap="undefined" stroke-linejoin="undefined" id="svg_2" y2="400" x2="799" y1="400" x1="1" fill="none"/>
<line stroke-width="2" stroke-dasharray="5,5" stroke="#FFF1" stroke-linecap="undefined" stroke-linejoin="undefined" id="svg_6" y2="400" x2="799" y1="400" x1="1" fill="none"/>
</g></svg>

Before

Width:  |  Height:  |  Size: 985 B

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -20,12 +20,12 @@
--chat-width: 450px;
}
body{
body {
padding:0;
margin:0;
background-color: #c9c9c9;
font-family: 'Sora', sans-serif;
overflow: hidden;
overflow: auto;
position: absolute;
border-radius: 50px;
background: #2e445c;
@ -60,7 +60,7 @@
position:absolute;
top: -150px;
}
#modal{
.modal{
overflow:auto;
}
.message{
@ -111,6 +111,16 @@
background: transparent; /* make scrollbar transparent */
}
.xbutton{
float: right;
cursor: pointer;
user-select: none;
color: white;
font-size: 150%;
font-family: Verdana;
line-height: 14px;
}
#chatModule {
bottom: 0;
position: fixed;
@ -122,6 +132,7 @@
height: calc(100vh - 40px);
overflow: hidden;
right:0;
background:#0001;
border: solid 2px #0005;
border-radius: 10px;
padding: 10px;
@ -146,23 +157,7 @@
button[data-state='true']{
background-color:#CEF !important;
}
#inviteOptions{
border: 0;
padding: 0;
display: block;
height: calc(100vh - 80px);
width: calc(100vw - 160px);
max-width:100%;
padding: 20px;
margin:0;
border:0;
display: inline-block;
background-color: #DDD;
border-radius: 18px;
background: #5b748f;
box-shadow: inset 20px 20px 40px #556c85,
inset -20px -20px 40px #617c99;
}
input[type="checkbox"] {
width: 20px;
height: 20px;
@ -201,13 +196,12 @@
#iframeContainer{
position: absolute;
left: 160px;
height: 100%;
width: calc(100vw - var(--chat-width) - 160px);
box-shadow: 1px 1px 3px #1b1b1b, -1px -1px 3px #1d1d1d;
}
#vdoninja {
max-width: calc(100vw - 160px - var(--chat-width));
width: 100vw;
height: calc(100vh - 90px);
}
#viewlink {
@ -437,23 +431,7 @@
display: inline-block;
}
#sceneSettings{
height: calc(100vh - 80px);
width: calc(100vw - 160px);
padding: 20px;
margin:0;
border:0;
display: inline-block;
background-color: #DDD;
border-radius: 18px;
background: #5b748f;
box-shadow: inset 20px 20px 40px #556c85,
inset -20px -20px 40px #617c99;
}
#sceneSettings > h2{
margin: 4px;
}
.settings {
display: block;
@ -596,7 +574,6 @@
}
.modal {
display: none;
position: fixed;
padding-top: 50px;
left: 0;
@ -606,12 +583,12 @@
background-color: rgb(0, 0, 0);
background-color: rgba(0, 0, 0, 0.5);
z-Index: 20;
}
.modal-content {
position: relative;
padding: 20px;
margin: auto;
margin: auto;
margin-bottom: 100px;
width: 75%;
-webkit-animation-name: animatetop;
-webkit-animation-duration: 0.4s;
@ -622,16 +599,18 @@
background-image: url("data:image/svg+xml,%3Csvg width='6' height='6' viewBox='0 0 6 6' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%239C92AC' fill-opacity='0.1' fill-rule='evenodd'%3E%3Cpath d='M5 0h1L0 6V5zM6 5v1H5z'/%3E%3C/g%3E%3C/svg%3E");
}
.close-btn {
float: right;
color: lightgray;
color: #333;
font-size: 24px;
font-weight: bold;
user-select: none;
}
.close-btn:hover {
color: darkgray;
color: black;
cursor:pointer;
}
span.close-btn {
float: right;
}
@-webkit-keyframes animatetop {
from {top:-300px; opacity:0}
to {top:0; opacity:1}
@ -862,9 +841,9 @@
<div id="container">
<div id="containermenu"></div>
<div id="containermenu2" class="hidden">
<button onclick="addElement();"> Add Item</button>
<button onclick="addElement();"> Add Element to Scene</button>
<button onclick="saveScene(false, event);">💾 Save Scene</button>
<button onclick="saveScene(true, event);">💾<span style="position:relative;right:12px;top:2px;width:12px;display: inline-block;">💾</span>Duplicate</button>
<button onclick="saveScene(true, event);">💾<span style="position:relative;right:12px;top:2px;width:12px;display: inline-block;">💾</span>Duplicate Scene</button>
<button onclick="copyJSON();" title="This can be used with &format directly in VDO.Ninja without the mixer app.">Show as JSON</button>
<button onclick="removeScene();">🗑️ Remove Scene</button>
<button id="saveAndClose" onclick="saveScene(false, event);closeScene();">💾❌ Save and Close</button>
@ -873,7 +852,7 @@
</div>
<div id="chatModuleButton" class="hidden" onclick="toggleChat();" style="user-select: none;position: fixed;top: 10px;right: 10px;cursor: pointer;" title="Show the chat window">💬</div>
<div id="chatModule" class="hidden">
<div style="float: right;cursor: pointer;user-select: none;" onclick="toggleChat();" title="Hide the chat window">x</div>
<div class="xbutton" onclick="toggleChat();" title="Hide the chat window">x</div>
<div id="chatBody" class="message">
<div class="inMessage" data-translate='welcome-to-vdo-ninja-chat'>
Welcome to VDO.Ninja! You can send text messages directly to connected peers from here.
@ -918,17 +897,32 @@
<div id="iframeContainer" class="tFadeStart">
<div id='canvas' class="hidden">
</div>
<div id='sceneSettings' class="hidden2">
</div>
<!--
<div class="modal" >
<span class="close-btn">&times;</span>
<div id="modal-content">
<p>this is the text inside the modal</p>
</div>
</div>
</div>
-->
<div id='sceneSettings' class="hidden modal">
<div class="modal-content">
<span class="close-btn">&times;</span>
<h2>General Settings</h2>
<h4>Aspect Ratio</h4>
<input type="checkbox" class="aspectbutton" onchange="changeAspectRatio(16/9.0, this);">16:9
<input type="checkbox" checked class="aspectbutton" onchange="changeAspectRatio(16/9.0, this);">16:9
<input type="checkbox" class="aspectbutton" onchange="changeAspectRatio(9.0/16, this);">9:16
<input type="checkbox" class="aspectbutton" onchange="changeAspectRatio(1.0, this);">1:1
<br /><small><i>This just impacts the aspect ratio of the local preview.</i></small><br />
<h4>Layout switching</h4>
<input type="checkbox" title="When a user is assigned a slot or switches slots, the last active layout is re-applied automatically" id="updateOnSlotChange" checked onchange="submitChange(this)";>Update layout on a slot change
<h4>Slot assignment</h4>
<input type="checkbox" title="A guest is assigned a slot when they join, automatically. If disabled, they must be assigned a slot manually." id="assignSlotToGuest" checked onchange="submitChange2(this)";>Assign a slot to new guests automatically
<h4>Show advanced controls</h4>
<input type="checkbox" title="Shows more director control options" onchange="toggleAdvanced(this)";>Show the advanced director control options
@ -941,37 +935,36 @@
<button onclick="exportSession();">Export all scenes and settings to disk</button><br />
<h4>📥 Import settings </h4>
Import scenes and settings from local file:<br /><input type="file" accept=".json" onchange="importSession(event);"/><br /><br />
<button onclick="getById('modal-content').innerHTML = '';modal.style.display = 'none';">Close Settings</button>
<button class='close-btn'>Close Settings</button>
</div>
<div id='roomSettings' class="hidden2">
</div>
<div id='roomSettings' class="hidden modal">
<div class="modal-content">
<span class="close-btn">&times;</span>
<h2>Room Setup</h2>
<br /><br />
<button onclick="showSettings();">Close</button>
</div>
<div id='inviteOptions' class="hidden">
</div>
<div id='inviteOptions' class="hidden modal">
<div class="modal-content">
<span class="close-btn">&times;</span>
<h2>Invite options</h2>
<h4>Your room name is: <b class="roomname">ROOMNAME</b></h4>
<h3>Create new invite</h3>
<a class="inviteLink" target="_blank">Invite Link</a><i class="inviteLink"></i><br />
<span class="hidden">
<input type="checkbox" /><label>Prompt user for a display name?</label><br />
<input type="checkbox" /><label>Guest can see other guests? (higher CPU for guest)</label><br />
<input type="checkbox" /><label>Guest will join muted? (director can remotely unmute)</label><br />
</span>
<i>You can manually customize the invite link further; see the documentation at <a href="docs.vdo.ninja" target="_blank">docs.vdo.ninja</a></i>
<h3>Copy invite to clipboard</h3>
<a class="inviteLink" target="_blank">Invite Link</a><i class="inviteLink"></i>
<br /><br />
<button onclick="getById('modal-content').innerHTML = '';modal.style.display = 'none';">Close</button>
<h3>Customize invite link</h3>
<span >
<input type="checkbox" id="toggleLabel" onclick="updateInviteLinks(event)" /><label>Prompt user for a display name</label><br />
<input type="checkbox" id="toggleBroadcast" onclick="updateInviteLinks(event)"/><label>Guest can see other guests and the active layout <small>(higher CPU for guest)</small></label><br />
</span>
<i>You can manually customize the invite link further; see the documentation at <a href="https://docs.vdo.ninja" target="_blank">docs.vdo.ninja</a></i>
<br /><br />
<button class='close-btn'>Close</button>
</div>
</div>
<div id="modal" class="modal" >
<div class="modal-content">
<span class="close-btn">&times;</span>
<div id="modal-content">
<p>this is the text inside the modal</p>
</div>
</div>
</div>
<div class="gone" >
<!-- This image is used when dragging elements -->
<img src="./media/favicon-32x32.png" style="pointer-events: none;" id="dragImage" loading="lazy" />
@ -1048,6 +1041,7 @@
}
return roomid;
}
var urlEdited = window.location.search.replace(/\?\?/g, "?");
urlEdited = urlEdited.replace(/\?/g, "&");
@ -1059,24 +1053,33 @@
}
var urlParams = new URLSearchParams(urlEdited);
var api = false;
if (urlParams.has('osc') || urlParams.has('api')) {
if (urlParams.get('osc') || urlParams.get('api')) {
api = urlParams.get('osc') || urlParams.get('api');
}
}
var streamIDs = [];
var slotsNeeded = 1;
var lastLayout = false;
var lastLayout = {"scene":"0", "layout":false};
var lastLayoutRaw = false;
var updateOnSlotChange = true;
var assignSlotToGuest = true;
var toggleLabel = false;
var toggleBroadcast = true;
var password = false;
if (urlParams.has("password")){
password = urlParams.get("password");
if (urlParams.has('password') || urlParams.has('pass') || urlParams.has('pw') || urlParams.has('p')) {
password = urlParams.get('password') || urlParams.get('pass') || urlParams.get('pw') || urlParams.get('p');
}
var aspectRatio = 16/9.0;
document.documentElement.style.setProperty('--aspect-ratio', aspectRatio);
var roomname = false;
if (urlParams.has("room") || urlParams.has("dir") || urlParams.has("director")){
roomname = urlParams.get("room") || urlParams.get("dir") || urlParams.get("director");
if (urlParams.has("room") || urlParams.has("r") ||urlParams.has("dir") || urlParams.has("director")){
roomname = urlParams.get("room") || urlParams.get("r") ||urlParams.get("dir") || urlParams.get("director");
roomname = sanitizeRoomName(roomname);
}
var savedLastRoom = getStorage("savedRoom");
@ -1191,8 +1194,6 @@
return "Seconds ago";
}
function updateMessages(message = false){
if (message){
var time = timeSince(message.time);
@ -1558,17 +1559,20 @@
{x:0, y:0, w:100, h:100}
];
initialLayouts.layouts.push(data);
var data = [
null,
{x:0, y:0, w:100, h:100}
];
initialLayouts.layouts.push(data);
var data = [
null,
null,
{x:0, y:0, w:100, h:100}
];
initialLayouts.layouts.push(data);
var data = [
null,
null,
@ -1576,6 +1580,7 @@
{x:0, y:0, w:100, h:100}
];
initialLayouts.layouts.push(data);
var data = [
{x:0, y:0, w:50, h:100, c:false},
{x:50, y:0, w:50, h:100, c:false}
@ -1584,7 +1589,7 @@
var data = [
{x:70, y:70, w:30, h:30, z:1, c:true},
{x:0, y:0, w:100, h:100,z:0, c:false}
{x:0, y:0, w:100, h:100, z:0, c:false}
];
initialLayouts.layouts.push(data);
@ -1595,6 +1600,7 @@
{x:50, y:50, w:50, h:50, c:true}
];
initialLayouts.layouts.push(data);
var data = [
{x:0, y:16.667, w:66.667, h:66.667, c:true},
{x:66.667, y:0, w:33.333, h:33.333, c:true},
@ -1610,6 +1616,42 @@
getById("updateOnSlotChange").value = "off";
getById("updateOnSlotChange").checked = false;
getById("updateOnSlotChange").removeAttribute('checked');
} else {
getById("updateOnSlotChange").value = "on";
getById("updateOnSlotChange").checked = true;
}
}
if (savedSession.settings && ("assignSlotToGuest" in savedSession.settings)){
assignSlotToGuest = savedSession.settings.assignSlotToGuest;
if (!assignSlotToGuest){
getById("assignSlotToGuest").value = "off";
getById("assignSlotToGuest").checked = false;
getById("assignSlotToGuest").removeAttribute('checked');
} else {
getById("assignSlotToGuest").value = "on";
getById("assignSlotToGuest").checked = true;
}
}
if (savedSession.settings && ("toggleLabel" in savedSession.settings)){
toggleLabel = savedSession.settings.toggleLabel;
if (!toggleLabel){
getById("toggleLabel").value = "off";
getById("toggleLabel").checked = false;
getById("toggleLabel").removeAttribute('checked');
} else {
getById("toggleLabel").value = "on";
getById("toggleLabel").checked = true;
}
}
if (savedSession.settings && ("toggleBroadcast" in savedSession.settings)){
toggleBroadcast = savedSession.settings.toggleBroadcast;
if (!toggleBroadcast){
getById("toggleBroadcast").value = "off";
getById("toggleBroadcast").checked = false;
getById("toggleBroadcast").removeAttribute('checked');
} else {
getById("toggleBroadcast").value = "on";
getById("toggleBroadcast").checked = true;
}
}
}
@ -1623,6 +1665,10 @@
savedSession = initialLayouts;
}
if (iframe){
iframe.contentWindow.postMessage({ layouts: savedSession.layouts }, "*");
}
var guestPositions = {};
function submitChange(element){
@ -1635,6 +1681,19 @@
saveSession();
}
function submitChange2(element){
if (element.checked){
assignSlotToGuest=true;
iframe.contentWindow.postMessage({"slotmode":1}, '*');
} else { // do not assign guests to slots automatically
element.removeAttribute('checked');
assignSlotToGuest=false
iframe.contentWindow.postMessage({"slotmode":2}, '*');;
}
saveSession();
}
function toggleAdvanced(element){
if (element.checked){
iframe.contentWindow.postMessage({"advancedMode":true}, '*');
@ -1644,8 +1703,6 @@
}
}
function exportSession() {
var content = JSON.stringify(savedSession);
var fileName = roomname + ".json";
@ -1733,9 +1790,6 @@
background: #2e445c;
box-shadow: 20px 20px 60px #182430, -20px -20px 60px #283c52;
}
label {
color: #fff;
}
`;
injectCSS = encodeURIComponent(btoa(injectCSS));
@ -1880,20 +1934,27 @@
}
}
var iframe = null;
function loadIframe(){
if (iframe){return;}
iframe = document.createElement("iframe");
var additional = "";
var additional = ""; // guest/scene links also
if (password){
additional = "&password="+password;
}
var additional2 = ""; // this iframe only
if (api){
additional2 += "&api="+api;
}
if (assignSlotToGuest){
additional2+="&slotmode";
} else {
additional2+="&slotmode=2";
}
roomname = sanitizeRoomName(roomname);
@ -1901,13 +1962,12 @@
iframe.allow = "autoplay;camera;microphone;fullscreen;picture-in-picture;display-capture;";
iframe.id = "vdoninja";
if (roomname){
var iframesrc = "./index.html?novice&ltb=350&transparent&cleanoutput&slotmode&director="+roomname+additional+"&b64css="+injectCSS;
} else {
if (!roomname){
roomname = generateString(10);
var iframesrc = "./index.html?novice&ltb=350&transparent&cleanoutput&slotmode&director="+roomname+additional+"&b64css="+injectCSS;
}
var iframesrc = "./index.html?novice&ltb=350&transparent&hideheader&hidetranslate&cleandirector&chatbutton=0&director="+roomname+additional+additional2+"&b64css="+injectCSS;
if (roomname!==false){
setStorage("savedRoom", {roomname:roomname,password:password}, 9999);
}
@ -1937,7 +1997,7 @@
button.onclick = function(){
this.state = !this.state;
this.dataset.state = this.state;
iframe.contentWindow.postMessage({"previewMode":this.state, "layout":currentLayout, "bitrate":35, "target": "*"}, '*');
iframe.contentWindow.postMessage({"previewMode":this.state, "layout":currentLayout.layouts, "bitrate":35, "target": "*"}, '*');
if (this.state){
this.innerHTML = "Director View &#x21bb;";
iframe.classList.add("aspectRatio");
@ -1998,14 +2058,16 @@
var a = document.createElement("a");
a.innerHTML = "Invite Guest Link 📎";
a.href = "./index.html?room="+roomname+"&sstype=3&broadcast"+additional;
a.href = "./?room="+roomname+"&sstype=3&broadcast"+additional;
a.target = "_blank";
a.onclick = function(evt){copyFunction(this, evt);};
document.getElementById("sources").appendChild(a);
var a = document.createElement("a");
a.innerHTML = "Scene View Link 📎";
a.href = "./index.html?scene=0&layout=%7B%7D&room="+roomname+additional;
a.innerHTML = "Scene View Link 📎";
a.href = "./?scene=0&layout&room="+roomname+additional;
a.target = "_blank";
a.onclick = function(evt){copyFunction(this, evt);};
document.getElementById("sources").appendChild(a);
@ -2026,6 +2088,10 @@
document.getElementById("chatModule").classList.remove("hidden");
iframe.onload = function(){
iframe.contentWindow.postMessage({ layouts: savedSession.layouts }, "*");
}
iframe.src = iframesrc;
iframeContainer.appendChild(iframe);
document.getElementById("container").appendChild(iframeContainer);
@ -2044,7 +2110,7 @@
eventer(messageEvent, function (e) {
if (e.source != iframe.contentWindow){return} // reject messages send from other iframes
console.log(e.data);
//console.log(e.data);
if ("action" in e.data){
///var outputWindow = document.createElement("div");
@ -2085,6 +2151,23 @@
if (e.data.action && (e.data.action == "scene-connected")){
if (lastLayout && lastLayout.scene == e.data.value){
var layoutIssue = {};
layoutIssue.layout = lastLayout.layout;
if (e.data.UUID){
layoutIssue.UUID = e.data.UUID;
}
layoutIssue.scene = lastLayout.scene
iframe.contentWindow.postMessage(lastLayout, '*');
}
}
if (e.data.action && (e.data.action == "guest-connected")){
if (lastLayout){
var layoutIssue = {};
layoutIssue.layout = lastLayout.layout;
if (e.data.UUID){
layoutIssue.UUID = e.data.UUID;
}
iframe.contentWindow.postMessage(lastLayout, '*');
}
}
@ -2136,18 +2219,37 @@
}
function showSettings(){
document.getElementById("modal").style.display = "block";
applySettings();
document.getElementById("modal-content").innerHTML = document.getElementById("sceneSettings").innerHTML;
document.getElementById("sceneSettings").classList.remove("hidden");
}
function showInviteOptions(){
document.getElementById("modal").style.display = "block";
document.getElementById("modal-content").innerHTML = document.getElementById("inviteOptions").innerHTML;
function showInviteOptions(event=false){
document.getElementById("inviteOptions").classList.remove("hidden");
updateInviteLinks();
}
var toggleBroadcast = false;
function updateInviteLinks(event=false){
var additional = "";
if (password){
additional = "&password="+password;
additional += "&password="+password;
}
if (document.getElementById("toggleLabel").checked){
additional += "&label";
toggleLabel = true;
} else {
toggleLabel= false;
}
if (!document.getElementById("toggleBroadcast").checked){
additional += "&broadcast";
toggleBroadcast = true;
} else {
additional += "&layout";
toggleBroadcast = false;
}
document.querySelectorAll(".roomname").forEach(ele=>{
@ -2156,15 +2258,24 @@
document.querySelectorAll(".inviteLink").forEach(ele=>{
if (ele.tagName == "A"){
ele.href = "./index.html?room="+roomname+"&broadcast"+additional;
ele.href = "./?room="+roomname+additional;
} else if (ele.tagName == "I"){
ele.innerHTML = "URL + ?room="+roomname+"&broadcast"+additional;
ele.innerHTML = "URL + ?room="+roomname+additional;
}
ele.onclick = function(evt){copyFunction(this, evt);};
});
if (event){
document.querySelectorAll(".inviteLink").forEach(ele=>{
ele.classList.remove("shake");
ele.classList.add("shake");
setTimeout(function(ele){ele.classList.remove("shake");},500,ele);
});
saveSession();
}
}
function addLayout(){
var layout = prompt("Enter your new layout as a JSON string", '[{"x":0,"y":0,"w":100,"h":100}]');
layout = JSON.parse(layout);
@ -2214,9 +2325,22 @@
});
}
function compareZ( a, b ) { // sorts layout based on z-index.
var aa = a.z || a.zIndex || 0;
var bb = b.z || b.zIndex || 0;
if ( aa > bb ){
return 1;
}
if ( aa < bb ){
return -1;
}
return 0;
}
function drawLayout(layoutOriginal, sceneName=false){
var layout = [];
for (var i=0;i<layoutOriginal.length;i++){
if (!layoutOriginal[i]){
continue;
@ -2227,18 +2351,7 @@
layout.push(layoutOriginal[i]);
}
function compare( a, b ) { // sorts layout based on z-index.
var aa = a.z || 0;
var bb = b.z || 0;
if ( aa > bb ){
return 1;
}
if ( aa < bb ){
return -1;
}
return 0;
}
layout.sort(compare);
layout.sort(compareZ);
var canvas = document.createElement('canvas');
canvas.width="80";
@ -2492,27 +2605,21 @@
for (var i=0;i<object.length;i++){
if (object[i] == null){continue;}
var slot = parseInt(object[i].slot) || 0;
<!-- ele.w = parseInt(compute.width)/1280*100; -->
<!-- ele.h = parseInt(compute.height)/720*100; -->
<!-- ele.x = parseInt(compute.left)/1280*100; -->
<!-- ele.y = parseInt(compute.top)/720*100; -->
<!-- ele.z = object[i].zIndex; -->
<!-- ele.bg = object[i].backgroundColor; -->
<!-- ele.bt = object[i].borderThickness; -->
<!-- ele.bc = object[i].borderColor; -->
<!-- ele.bm = object[i].backgroundMedia; -->
<!-- ele.margin = object[i].margin; -->
<!-- ele.r = object[i].rounded; -->
<!-- ele.m = object[i].muted; -->
var color = colors[slot];
var container = document.createElement("div");
container.className = "widget ui-widget-content draggable resizable";
container.slot = slot;
container.cover = object[i].cover || false;
container.zIndex = parseInt(object[i].z) || 0;
if ("cover" in object[i]){
container.cover = object[i].cover || false;
} else if ("c" in object[i]){
container.cover = object[i].c || false;
} else {
container.cover = true;
}
container.zIndex = parseInt(object[i].zIndex) || parseInt(object[i].z) || 0;
//container.backgroundColor = object[i].backgroundColor || "#000";
container.borderThickness = object[i].borderThickness || 0;
container.animated = object[i].animated || 0;
@ -2613,7 +2720,7 @@
}
var color = colors[slot];
var container = document.createElement("div");
var container = document.createElement("div"); // we use the long-form of meta attributes for container elements.
container.className = "widget ui-widget-content draggable resizable";
container.slot = slot;
@ -2683,7 +2790,36 @@
$(function(){
$(".resizable").resizable({ snap: true , grid: [ 10, 10] });
});
}
}
function combinedLayout(layout){
var combined = {};
console.log(layout);
for (var i=0;i<layout.length;i++){
console.log(i);
if (!layout[i]){continue;}
if (!("slot" in layout[i])){
continue;
}
try {
var stream = guestPositions[parseInt(layout[i].slot)+1]; // slot 1 is index of 0, but slot 0 is considered NULL; I need to stream line this a bit
} catch(e){
errorlog(e);
continue;
}
console.log(stream);
if (!stream){
if (layout[i].defaultStreamID){
combined[layout[i].defaultStreamID] = layout[i];
}
continue;
}
combined[stream] = layout[i];
}
console.log(combined);
return combined;
}
function remoteActivate(event=null, layout=null){
console.log(this);
@ -2698,45 +2834,19 @@
event.target.parentNode.classList.add("pressed");
}
var combined = false;
lastLayoutRaw = layout;
var combined = false;
if (layout){
layout = JSON.parse(layout);
console.log(layout);
combined = {};
for (var i=0;i<layout.length;i++){
if (!layout[i]){continue;}
if (!("slot" in layout[i])){
continue;
}
try {
var stream = guestPositions[parseInt(layout[i].slot)+1]; // slot 1 is index of 0, but slot 0 is considered NULL; I need to stream line this a bit
} catch(e){
errorlog(e);
continue;
}
console.log(stream);
console.log(i);
console.log(layout);
if (!stream){
if (layout[i].defaultStreamID){
combined[layout[i].defaultStreamID] = layout[i];
}
continue;
}
combined[stream] = layout[i];
}
combined = combinedLayout(layout);
}
console.log(combined);
currentLayout = combined; // global current state
lastLayout = {"scene":"0", "layout":combined};
iframe.contentWindow.postMessage({"scene":"0", "layout":combined}, '*');
}
function deleteElement(event){
@ -2927,9 +3037,16 @@
ele.y = parseFloat(compute.top)/hh*100;
try {
ele.z = parseInt(eles[i].zIndex) || 0;
ele.slot = parseInt(eles[i].slot) || i;
ele.cover = eles[i].cover || false;
ele.zIndex = parseInt(eles[i].zIndex) || 0;
if ("cover" in eles[i]){
ele.cover = eles[i].cover || false;
} else {
ele.cover = true;
}
//ele.backgroundColor = eles[i].backgroundColor || "#000";
ele.borderThickness = parseInt(eles[i].borderThickness) || 0;
@ -2943,37 +3060,9 @@
} catch(e){errorlog(e);}
layout.push(ele);
}
var combined = {};
for (var i=0;i<layout.length;i++){
//layout[i].z = i;
if (!layout[i]){continue;}
if (!("slot" in layout[i])){
//console.log("no slot");
//if (!(null in combined)){
// combined[null] = [];
//}
//combined[null].push(layout[i]);
continue;
}
try {
var stream = document.querySelector(".thing[data-slot='"+(parseInt(layout[i].slot)+1)+"'");
} catch(e){
errorlog(e);
continue;
}
console.log(stream);
console.log(i);
console.log(layout);
if (!stream){
if (layout[i].defaultStreamID){
if (!document.querySelector(".thing[data-slot][data-sid='"+layout[i].defaultStreamID+"']")){
combined[layout[i].defaultStreamID] = layout[i];
}
}
continue;
}
combined[stream.dataset.sid] = layout[i];
}
var combined = combinedLayout(layout);
prompt("Layout as URL-encoded JSON. StreamIDs are based on current and default values.", encodeURIComponent(JSON.stringify(combined)));
}
@ -2994,8 +3083,14 @@
ele.y = parseFloat(eles[i].style.top)/hh*100;
try {
ele.slot = parseInt(eles[i].slot) || i;
ele.cover = eles[i].cover || false;
if ("slot" in eles[i]){
ele.slot = parseInt(eles[i].slot);
}
if ("cover" in eles[i]){
ele.cover = eles[i].cover || false;
} else {
ele.cover = true;
}
ele.zIndex = parseInt(eles[i].zIndex) || 0;
//ele.backgroundColor = eles[i].backgroundColor || "#000";
@ -3054,11 +3149,14 @@
saveSession();
}
function saveSession(){
function saveSession(){
var layouts = document.querySelectorAll(".canvasContainer>canvas");
savedSession.layouts = [];
savedSession.settings = {};
savedSession.settings.updateOnSlotChange = updateOnSlotChange;
savedSession.settings.assignSlotToGuest = assignSlotToGuest;
savedSession.settings.toggleLabel = toggleLabel;
savedSession.settings.toggleBroadcast = toggleBroadcast;
savedSession.version = 1;
for (var i=0;i<layouts.length;i++){
@ -3070,6 +3168,10 @@
}
}
setStorage("savedSession", JSON.stringify(savedSession));
if (iframe){
iframe.contentWindow.postMessage({ layouts: savedSession.layouts }, "*");
}
console.log(getStorage("savedSession"));
}
@ -3083,25 +3185,21 @@
document.documentElement.style.setProperty('--aspect-ratio', ar);
}
let modal = document.querySelector("#modal");
let closeBtn = document.querySelector(".close-btn");
closeBtn.onclick = function(){
document.getElementById("modal-content").innerHTML = "";
modal.style.display = "none";
}
window.onclick = function(e){
if (e.target == modal){
document.getElementById("modal-content").innerHTML = "";
modal.style.display = "none";
//let modal = document.querySelector("#modal");
document.querySelectorAll(".close-btn").forEach(ele2=>{
ele2.onclick = function(){
document.querySelectorAll(".modal").forEach(ele=>{
ele.classList.add("hidden");
});
}
}
modal.onclick = function(e){
if (e.target == modal){
document.getElementById("modal-content").innerHTML = "";
modal.style.display = "none";
});
document.querySelectorAll(".modal").forEach(ele=>{
ele.onclick = function(e){
if (e.target.classList.contains("modal")){
e.target.classList.add("hidden");
}
}
}
});
if (roomname){
loadIframe();

File diff suppressed because one or more lines are too long