This commit is contained in:
Steve Seguin 2020-11-02 02:40:44 -05:00 committed by GitHub
parent 589d0e4575
commit 84e6565c01
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 3126 additions and 1440 deletions

View File

@ -6,11 +6,16 @@ $("body").append('<style id="lightbox-animations" type="text/css"></style>');
$(".column").on('click', function() {
/* The position of the container will be set to fixed, so set the top & left properties of the container */
if ( $(this).hasClass( "skip-animation" )){
return;
}
var bounding_box = $(this).get(0).getBoundingClientRect();
$(this).css({ top: bounding_box.top + 'px', left: bounding_box.left -20+ 'px' });
/* Set container to fixed position. Add animation */
$(this).addClass('in-animation');
$(this).addClass('in-animation').removeClass('pointer');
/* An empty container has to be added in place of the lightbox container so that the elements below don't come up
Dimensions of this empty container is the same as the original container */
@ -91,8 +96,8 @@ if(e.originalEvent.animationName == 'inlightbox') {
/* On animation end from full-screen to normal */
else if(e.originalEvent.animationName == 'outlightbox') {
/* Remove fixed positioning, remove animation rules */
$(this).removeClass('in-animation').removeClass('out-animation').removeClass('columnfade');
$(this).removeClass('in-animation').removeClass('out-animation').removeClass('columnfade').addClass('pointer');
/* Remove the empty container that was earlier added */
$("#empty-container").remove();

View File

@ -50,6 +50,12 @@ button{
transform-origin: 0 0;
}
.gone {
position: absolute;
display:inline-block;
left: -9999px;
}
</style>
</head>
<body>
@ -69,17 +75,35 @@ function copyFunction(copyText) {
}
function generateStreamID(){
var text = "";
var possible = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz23456789";
for (var i = 0; i < 7; i++){
text += possible.charAt(Math.floor(Math.random() * possible.length));
var text = "";
var possible = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz23456789";
for (var i = 0; i < 7; i++){
text += possible.charAt(Math.floor(Math.random() * possible.length));
}
console.log(text);
return text;
};
function toHexString(byteArray){
return Array.prototype.map.call(byteArray, function(byte){
return ('0' + (byte & 0xFF).toString(16)).slice(-2);
}).join('');
}
generateHash = function (str, length=false){
var buffer = new TextEncoder("utf-8").encode(str);
return crypto.subtle.digest("SHA-256", buffer).then(
function (hash) {
hash = new Uint8Array(hash);
if (length){
hash = hash.slice(0, parseInt(parseInt(length)/2));
}
hash = toHexString(hash);
return hash;
}
console.log(text);
return text;
};
);
};
function generateInvite(){
var title = encodeURI(getById("videoname").value);
var title = encodeURI(getById("videoname").value.replace(/[\W]+/g,"_"));
if (title.length){
title = "&label="+title;
}
@ -111,16 +135,10 @@ function generateInvite(){
}
if (getById("invite_joinroom").value.trim().length){
sendstr+="&room="+getById("invite_joinroom").value.trim();
viewstr+="&scene=1&room="+getById("invite_joinroom").value.trim();
sendstr+="&room="+getById("invite_joinroom").value.replace(/[\W]+/g,"_");
viewstr+="&scene&room="+getById("invite_joinroom").value.replace(/[\W]+/g,"_");
}
if (getById("invite_password").value.trim().length){
sendstr+="&password";
viewstr+="&password="+getById("invite_password").value.trim();
}
if (getById("invite_group_chat_type").value){ // 0 is default
if (getById("invite_group_chat_type").value==1){ // no video
sendstr+="&novideo";
@ -142,13 +160,31 @@ function generateInvite(){
var href = window.location.href;
var dir = href.substring(0, href.lastIndexOf('/')) + "/";
sendstr = dir+'?push=' + sid + sendstr;
viewstr = dir+'?view=' + sid + viewstr + title;
getById("container-setup").style.display="none";
getById("container-links").style.display="block";
var salt = location.hostname; // "obs.ninja" is the expected default. You will want to change this if hosting dock.html locally.
if (getById("invite_password").value.trim().length){
generateHash(getById("invite_password").value.trim().replace(/[\W]+/g,"_")+salt,4).then(function(hash){
sendstr+="&hash="+hash;
viewstr+="&password="+getById("invite_password").value.trim();
sendstr = dir+'?push=' + sid + sendstr;
viewstr = dir+'?view=' + sid + viewstr + title;
getById("container-setup").style.display="none";
getById("container-links").style.display="block";
getById("guest-link").value = sendstr;
getById("obs-link").value = viewstr;
});
} else {
sendstr = dir+'?push=' + sid + sendstr;
viewstr = dir+'?view=' + sid + viewstr + title;
getById("container-setup").style.display="none";
getById("container-links").style.display="block";
getById("guest-link").value = sendstr;
getById("obs-link").value = viewstr;
}
getById("guest-link").value = sendstr;
getById("obs-link").value = viewstr;
}
function goBack(){
@ -158,10 +194,13 @@ function goBack(){
document.addEventListener("dragstart", event => {
var url = event.target.href || event.target.value;
if (!url || !url.startsWith('https://')) return;
if (event.target.dataset.drag!="1"){
return;
}
//event.target.ondragend = function(){event.target.blur();}
var streamId = url.split('view=');
var label = url.split('label=');
@ -173,8 +212,12 @@ document.addEventListener("dragstart", event => {
url += '&layer-width=1920'; // this isn't always 100% correct, as the resolution can fluxuate, but it is probably good enough
url += '&layer-height=1080';
console.warn(url);
event.dataTransfer.setDragImage(document.querySelector('#dragImage'), 24, 24);
event.dataTransfer.setData("text/uri-list", encodeURI(url));
//event.dataTransfer.setData("url", encodeURI(url));
//warnlog(event);
});
</script>
@ -248,5 +291,9 @@ document.addEventListener("dragstart", event => {
<i>(links are draggable)</i>
</div>
</div>
<div class="gone" >
<!-- This image is used when dragging elements -->
<img src="./images/favicon-32x32.png" id="dragImage" />
</div>
</body>
</html>

View File

@ -1,4 +1,5 @@
<html>
<meta charset="UTF-8">
<head><style>
html {
border:0;
@ -108,11 +109,23 @@ input[type='checkbox']:checked {
max-width:486px
}
}
#messageDiv {
font-size: .7em;
color: #DDD;
transition: all 0.5s linear;
font-style: italic;
opacity: 0;
text-align: center;
margin: 10px 0;
}
</style></head>
<body >
<div id="header" style="-webkit-app-region: drag;color:white;font-size:2em">OBS.Ninja</div>
<div class="formcss" >
<div id='warning4mac' style="border:2px dotted; display:none;max-width:700px; padding:10px; margin:0 90px 20px 90px;color:white;font-size:1.3em"> 🚨 If using OBS v26 on macOS, right-click the Electron Capture app and disable <i>Always-on-Top</i> to reveal it during window-selection. You can enable it again afterwards.</div>
<input type="checkbox" class="check" id="prefervp9" name="prefervp9" value="false" onclick="modURL(this);">
<label for="prefervp9">Force VP9 Codec</label>
@ -129,7 +142,8 @@ input[type='checkbox']:checked {
<input type="checkbox" class="check" id="buffer" name="buffer" value="false" onclick="modURL(this);">
<label for="buffer">Lip-sync Fix</label>
<br><br><br>
<br>
<div id="messageDiv" style='display:block'><br /></div>
<div class="formcss"><center>
<input type="text" id="changeText" class="inputfield" value="http://obs.ninja/?view=" onchange="modURL" onkeyup="enterPressed(event, gohere);" />
<button onclick="gohere();" id="gobutton">GO</button>
@ -150,6 +164,10 @@ input[type='checkbox']:checked {
*
*/
if (navigator.userAgent.indexOf('Mac OS X') != -1){
document.getElementById("warning4mac").style.display="block";
}
var audioOutputSelect = document.querySelector('select#audioOutput');
audioOutputSelect.disabled = !('sinkId' in HTMLMediaElement.prototype);
audioOutputSelect.onclick = getPermssions;
@ -178,7 +196,12 @@ function getPermssions(e=null){
listed=true;
audioOutputSelect.focus();
}).catch(function(){alert("Failed to list available output devices\n\nPlease ensure you allowed the microphone permissions.");});
}).catch(function(){
document.getElementById("messageDiv").innerHTML = "Failed to list available output devices\n\nPlease ensure you allowed the microphone permissions.";
document.getElementById("messageDiv").style.display="block";
setTimeout(function(){document.getElementById("messageDiv").style.opacity="1.0";},0);
});
}
function gotDevices(deviceInfos) {
@ -310,10 +333,13 @@ function modURL(ele=false){
if (ele.id =="stereo"){
if (document.getElementById("stereo").checked){
url=updateURLParameter(url, "stereo", "");
alert('Audio bitrate increased to 256kbps.\n\nPlease note: the Video Publisher must also have the stereo flag enabled for stereo to work.');
url=updateURLParameter(url, "proaudio", "");
document.getElementById("messageDiv").innerHTML = "Audio bitrate increased to 256-kbps.\n\nPlease note that the Sender must also have the <b>&proaudio</b> flag added for full-effect";
document.getElementById("messageDiv").style.display="block";
setTimeout(function(){document.getElementById("messageDiv").style.opacity="1.0";},0);
} else {
url=updateURLParameter(url, "stereo", false);
url=updateURLParameter(url, "proaudio", false);
setTimeout(function(){document.getElementById("messageDiv").style.opacity="0";},0);
}
}

View File

@ -80,7 +80,7 @@ function loadIframe(){
var button = document.createElement("button");
button.innerHTML = "Mute Speaker";
button.onclick = function(){iframe.contentWindow.postMessage({"mute":true}, '*');};
button.onclick = function(){iframe.contentWindow.postMessage({"mute":true}, '*');}; // "speaker" also works in the same way.
iframeContainer.appendChild(button);
var button = document.createElement("button");
@ -90,7 +90,7 @@ function loadIframe(){
var button = document.createElement("button");
button.innerHTML = "Toggle Speaker";
button.onclick = function(){iframe.contentWindow.postMessage({"mute":"toggle"}, '*');}
button.onclick = function(){iframe.contentWindow.postMessage({"mute":"toggle"}, '*');}; // open to a better suggestion here.
iframeContainer.appendChild(button);
var button = document.createElement("button");
@ -148,6 +148,15 @@ function loadIframe(){
button.onclick = function(){iframe.contentWindow.postMessage({"getStats":true}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Request Loudness Levels";
button.onclick = function(){iframe.contentWindow.postMessage({"getLoudness":true}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Stop Sending Loudness Levels";
button.onclick = function(){iframe.contentWindow.postMessage({"getLoudness":false}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Say Hello";
@ -178,7 +187,7 @@ function loadIframe(){
if (e.source != iframe.contentWindow){return} // reject messages send from other iframes
if ("stats" in e.data){
var outputWindow = document.createElement("outputWindow");
var outputWindow = document.createElement("div");
console.log(e.data.stats);
@ -195,11 +204,38 @@ function loadIframe(){
}
if ("gotChat" in e.data){
var outputWindow = document.createElement("outputWindow");
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);
}
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";
}
});
}

View File

@ -47,14 +47,14 @@
<meta property="twitter:image" content="./images/obsNinja_logo_full.png" />
<meta name="msapplication-TileColor" content="#da532c" />
<meta name="theme-color" content="#ffffff" />
<!-- <script src="//console.re/connector.js" data-channel="obsninjaalpha" type="text/javascript" id="consolerescript"></script>-->
<!--<script src="//console.re/connector.js" data-channel="obsninjadev" type="text/javascript" id="consolerescript"></script>-->
<link rel="stylesheet" href="./lineawesome/css/line-awesome.min.css">
<script type="text/javascript" crossorigin="anonymous" src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
<script type="text/javascript" crossorigin="anonymous" src="./thirdparty/qrcode.min.js"></script>
<script type="text/javascript" src="./thirdparty/jquery.min.js"></script>
<link rel="stylesheet" href="./main.css?ver=11" />
<link rel="stylesheet" href="./main.css?ver=18" />
</head>
<body id="main" >
<body id="main" class="hidden">
<span itemprop="image" itemscope itemtype="image/png">
<link itemprop="url" href="./images/obsNinja_logo_full.png" />
</span>
@ -62,10 +62,10 @@
<span itemprop="thumbnail" itemscope itemtype="http://schema.org/ImageObject">
<link itemprop="url" href="./images/obsNinja_logo_full.png" />
</span>
<script type="text/javascript" crossorigin="anonymous" src="./thirdparty/CodecsHandler.js?ver=19"></script>
<script type="text/javascript" crossorigin="anonymous" src="./webrtc.js?ver=60"></script>
<script type="text/javascript" crossorigin="anonymous" src="./thirdparty/CodecsHandler.js?ver=22"></script>
<script type="text/javascript" crossorigin="anonymous" src="./webrtc.js?ver=110"></script>
<input id="zoomSlider" type="range" style="display: none;" />
<div id="header"">
<div id="header">
<a id="logoname" href="./" style="text-decoration: none; color: white; margin: 2px;">
<span data-translate="logo-header">
<font id="qos">O</font>BS.Ninja
@ -73,26 +73,26 @@
</a>
<div id="head1" style="display: inline-block; padding:1px; position: relative;">
<input id="joinroomID" name="joinroomID" size="22" placeholder="Join by Room Name here" />
<button style="padding:1px;margin:0;" onclick="jumptoroom();">GO</button>
<button onclick="jumptoroom();">GO</button>
</div>
<div id="head3" style="display: inline-block;" class="advanced">
<font style="color: #888;" id="copythisurl"> &nbsp;
<span data-translate="copy-this-url">Copy this URL into an OBS "Browser Source"</span> <i style="color: #CCC;" class="las la-long-arrow-alt-right"></i> &nbsp;
</font>
<input
<a
id="reshare"
data-drag="1"
onclick="popupMessage(event);copyFunction(this)"
class="task"
class="task grabLinks"
onmousedown="copyFunction(this)"
style="font-weight: bold; color: #afa; cursor: grab; background-color: #0000; font-size: 115%; min-width: 335px; max-width: 800px;"
/>
<i class="las la-paperclip task" style="color: #DDD;" onclick="popupMessage(event);copyFunction(document.getElementById('reshare'));" onmouseover="this.style.cursor='pointer'"></i>
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>
</div>
<div id="head4" style="display: inline-block;" class="advanced">
<font style="font-size: 68%; color: white;">
&nbsp;
<span data-translate="you-are-in-the-control-center">You are in the room's control center</span>:&nbsp;
<span data-translate="you-are-in-the-control-center">Control center for room:</span>
<div id="dirroomid" style="font-size: 140%; color: #99c; display: inline-block;"></div>
</font>
@ -109,6 +109,9 @@
<i id="chattoggle" class="toggleSize las la-comment-alt my-float"></i>
<div id="chatNotification"></div>
</div>
<div id="mutespeakerbutton" title="Mute the Speaker" onclick="toggleSpeakerMute()" class="advanced float" style="cursor: pointer;" alt="Toggle the speaker output">
<i id="mutespeakertoggle" class="toggleSize las la-volume-up my-float" style="position: relative; top: 0.5px;"></i>
</div>
<div id="mutebutton" title="Mute the Mic" onclick="toggleMute()" class="advanced float" style="cursor: pointer;" alt="Toggle the mic">
<i id="mutetoggle" class="toggleSize las la-microphone my-float" style="position: relative; top: 0.5px;"></i>
</div>
@ -126,58 +129,62 @@
<span
id="helpbutton"
title="Show Help Info"
onclick="alert('Email steve@seguin.email if the system breaks or check https://reddit.com/r/obsninja for support.\n\nThere are some advanced options hidden away, such as persistent stream links and custom resolutions; see the Wiki.')"
onclick="alert('Email steve@seguin.email if the system breaks or check https://reddit.com/r/obsninja for support.\n\nThe Wiki contains many help guides and advanced settings.\n\nAccess the debug menu by pressing CTRL (command) and Left-Clicking on a video.\n\nMost issues can be fixed by using Wired Internet instead of Wi-Fi.')"
style="cursor: pointer; display:none;"
alt="How to Use This with OBS"
>
<i class="las la-question-circle " style="float: right; bottom: 0px; cursor: pointer; position: fixed; right: 24px; color: #d9e4eb; padding: 2px; margin: 2px 2px 0 0; font-size: 140%;"></i>
<i style="float: right; bottom: 0px; cursor: pointer; position: fixed; right: 24px; color: #d9e4eb; padding: 2px; margin: 2px 2px 0 0; font-size: 140%;" class="las la-question-circle" aria-hidden="true"></i>
</span>
<span title="Language Options" onclick="toggle(document.getElementById('languages'));" id="translateButton">
<i style="float: right; bottom: 0px; cursor: pointer; position: fixed; right: 2px; color: #d9e4eb; padding: 2px; margin: 2px 2px 0 0; font-size: 140%;" class="las la-language" aria-hidden="true"></i>
<i style="float: right; bottom: 0px; cursor: pointer; position: fixed; right: 2px; color: #d9e4eb; padding: 2px; margin: 2px 2px 0 0; font-size: 140%;" class="las la-language" aria-hidden="true"></i>
</span>
<div id="mainmenu" class="row" style="opacity: 0; align: center;">
<div id="container-1" class="column columnfade" style=" overflow-y: auto;">
<div id="container-1" class="column columnfade pointer" style=" overflow-y: auto;">
<h2>
<span data-translate="add-group-chat">Add Group Chat to OBS</span>
</h2>
<div class="container-inner">
<br />
<br />
<span data-translate="rooms-allow-for">Rooms allow for simplified group-chat and the advanced management of multiple streams at once.</span>
<span data-translate="rooms-allow-for">Rooms allow for group-chat and the tools to manage multiple guests.</span>
<br />
<br />
<p>
<b>
<table>
<tr>
<th><b>
<span data-translate="room-name">Room Name</span>:
</b>
<input id="videoname1" placeholder="Enter a Room Name here" onkeyup="enterPressed(event, createRoom);" size="30" maxlength="50" style="font-size: 110%; padding: 5px;" />
<br />
<br />
</p>
<p>
<b>
</b></th>
<th><input id="videoname1" placeholder="Enter a Room Name here" onkeyup="enterPressed(event, createRoom);" size="30" maxlength="30" style="font-size: 110%; padding: 5px;" /></th>
</tr><tr>
<th><b>
<span data-translate="password-input-field">Password</span>:
</b>
<input id="passwordRoom" placeholder="Optional room password here" onkeyup="enterPressed(event, createRoom);" size="30" maxlength="50" style="font-size: 110%; padding: 5px;" />
</th><th>
<input id="passwordRoom" placeholder="Optional room password here" onkeyup="enterPressed(event, createRoom);" size="30" maxlength="30" style="font-size: 110%; padding: 5px;" />
</th>
</tr><tr><th></th><th>
<br />
<br />
</p>
<button onclick="createRoom()" class="gobutton" style="float: left;">
<span data-translate="enter-the-rooms-control">Enter the Room's Control Center</span>
</button>
<br /><br />
<button class="white" style="display: block;" onclick="toggle(document.getElementById('roomnotes'),this);">
<span data-translate="show-tips">Show me some tips..</span>
</button>
</th></tr>
</table>
<br />
<button onclick="createRoom()" class="gobutton">
<span data-translate="enter-the-rooms-control">Enter the Room's Control Center</span>
</button>
<br />
<button onclick="toggle(document.getElementById('roomnotes'),this);">
<span data-translate="show-tips">Show me some tips..</span>
</button><br />
<ul style=" margin: auto auto; max-width: 500px; display: none; text-align: left;" id="roomnotes">
<br />
<span data-translate="added-notes">
<u>
<i>Important Notes:</i><br /><br />
<i>Important Tips:</i><br /><br />
</u>
<li>Invite only guests to the room that you trust.</li>
<li>iOS devices will share just their audio with other room guests; not video. This is intended as a way to avoid a hardware limitation with iOS devices.</li>
<li>iOS devices will share just their audio with other room guests; not video. This is intentional.</li>
<li>The "Recording" option is considered experimental.</li>
</span>
</ul>
@ -190,7 +197,7 @@
</div>
</div>
</div>
<div id="container-3" class="column columnfade" onclick="previewWebcam()" style=" overflow-y: auto;">
<div id="container-3" class="column columnfade pointer" onclick="previewWebcam()" style=" overflow-y: auto;">
<h2 id="add_camera">
<span data-translate="add-your-camera">Add your Camera to OBS</span>
</h2>
@ -200,7 +207,7 @@
<video id="previewWebcam" class="previewWebcam" oncanplay="updateStats();" disablePictureInPicture controlsList="nodownload" muted autoplay playsinline ></video>
</p>
<div id="infof"></div>
<button onclick="publishWebcam()" id="gowebcam" class="gowebcam" disabled>
<button onclick="publishWebcam(this)" id="gowebcam" class="gowebcam" disabled data-ready="false" >
<span data-translate="waiting-for-camera">Waiting for Camera to Load</span>
</button>
<br />
@ -255,7 +262,7 @@
<br />
<span id="headphonesDiv" style="text-align:left; margin:17px 0; max-width: 550px; min-width: 420px; background-color: #f3f3f3; display: none; padding: 10px 10px; border: 1px solid #ccc; vertical-align: middle;">
<div class="audioTitle2">
<i class="las la-headphones"></i><span data-translate="select-output-source"> Audio Output Destination: <button onclick="playtone()" style="margin:0 0 0 15px;" type="button">Test</button>
<i class="las la-headphones"></i><span data-translate="select-output-source"> Audio Output Destination: <button onclick="playtone()" class="white" style="margin:0 0 0 15px;" type="button">Test</button>
</span></div>
<select id="outputSource" ></select>
@ -271,7 +278,7 @@
</div>
</div>
<div id="container-2" class="column columnfade" style=" overflow-y: auto;">
<div id="container-2" class="column columnfade pointer" style=" overflow-y: auto;">
<h2 id="add_screen">
<span data-translate="remote-screenshare-obs">Remote Screenshare into OBS</span>
</h2>
@ -327,7 +334,7 @@
<span id="headphonesDiv2" style="background-color: #f3f3f3; min-width: 270px; display: none; padding: 5px 10px; border: 1px solid #ccc; vertical-align: middle;">
<i class="las la-headphones"></i>
<span data-translate="select-output-source"> Audio Output Destination: <button onclick="playtone(true)" style="padding:2px 5px; margin:0;margin-left:15px; position: relative; top: -2px;" type="button">Test</button></span>
<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();">
<option value="default">
@ -344,7 +351,7 @@
</div>
</div>
</div>
<div id="container-4" class="column columnfade" style=" overflow-y: auto;">
<div id="container-4" class="column columnfade pointer" style=" overflow-y: auto;">
<h2>
<span data-translate="create-reusable-invite">Create Reusable Invite</span>
</h2>
@ -372,19 +379,19 @@
<div class="invite_setting_item">
<input type="checkbox" id="invite_bitrate" />
<label for="invite_bitrate">
<span data-translate="unlock-video-bitrate">Unlock Video Bitrate (20mbps)</span>
<span data-translate="unlock-video-bitrate" title="Ideal for 1080p60 gaming, if your computer and upload are up for it" >Unlock Video Bitrate (20mbps)</span>
</label>
</div>
<div class="invite_setting_item">
<input type="checkbox" id="invite_vp9" />
<label for="invite_vp9">
<span data-translate="force-vp9-video-codec">Force VP9 Video Codec (less artifacting)</span>
<span data-translate="force-vp9-video-codec" title="Better video compression and quality at the cost of increased CPU encoding load">Force VP9 Video Codec</span>
</label>
</div>
<div class="invite_setting_item">
<input type="checkbox" id="invite_stereo" />
<label for="invite_stereo">
<span data-translate="enable-stereo-and-pro">Enable Stereo and Pro HD Audio</span>
<span data-translate="enable-stereo-and-pro" title="Disable digital audio-effects and increase audio bitrate">Enable Stereo and Pro HD Audio</span>
</label>
</div>
<div class="invite_setting_item">
@ -399,33 +406,33 @@
</div>
<div class="invite_setting_group">
<div class="invite_setting_item">
<input type="checkbox" id="invite_secure" />
<label for="invite_secure">
<span data-translate="high-security-mode">High Security Mode</span>
<input type="checkbox" id="invite_automic" />
<label for="invite_automic">
<span data-translate="hide-mic-selection" title="The guest will not have a choice over audio-options">Force Default Microphone</span>
</label>
</div>
<div class="invite_setting_item">
<input type="checkbox" id="invite_hidescreen" />
<label for="invite_hidescreen">
<span data-translate="hide-screen-share">Hide Screenshare Option</span>
<span data-translate="hide-screen-share" title="The guest will only be able to select their webcam as an option">Hide Screenshare Option</span>
</label>
</div>
<div class="invite_setting_item">
<input type="checkbox" id="invite_remotecontrol" />
<label for="invite_remotecontrol">
<span data-translate="allow-remote-control">Remote Control Camera Zoom (android)</span>
<span data-translate="allow-remote-control" title="Hold CTRL and the mouse wheel to zoom in and out remotely of compatible video streams">Remote Control Camera Zoom (android)</span>
</label>
</div>
<div class="invite_setting_item">
<span data-translate="add-a-password-to-stream"> Add a password:</span>
<span data-translate="add-a-password-to-stream" title="Add a password to make the stream inaccessible to those without the password"> Add a password:</span>
<input id="invite_password" placeholder="Add an optional password" />
</div>
<div class="invite_setting_item">
<span data-translate="add-the-guest-to-a-room"> Add the guest to a room:</span>
<span data-translate="add-the-guest-to-a-room" title="Add the guest to a group-chat room; it will be created automatically if needed."> Add the guest to a room:</span>
<input id="invite_joinroom" placeholder="Enter Room name here" oninput="document.getElementById('invitegroupchat').style.display='block';" />
</div>
<div class="invite_setting_item">
<span id="invitegroupchat" style="display: none;">
<span id="invitegroupchat" style="display: none;" title="Customize the room settings for this guest">
<label for="invite_group_chat_type" data-translate="invite-group-chat-type">This room guest can:</label>
<select id="invite_group_chat_type" name="invite_group_chat_type">
<option value="0" selected data-translate="can-see-and-hear">Can see and hear the group chat</option>
@ -448,6 +455,24 @@
</div>
</div>
</div>
<div id="container-5" class="column columnfade pointer advanced" style=" overflow-y: auto;">
<h2><span data-translate="share-local-video-file">Stream Media File</span></h2>
<div class="container-inner">
<br /><br />
SELECT THE VIDEO FILE TO SHARE<br /><br />
<input id="fileselector" onchange="session.publishFile(this,event);" type="file" accept="video/*,audio/*"/>
</div>
<div class="outer close">
<div class="inner">
<label class="labelclass">
<span data-translate="back">Back</span>
</label>
</div>
</div>
</div>
<p></p>
<div id="info" class="fullcolumn columnfade">
<center>
@ -471,21 +496,20 @@
</i>
<br />
<li>
<a href="https://github.com/steveseguin/obsninja/wiki/FAQ#mac-os">MacOS <i class="lab la-apple"></i> users</a> need to use OBS v23 or resort to
<a href="https://github.com/steveseguin/electroncapture">Window Capturing</a> a browser with OBS v25
<a href="https://github.com/steveseguin/obsninja/wiki/FAQ#mac-os">MacOS <i class="lab la-apple"></i> users</a> will need to use OBS v23 or resort to
<a href="https://github.com/steveseguin/electroncapture">Window Capturing</a> with the provided Electron-Capture app for the time being.
</li>
<li>Some users will have
<a href="https://github.com/steveseguin/obsninja/wiki/FAQ#video-is-pixelated">"pixel smearing"</a> problems with videos. Avoid Wi-Fi to reduce it or add
<b>&codec=vp9</b> to the OBS view links to prevent it.
<li>If you have <a href="https://github.com/steveseguin/obsninja/wiki/FAQ#video-is-pixelated">"pixel smearing"</a> or corrupted video, try adding <b>&codec=vp9</b> or &codec=h264 to the OBS view link. Using Wi-Fi will make the issue worse.
</li>
<li>
iOS devices may have occasional audio issues, such as no sound. Fully close Safari and reopen it if nothing else seems to work.
</li>
<br />
🚨 Site Recently Updated: September 12th, 2020. The previous version can be found at
<a href="https://obs.ninja/v11/">https://obs.ninja/v11/</a> if you are having new issues.
🎈 Site Updated: <a href="https://www.reddit.com/r/OBSNinja/comments/jik5fk/version_13_released_change_log/">November 2nd, 2020</a>. The previous version can be found at
<a href="https://obs.ninja/v12/">https://obs.ninja/v12/</a> if you are having new issues.
<br />
<br />
<h3>
@ -493,7 +517,7 @@
Check out the
<a href="https://www.reddit.com/r/OBSNinja/">sub-reddit
<i class="lab la-reddit-alien"></i> </a>for help and see the <a href="https://github.com/steveseguin/obsninja/wiki/">Wiki for advanced info</a>. I'm also on
<a href="https://discord.gg/EksyhGA">Discord</a> and you can email me at steve@seguin.email
<a href="https://discord.gg/T4xpQVv">Discord</a> and you can email me at steve@seguin.email
</i>
</h3>
@ -518,31 +542,62 @@
<span id="electronDragZone" style="pointer-events: none; z-index:-10; position:absolute;top:0;left:0;width:100%;height:20%;-webkit-app-region: drag;"></span>
<div id="gridlayout" ></div>
<div id="controls_blank" style="display: none;">
<b>
<button data-value="0" style="padding: 2px 10px 3px 5px;font-weight:display-block;margin: 2px 0 0 5px;" title="Add this Video to any remote '&scene=1'" onclick="directEnable(this, event);">
<span data-translate="add-to-group" view"> Add to Group Scene</span>
<div class="controlsGrid">
<button data-action-type="addToScene" data-value="0" title="Add this Video to any remote '&scene=1'" onclick="directEnable(this, event);">
<i class="las la-plus-square"></i>
<span data-translate="add-to-scene">Add to Scene</span>
</button>
<button style="padding: 2px 10px 3px 5px;display:inline-block;margin: 2px 0 0 5px;" title="Start Recording this stream. *experimental*' views" onclick="recordVideo(this, event)">
<span data-translate="record">🔴 Record</span>
<button data-action-type="forward" data-value="0" title="Forward user to another room. They can always return." onclick="directMigrate(this, event);">
<i class="las la-paper-plane"></i>
<span data-translate="forward-to-room">Transfer</span>
</button>
<br />
<button style="padding: 2px 10px 3px 5px;display:inline-block;margin: 2px 0 0 5px;" title="Remotely Mute this Audio in all remote '&scene' views" onclick="directMute(this, event);">
<span data-translate="mute" >🔇 Mute in all Scenes</span>
<button data-action-type="recorder" title="Start Recording this stream. *experimental*' views" onclick="recordVideo(this, event)">
<i class="las la-circle"></i>
<span data-translate="record"> Record</span>
</button>
<i style="font-size:2vh; position: relative; top: 3px;" title="Change this Audio's volume in all remote '&scene' views" class="las la-volume-up"></i>
<input type="range" min="1" max="100" value="100" title="Change this Audio's volume in all remote '&scene' views" onclick="directVolume(this);" style="width: 100px; position: relative; top: 4px;"/>
</b>
<br />
<hr style="margin:5px 0 0 0;"/>
<button data-action-type="hangup" data-value="0" title="Force the user to Disconnect. They can always reconnect." onclick="directHangup(this, event);">
<i class="las la-sign-out-alt"></i>
<span data-translate="disconnect-guest" >Hangup</span>
</button>
<!--
<span>
<button data-action-type="change-quality" title="Change Video Bitrate" onclick="session.requestRateLimit(50, this.dataset.UUID);">
<i class="las la-circle"></i>
<span data-translate="change-to-low-quality"> O</span>
</button>
<button data-action-type="change-quality" title="Change Video Bitrate" onclick="session.requestRateLimit(35, this.dataset.UUID);">
<i class="las la-circle"></i>
<span data-translate="change-to-medium-quality"> L</span>
</button>
<button data-action-type="change-quality" title="Change Video Bitrate" onclick="session.requestRateLimit(1200, this.dataset.UUID);">
<i class="las la-circle"></i>
<span data-translate="change-to-high-quality"> H</span>
</button>
</span>
<button data-action-type="direct-chat" title="Send Direct Message" onclick="sendChatMessage(this.dataset.UUID);">
<i class="las la-circle"></i>
<span data-translate="send-direct-chat"> Message</span>
</button>
-->
<input data-action-type="volume" class="slider" type="range" min="1" max="100" value="100" title="Change this Audio's volume in all remote '&scene' views" onclick="directVolume(this);" style="grid-column: 1; margin:5px; width: 90%; position: relative; top: 0px;"/>
<button data-action-type="mute" style="grid-column: 2;" title="Remotely Mute this Audio in all remote '&scene' views" onclick="directMute(this, event);">
<i class="las la-volume-off"></i>
<span data-translate="mute" >Mute in Scenes</span>
</button>
</div>
</div>
<div id="popupSelector" style="display:none;">
<span id="videoMenu3" class="videoMenu">
<i class="las la-video"></i><span data-translate="video-source"> Video Source </span>
<select id="videoSource3" ></select>
<select id="videoSource3" ></select>
</span>
<br />
<br />
@ -566,7 +621,7 @@
</div>
<select id="outputSource3" ></select>
</span>
<button id="shareScreenGear" style="padding:20px;" onclick="grabScreen()"><b>Share Screen</b><br /><i style="padding:5px; font-size:300%;" class="las la-desktop"></i></button><br />
<button id="shareScreenGear" style="padding:20px;text-align:center;" onclick="grabScreen()"><b>Share Screen</b><br /><i style="padding:5px; font-size:300%;" class="las la-desktop"></i></button><br />
<button onclick="toggleSettings()" style="background-color:#EFEFEF;padding:10px 12px 12px 2px;"><i class="chevron right" style="font-size:150%;top:3px;position:relative;"></i> <b>Close Settings</b></button>
</p>
</div>
@ -587,6 +642,49 @@
</ul>
</nav>
<div id="roomTemplate" style="display:none;">
<div class='directorContainer half'>
<button class="grey" data-translate="click-for-quick-room-overview" onclick="toggle(getById('roomnotes2'),this,false);"><i class="las la-question-circle"></i> Click Here for a quick overview and help</button>
<span id="miniPerformer"><button id="press2talk" class="grey" onclick="press2talk();"><i class="las la-headset"></i><span data-translate="push-to-talk-enable"> Enable Director's Push-to-Talk Mode</span></button></span>
</div>
<div id='roomnotes2' style='max-width:1200px;display:none;padding:0 0 0 10px;' >
<font style='color:#CCC;' data-translate='welcome-to-control-room'>
<b>Welcome. This is the director's control-room for the group-chat.</b><br /><br />
You can host a group chat with friends using a room. Share the blue link to invite guests who will join the chat automatically.
<br /><br />
<font style='color:red'>Known Limitations with Group Rooms:</font><br />
<li>A group room can handle up to around 30 guests, depending on numerous factors, including CPU and available bandwidth of all guests in the room. To achieve more than around 7-guests though, you will likely want to disable video sharing between guests. &roombitrate=0 or &novideo are options there.</li>
<li>Videos will appear of low quality on purpose for guests and director; this is to save bandwidth and CPU resources. It will be high-quality within OBS still though.</li>
<li>The state of the scenes, such as which videos are active in a scene, are lost when the director resets the control-room or the scene.</li>
<br />
Further Notes:<br /><br />
<li>Links to Solo-views of each guest video are offered under videos as they load. These can be used within an OBS Browser Source.</li>
<li>You can use the auto-mixing Group Scenes, the green links, to auto arrange multiple videos for you in OBS.</li>
<li>You can use this control room to record isolated video or audio streams, but it is an experimental feature still.</li>
<li>If you transfer a guest from one room to another, they won't know which room they have been transferred to.</li>
<li>OBS will see a guest's video in high-quality; the default video bitrate is 2500kbps. Setting higher bitrates will improve motion.</li>
<li>VP8 is typically the default video codec, but using &codec=vp9 or &codec=h264 as a URL in OBS can help to reduce corrupted video puke issues.</li>
<li>&stereo=2 can be added to guests to turn off audio effects, such as echo cancellation and noise-reduction.</li>
<li>https://invite.cam is a free service provided that can help obfuscuate the URL parameters of an invite link given to guests.</li>
<li>Adding &showonly=SOME_OBS_VIRTUALCAM to the guest invite links allows for only a single video to be seen by the guests; this can be output of the OBS Virtual Camera for example</li>
<br />
For advanced URL options and parameters, <a href="https://github.com/steveseguin/obsninja/wiki/Advanced-Settings">see the Wiki.</a>
</font>
</div>
<div id='guestFeeds'><div id='deleteme'>
<div class='vidcon' style='margin: 15px 20px 0 0; min-height: 300px;text-align: center;'><h2>Guest 1</h2><i class='las la-user-circle' style='font-size:8em; margin: 20px 0px;' aria-hidden='true'></i></div>
<div class='vidcon' style='margin: 15px 20px 0 0; min-height: 300px;text-align: center;'><h2>Guest 2</h2><i class='las la-user-circle' style='font-size:8em; margin: 20px 0px;' aria-hidden='true'></i></div>
<div class='vidcon' style='margin: 15px 20px 0 0; min-height: 300px;text-align: center;'><h2>Guest 3</h2><i class='las la-user-circle' style='font-size:8em; margin: 20px 0px;' aria-hidden='true'></i></div>
<div class='vidcon' style='margin: 15px 20px 0 0; min-height: 300px;text-align: center;'><h2>Guest 4</h2><i class='las la-user-circle' style='font-size:8em; margin: 20px 0px;' aria-hidden='true'></i></div>
<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="chatModule" style="display:none;">
<div id="chatBody">
<div class="inMessage" data-translate='welcome-to-obs-ninja-chat'>
@ -596,7 +694,7 @@
Names identifying connected peers will be a feature in an upcoming release.
</div>
</div>
<input id="chatInput" placeholder="Enter chat message to send here"onkeypress="EnterButtonChat(event)" />
<input id="chatInput" placeholder="Enter chat message to send here" onkeypress="EnterButtonChat(event)" />
<button style="width:60px;background-color:#EEE;" onclick="sendChatMessage()" data-translate='send-chat'>Send</button>
</div>
@ -604,7 +702,10 @@
<source src="tone.mp3" type="audio/mpeg">
<source src="tone.ogg" type="audio/ogg">
</audio>
<div class="gone" >
<!-- This image is used when dragging elements -->
<img src="./images/favicon-32x32.png" id="dragImage" />
</div>
<div id="messagePopup" class="popup-message"></div>
<div id="languages" class="popup-message" style="display: none; right: 0; bottom: 25px; position: absolute;">
<b data-translate='available-languages'>Available Languages:</b>
@ -635,12 +736,16 @@
<a href="https://github.com/steveseguin/obsninja/tree/master/translations" data-translate='add-more-here'>Add More Here!</a>
<br />
</div>
<script>
var session = WebRTC.Media; // session is a required global variable if configuring manually. Run before loading main.js but after webrtc.js.
session.version = "13.1";
session.streamID = session.generateStreamID(); // randomly generates a streamID for this session. You can set your own programmatically if needed
session.defaultPassword = "someEncryptionKey123"; // Disabling improves compatibility and is helpful for debugging.
session.salt = location.hostname; // used only if password is not == False.
// session.configuration = {
// iceServers: [
// { urls: ["stun:stun.l.google.com:19302", "stun:stun4.l.google.com:19302"] }, // more than 4 stun+turn servers may cause issues
@ -662,7 +767,7 @@
// session.configuration.iceTransportPolicy = "relay"; // uncomment to enable "&privacy" and force the TURN server
// session.wss = false;
// session.wss = false; // uses default handshake wss
///// The following lets you set the defaults
@ -680,6 +785,7 @@
// session.codec
// session.scale
// session.bitrate // int in kbps
// session.totalRoomBitrate = 500; // int, kbps
// session.height // int
// session.width // int
// session.quality // int
@ -698,7 +804,7 @@
// 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=41"></script>
<script type="text/javascript" crossorigin="anonymous" src="./animations.js?ver=10"></script>
<script type="text/javascript" crossorigin="anonymous" id="main-js" src="./main.js?ver=79"></script>
<script type="text/javascript" crossorigin="anonymous" src="./animations.js?ver=13"></script>
</body>
</html>

1926
main.css

File diff suppressed because it is too large Load Diff

2160
main.js

File diff suppressed because it is too large Load Diff

View File

@ -335,7 +335,7 @@ var CodecsHandler = (function() {
}
if (typeof params.maxaveragebitrate != 'undefined') {
appendOpusNext += ';maxaveragebitrate=' + params.maxaveragebitrate; // default 2500 (kbps)
appendOpusNext += ';maxaveragebitrate=' + params.maxaveragebitrate; // default 32000? (kbps)
}
if (typeof params.maxplaybackrate != 'undefined') {

File diff suppressed because one or more lines are too long