Version 13.4 Release

pushed to production on November 24th
This commit is contained in:
Steve Seguin 2020-11-24 05:25:07 -05:00 committed by GitHub
parent 595b233327
commit d48483f113
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 114865 additions and 132 deletions

View File

@ -4,7 +4,7 @@
body{
padding:0;
margin:0;
background-color: rgb(253,253,253);
background-color: rgb(222,242,253);
}
iframe {
border:0;
@ -33,13 +33,14 @@ button{
</style>
<script>
function loadIframe(){
function loadIframe(){ // this is pretty important if you want to avoid camera permission popup problems. You can also call it automatically via: <body onload=>loadIframe();"> , but don't call it before the page loads.
var iframe = document.createElement("iframe");
var iframeContainer = document.createElement("div");
var iframesrc = document.getElementById("viewlink").value;
iframe.allow="autoplay;camera;microphone";
iframe.allowtransparency="true"
iframe.allowtransparency="true";
iframe.allowfullscreen ="true";
if (iframesrc==""){
iframesrc="./";
@ -108,6 +109,24 @@ function loadIframe(){
button.onclick = function(){iframe.contentWindow.postMessage({"mic":"toggle"}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Mute Camera";
button.onclick = function(){iframe.contentWindow.postMessage({"camera":false}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Unmute Camera";
button.onclick = function(){iframe.contentWindow.postMessage({"camera":true}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Toggle Camera";
button.onclick = function(){iframe.contentWindow.postMessage({"camera":"toggle"}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Disconnect";
button.onclick = function(){iframe.contentWindow.postMessage({"close":true}, '*');};
@ -163,17 +182,56 @@ function loadIframe(){
button.onclick = function(){iframe.contentWindow.postMessage({"sendChat":"Hello!"}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Insert Style Sheet";
var stylesheet = "#main { zoom: 0.5;} video {float: left; margin: 0; padding: 0; } #info {display:none;}";
button.onclick = function(){iframe.contentWindow.postMessage({"style":stylesheet}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "get StreamIDs and labels";
button.onclick = function(){iframe.contentWindow.postMessage({"getStreamIDs":true}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Start AutoMixer";
button.onclick = function(){iframe.contentWindow.postMessage({"automixer":true}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Stop AutoMixer";
button.onclick = function(){iframe.contentWindow.postMessage({"automixer":false}, '*');};
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Add Target Video";
button.onclick = function(){iframe.contentWindow.postMessage({"target":"*", "add":true, "settings":{"style":{"width":"640px", "height":"360px", "float":"left", "border":"10px solid red", "display":"block"}}}, '*');}; // target can be a stream ID or * for all.
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Remove Target Video";
button.onclick = function(){iframe.contentWindow.postMessage({"target":"*", "remove": true}, '*');}; // target can be a stream ID or * for all.
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "previewWebcam()";
button.onclick = function(){iframe.contentWindow.postMessage({"function":"previewWebcam"}, '*');}; // publishScreen
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "Change Add Camera text";
button.onclick = function(){iframe.contentWindow.postMessage({"function":"changeHTML", "target":"add_camera", "value":"NEW CAMERA TEXT"}, '*');}; // change text of add camera button
iframeContainer.appendChild(button);
var button = document.createElement("button");
button.innerHTML = "CLOSE IFRAME";
button.onclick = function(){iframeContainer.parentNode.removeChild(iframeContainer);};
iframeContainer.appendChild(button);
//////////// LISTEN FOR EVENTS
var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
@ -217,6 +275,17 @@ function loadIframe(){
iframeContainer.appendChild(outputWindow);
}
if ("streamIDs" in e.data){
var outputWindow = document.createElement("div");
outputWindow.innerHTML = "child-page-action: streamIDs<br />";
for (var key in e.data.streamIDs) {
outputWindow.innerHTML += "streamID: " + key + ", label:"+e.data.streamIDs[key] + "\n";
}
outputWindow.style.border="1px dotted black";
iframeContainer.appendChild(outputWindow);
}
if ("loudness" in e.data){
console.log(e.data);
if (document.getElementById("loudness")){

View File

@ -5,9 +5,9 @@
try {
var msie = window.navigator.userAgent.indexOf("MSIE ");
if (msie>0 || !!navigator.userAgent.match(/Trident.*rv\:11\./)){ // If MSIE or IE 11
alert("Internet Explorer is not supported.\n\nPlease consider using Google Chrome instead\n\nYou will be forwarded to the download page for Google Chrome now.");
alert("Internet Explorer is not supported.\n\nPlease consider using Microsoft Edge or Google Chrome instead\n\nYou will be forwarded to the download page for MS Edge now.");
console.error("INTERNET EXPLORER IS EVIL");
window.location = "https://www.google.com/chrome/";
window.location = "https://www.microsoft.com/edge";
}
} catch(e){
console.error(e);
@ -47,9 +47,9 @@
<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="obsninjadev" 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/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=22" />
@ -63,7 +63,7 @@
<link itemprop="url" href="./images/obsNinja_logo_full.png" />
</span>
<script type="text/javascript" crossorigin="anonymous" src="./thirdparty/CodecsHandler.js?ver=22"></script>
<script type="text/javascript" crossorigin="anonymous" src="./webrtc.js?ver=111"></script>
<script type="text/javascript" crossorigin="anonymous" src="./webrtc.js?ver=118"></script>
<input id="zoomSlider" type="range" style="display: none;" />
<div id="header">
<a id="logoname" href="./" style="text-decoration: none; color: white; margin: 2px;">
@ -461,7 +461,7 @@
<br /><br />
SELECT THE VIDEO FILE TO SHARE<br /><br />
<input id="fileselector" onchange="session.publishFile(this,event);" type="file" accept="video/*,audio/*"/>
<input id="fileselector" onchange="session.publishFile(this,event);" type="file" accept="video/*,audio/*" alt="Hold CTRL (or CMD) to select multiple files" title="Hold CTRL (or CMD) to select multiple files" multiple/>
</div>
<div class="outer close">
<div class="inner">
@ -503,14 +503,14 @@
<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.
iOS devices may have occasional audio or camera issues, such as no sound. Fully close Safari and reopen it if nothing else seems to work.
</li>
<li>
The VP9 codec on Chromium-based browsers seems to lag when screen-sharing at the moment. Use the OBS Virtual Camera as a capture source instead.
</li>
<br />
🎈 Site Updated: <a href="https://www.reddit.com/r/OBSNinja/comments/jik5fk/version_13_released_change_log/">November 9th, 2020</a>. The previous version can be found at
🎈 Site Updated: <a href="https://www.reddit.com/r/OBSNinja/comments/k02enh/version_134_of_obsninja_released_change_log_here/">November 24th, 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 />
@ -542,8 +542,9 @@
<a href="https://www.flaticon.com/" title="Flaticon">www.flaticon.com</a>
</div>
</div>
<span id="electronDragZone" style="pointer-events: none; z-index:-10; position:absolute;top:0;left:0;width:100%;height:5%;-webkit-app-region: drag;"></span>
<span id="electronDragZone" style="pointer-events: none; z-index:-10; position:absolute;top:0;left:0;width:100%;height:5%;-webkit-app-region: drag;min-height:33px;"></span>
<div id="gridlayout" ></div>
<div id="overlayMsgs" onclick="function(e){e.target.innerHTML = '';}" style="display:none"></div>
<div id="controls_blank" style="display: none;">
<div class="controlsGrid">
@ -565,37 +566,29 @@
<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;"/>
<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: 93%; position: relative; top: 0px; background-color:#fff0;"/>
<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>
<span>
<button style="width: 36px" data-action-type="change-quality" title="Disable Video Preview" onclick="toggleQualityDirector(0, this.dataset.UUID, this);">
<span data-translate="change-to-low-quality">&nbsp;&nbsp;<i class="las la-video-slash"></i></span>
</button>
<button style="width: 36px" data-action-type="change-quality" title="Low-Quality Preview" onclick="toggleQualityDirector(50, this.dataset.UUID, this);">
<span data-translate="change-to-medium-quality">&nbsp;&nbsp;<i class="las la-video"></i></span>
</button>
<button style="width: 36px" data-action-type="change-quality" title="High-Quality Preview" onclick="toggleQualityDirector(1200, this.dataset.UUID, this);">
<span data-translate="change-to-high-quality">&nbsp;&nbsp;<i class="las la-binoculars"></i></span>
</button>
</span>
<button data-action-type="direct-chat" title="Send Direct Message" onclick="directorSendMessage(this);">
<span data-translate="send-direct-chat"><i class="las la-envelope"></i> Message</span>
</button>
</div>
</div>
</div>
<div id="popupSelector" style="display:none;">
<span id="videoMenu3" class="videoMenu">
@ -624,9 +617,11 @@
</div>
<select id="outputSource3" ></select>
</span>
<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;margin: 10px 0px 20px 0"><i class="chevron right" style="font-size:150%;top:3px;position:relative;"></i> <b>Close Settings</b></button>
<span id="popupSelector_constraints" style="display: block;">
<button id="shareScreenGear" style="width: 135px; 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="width: 135px; background-color:#EFEFEF;padding:10px 12px 12px 2px;margin: 10px 0px 20px 0"><i class="chevron right" style="font-size:150%;top:3px;position:relative;"></i> <b>Close Settings</b></button>
<button id='advancedOptions' onclick="this.style.display = 'none'; toggle(getById('popupSelector_constraints'),false,false); " style="display:none; background-color:#EFEFEF;padding:10px 12px 12px 2px;margin: 10px 0px 0px 10px"><i class="chevron bottom" style="font-size:150%;top:3px;position:relative;"></i> <b>Show Advanced</b></button>
<span id="popupSelector_constraints" style="display: none;">
</span>
</p>
@ -733,8 +728,12 @@
<br />
<a onclick="changeLg('nl');toggle(document.getElementById('languages'));" style="cursor: pointer;">Dutch</a>
<br />
<a onclick="changeLg('tr');toggle(document.getElementById('languages'));" style="cursor: pointer;">Turkish</a>
<br />
<a onclick="changeLg('ja');toggle(document.getElementById('languages'));" style="cursor: pointer;">Japanese</a>
<br />
<a onclick="changeLg('cs');toggle(document.getElementById('languages'));" style="cursor: pointer;">Czech </a>
<br />
<a onclick="changeLg('pig');toggle(document.getElementById('languages'));" style="cursor: pointer;">Pig Latin</a>
<br />
</u>
@ -746,7 +745,7 @@
<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.2";
session.version = "13.4";
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.
@ -810,7 +809,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=80"></script>
<script type="text/javascript" crossorigin="anonymous" id="main-js" src="./main.js?ver=87"></script>
<script type="text/javascript" crossorigin="anonymous" src="./animations.js?ver=13"></script>
</body>
</html>

View File

@ -127,11 +127,42 @@ button.white:active {
#header {
width: 100%;
min-height: 24px;
padding: 1px;
background-color: #0F131D;
color: #FFF;
}
#overlayMsgs{
margin:0 auto;
background-color: #0000;
color: white;
font-family: Cousine, monospace;
font-size: 4em;
line-height: 1.5em;
letter-spacing: 0.0em;
text-shadow: 0.05em 0.05em 0px rgba(0,0,0,1);
width:100%;
height:100vh;
z-index: 1;
vertical-align: top;
text-align: center;
top: 0;
position: fixed;
overflow-wrap: anywhere;
padding:3%;
pointer-events: none
}
#overlayMsgs span{
background-color: #000A;
padding: 8px 8px 0px 8px;
margin:10px;
text-align: center;
width:100%;
pointer-events: none
}
#popupSelector_constraints{
margin:30px 9% 0 7%;
}
.credits {
color: #101020;
position: fixed;
@ -495,13 +526,13 @@ input[type=range]::-webkit-slider-runnable-track {
input[type=range]::-webkit-slider-thumb {
box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d;
border: 1px solid #000000;
height: 36px;
height: 30px;
width: 16px;
border-radius: 3px;
background: #ffffff;
cursor: pointer;
-webkit-appearance: none;
margin-top: -14px;
margin-top: -11px;
}
input[type=range]:focus::-webkit-slider-runnable-track {
background: #367ebd;
@ -1066,8 +1097,14 @@ img {
.close-btn:hover {
color: darkgray;
}
#chattoggle{
top: 0.5px;
position: relative;
}
.la-phone {
color: red;
top:0.5;
}
@-webkit-keyframes animatetop {
from {
@ -1095,6 +1132,12 @@ video {
background-color: transparent !important;
border: 0;
margin: 0;
user-select:none;
-webkit-user-select: none; /* Chrome/Safari */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* IE10+ */
-webkit-tap-highlight-color:transparent;
outline-style:none;
background-size: auto 50px;
background-repeat: no-repeat;
background-position: center;
@ -1446,7 +1489,7 @@ input[type=checkbox] {
border-radius: 5px;
padding: 1px 7px;
overflow-y:scroll;
overflow-wrap: break-word;
overflow-wrap: anywhere;
max-height: 800px;
}

602
main.js
View File

@ -22,6 +22,7 @@ function getById(id) {
return el;
}
function changeParam(url,paramName,paramValue){
var qind = url.indexOf('?');
var params = url.substring(qind).split('&');
@ -50,11 +51,22 @@ function changeParam(url,paramName,paramValue){
return url.substring(0,qind) + query;
}
function updateURL(param, force=false) {
var para = param.split('=');
if (!(urlParams.has(para[0]))){
function updateURL(param, force=false, cleanUrl=false){
var para = param.split('=');
if (cleanUrl){
if (history.pushState){
log("not false");
var href = new URL(cleanUrl);
if (para.length==1){
href = changeParam(cleanUrl, para[0],"")
} else {
href = changeParam(cleanUrl, para[0],para[1]);
}
log("---"+href.toString());
window.history.pushState({path:href.toString()},'',href.toString());
}
} else if (!(urlParams.has(para[0]))){
if (history.pushState){
var arr = window.location.href.split('?');
var newurl;
if (arr.length > 1 && arr[1] !== '') {
@ -69,20 +81,56 @@ function updateURL(param, force=false) {
if (history.pushState){
var href = new URL(window.location.href);
if (para.length==1){
href = changeParam(window.location.href,para[0],"")
href = changeParam(window.location.href, para[0],"")
} else {
href = changeParam(window.location.href,para[0],para[1]);
href = changeParam(window.location.href, para[0], para[1]);
}
log(href.toString());
log("---"+href.toString());
window.history.pushState({path:href.toString()},'',href.toString());
}
log("asdfasdf");
}
log("asdfasdf 222");
if (session.sticky){
setCookie("settings", encodeURI(window.location.href), 90);
}
}
var filename=false;
try{
filename = window.location.pathname.substring(window.location.pathname.lastIndexOf('/')+1);
filename2 = filename.split("?")[0];
// split at ???
if (filename.split(".").length==1){
if (filename2.length<2){ // easy win
filename=false;
} else if (filename.startsWith("&")){ // easy win
var tmpHref = window.location.href.substring(0, window.location.href.lastIndexOf('/')) + "/?" + filename.split("&").slice(1).join("&");
log("TMP "+tmpHref);
updateURL(filename.split("&")[1], true, tmpHref);
filename=false;
} else if (filename2.split("&")[0].includes("=")){
log("asdf "+ filename.split("&")[0]);
if (history.pushState){
var tmpHref = window.location.href.substring(0, window.location.href.lastIndexOf('/'));
tmpHref = tmpHref+"/?"+filename;
filename = false;
//alert("Please ensure your URL is correctly formatted.");
window.history.pushState({path:tmpHref.toString()},'',tmpHref.toString());
}
} else {
filename = filename2.split("&")[0];
if (filename2!=filename){
alert("Warning: Please ensure your URL is correctly formatted.");
}
}
} else {
filename = false;
}
log(filename);
} catch(e){errorlog(e);}
(function (w) {
w.URLSearchParams = w.URLSearchParams || function (searchString) {
var self = this;
@ -209,17 +257,9 @@ if (window.obsstudio){
window.onload = function() { // This just keeps people from killing the live stream accidentally. Also give me a headsup that the stream is ending
window.addEventListener("beforeunload", function (e) {
session.ws.close();
session.hangup();
//setTimeout(function(){session.hangup();},0);
return undefined; // ADDED OCT 29th; get rid of popup. Just close the socket connection if the user is refreshing the page. It's one or the other.
///
// if (formSubmitting) {
// return undefined;
// }
// var confirmationMessage = 'Leaving the page now will terminate your stream ';
// (e || window.event).returnValue = confirmationMessage; //Gecko + IE
// getById("main").innerHTML = "SFSDFSDF";
// return confirmationMessage; //Gecko + Webkit, Safari, Chrome etc.
});
};
@ -310,6 +350,8 @@ if (typeof session === 'undefined') { // make sure to init the WebRTC if not exi
function setCookie(cname, cvalue, exdays) {
var d = new Date();
d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
@ -376,6 +418,20 @@ if( /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(naviga
getById("container-2").className = 'column columnfade advanced'; // Hide screen share on mobile
}
if (/CriOS/i.test(navigator.userAgent) && /iphone|ipod|ipad/i.test(navigator.userAgent)){
alert("Chrome on iPhones/iPads do not support the required technology to use this site.\n\nPlease use Safari instead.");
}
if (urlParams.has('broadcast') || urlParams.has('bc')){
log("Room BITRATE SET");
session.broadcast = urlParams.get('broadcast') || urlParams.get('bc');
session.nopreview=true;
session.style=1;
getById("header").style.display="none";
getById("header").style.opacity = 0;
}
if (urlParams.has('webcam') || urlParams.has('wc')){
session.webcamonly = true;
} else if (urlParams.has('screenshare') || urlParams.has('ss')){
@ -398,12 +454,16 @@ if (session.screenshare==true){
getById("container-2").classList.remove('pointer');
}
if (urlParams.has('manual')){
session.manual = true;
}
if (session.webcamonly==true){
getById("container-2").className = 'column columnfade advanced'; // Hide screen share on mobile
//getById("shareScreenGear").style.display="none"; // removed based on fluffy's feedback
getById("container-3").classList.add("skip-animation");
getById("container-3").classList.remove('pointer');
setTimeout(function(){previewWebcam();},10);
setTimeout(function(){previewWebcam();},100);
}
getById("main").classList.remove('hidden');
@ -603,7 +663,13 @@ if (urlParams.has('audiobitrate') || urlParams.has('ab')){ // both peers need th
if ((iOS) || (iPad)){
session.audiobitrate = false; // iOS devices seem to get distortion with custom audio bitrates. Disable for now.
}
/* if (urlParams.has('whitebalance') || urlParams.has('temp')){ // Need to be applied after the camera is selected. bleh. not enforcible. remove for now.
var temperature = urlParams.get('whitebalance') || urlParams.get('temp');
try{
updateCameraConstraints('colorTemperature', parseFloat(temperature));
} catch (e){errorlog(e);}
} */
if (urlParams.has('streamid') || urlParams.has('view') || urlParams.has('v') || urlParams.has('pull')){ // the streams we want to view; if set, but let blank, we will request no streams to watch.
session.view = urlParams.get('streamid') || urlParams.get('view') || urlParams.get('v') || urlParams.get('pull'); // this value can be comma seperated for multiple streams to pull
@ -625,7 +691,7 @@ if (urlParams.has('streamid') || urlParams.has('view') || urlParams.has('v') ||
if (urlParams.has('nopreview')){
log("preview OFF");
session.nopreview = true;
}
}
if (urlParams.has('obsfix')){
session.obsfix = urlParams.get('obsfix');
@ -704,7 +770,8 @@ if (urlParams.has("videodevice") || urlParams.has("vdevice") || urlParams.has("
} else if (session.videoDevice=="default"){
session.videoDevice = 1;
} else {
// whatever the user entered I guess
// whatever the user entered I guess, santitized.
session.videoDevice = session.videoDevice.replace(/[\W]+/g,"_").toLowerCase();
}
if (session.videoDevice === 0){
@ -741,6 +808,20 @@ if (urlParams.has("audiodevice") || urlParams.has("adevice") || urlParams.has("
session.audioDevice="line_newtek_ndi_audio";
} else {
// whatever the user entered I guess
session.audioDevice = session.audioDevice.replace(/[\W]+/g,"_").toLowerCase();
}
if (session.videoDevice === 0){
if (session.audioDevice === 0){
getById("add_camera").innerHTML = "Click Start to Join";
getById("container-2").className = 'column columnfade advanced'; // Hide screen share on mobile
//getById("shareScreenGear").style.display="none"; // removed based on fluffy's feedback
getById("container-3").classList.add("skip-animation");
getById("container-3").classList.remove('pointer');
setTimeout(function(){previewWebcam();},100);
session.webcamonly = true;
}
}
log("session.audioDevice:" + session.audioDevice);
@ -1039,6 +1120,29 @@ if (urlParams.has('quality') || urlParams.has('q')){
if (urlParams.has('sink')){
session.sink = urlParams.get('sink');
} else if (urlParams.has('outputdevice') || urlParams.has('od')){
session.outputDevice = urlParams.get('outputdevice') || urlParams.get('od');
if (session.outputDevice){
session.outputDevice = session.outputDevice.toLowerCase().replace(/[\W]+/g,"_");
} else {
session.outputDevice = false;
}
if (session.outputDevice){
try {
enumerateDevices().then(function(deviceInfos){
for (let i = 0; i !== deviceInfos.length; ++i) {
if (deviceInfos[i].kind === 'audiooutput'){
if (deviceInfos[i].label.replace(/[\W]+/g,"_").toLowerCase().includes(session.outputDevice)){
session.sink = deviceInfos[i].deviceId;
log("AUDIO OUT DEVICE: "+deviceInfos[i].deviceId);
break
}
}
}
});
} catch (e){}
}
}
if (urlParams.has('fullscreen')){
@ -1247,6 +1351,10 @@ if (urlParams.has('turn')){
};
}
} catch(e){errorlog("Twilio Failed");}
} else if ((turnstring=="false") || (turnstring=="off")){ // disable TURN servers
session.configuration.iceServers = [{ urls: ["stun:stun.l.google.com:19302", "stun:stun4.l.google.com:19302" ]}];
//session.configuration.iceServers.push(turn);
} else {
try {
turnstring = turnstring.split(";");
@ -1286,13 +1394,16 @@ if (urlParams.has('wss')){
}
window.onmessage = function(e){ // iFRAME support
log(e);
if ("function" in e.data){ // these are calling in-app functions, with perhaps a callback -- TODO: add callbacks
var ret = null;
if (e.data.function === "previewWebcam"){
ret = previewWebcam();
} else if (e.data.function === "publishWebcam"){
ret = publishWebcam();
} else if (e.data.function === "changeHTML"){
try {
ret = getById(e.data.target);
ret.innerHTML = e.data.value;
} catch(e){}
} else if (e.data.function === "publishScreen"){
ret = publishScreen();
}
@ -1307,15 +1418,29 @@ window.onmessage = function(e){ // iFRAME support
if ("mic" in e.data){ // this should work for the director's mic mute button as well. Needs to be manually enabled the first time still tho.
if (e.data.mic === true){ // unmute
session.muted = false; // set
log(session.mute);
log(session.muted);
toggleMute(true); // apply
} else if (e.data.mic === false){ // mute
session.muted = true; // set
log(session.mute);
log(session.muted);
toggleMute(true); // apply
} else if (e.data.mic === "toggle"){ // toggle
toggleMute();
}
}
if ("camera" in e.data){ // this should work for the director's mic mute button as well. Needs to be manually enabled the first time still tho.
if (e.data.camera === true){ // unmute
session.videoMuted = false; // set
log(session.videoMuted);
toggleVideoMute(true); // apply
} else if (e.data.camera === false){ // mute
session.videoMuted = true; // set
log(session.videoMuted);
toggleVideoMute(true); // apply
} else if (e.data.camera === "toggle"){ // toggle
toggleVideoMute();
}
}
if ("mute" in e.data){
@ -1351,6 +1476,8 @@ window.onmessage = function(e){ // iFRAME support
}
}
if ("bitrate" in e.data){
for (var i in session.rpcs){
try {
@ -1399,6 +1526,18 @@ window.onmessage = function(e){ // iFRAME support
}
}
if ("getStreamIDs" in e.data){
log("GOT OUDNESS REQUEST");
if (e.data.getStreamIDs == true){
var streamIDs = {};
for (var i in session.rpcs){
streamIDs[session.rpcs[i].streamID] = session.rpcs[i].label;
}
parent.postMessage({"streamIDs": streamIDs }, "*");
}
}
if ("close" in e.data){
for (var i in session.rpcs){
try {
@ -1409,7 +1548,60 @@ window.onmessage = function(e){ // iFRAME support
}
}
if ("style" in e.data){
try{
const style = document.createElement('style');
style.textContent = e.data.style;
document.head.append(style);
log(style);
} catch(e){
errorlog(e);
}
}
if ("automixer" in e.data){
if (e.data.automixer==true){
session.manual = false;
try{
updateMixer();
} catch(e){};
} else if (e.data.automixer==false){
session.manual = true;
}
}
if ("target" in e.data){
log(e.data);
for (var i in session.rpcs){
try {
if ("streamID" in session.rpcs[i]){
if ((session.rpcs[i].streamID == e.data.target) || ( e.data.target == "*")){
try{
if ("settings" in e.data){
for (const property in e.data.settings){
session.rpcs[i].videoElement[property] = e.data.settings[property];
}
}
if ("add" in e.data){
getById("gridlayout").appendChild(session.rpcs[i].videoElement);
} else if ("remove" in e.data){
try {
session.rpcs[i].videoElement.parentNode.removeChild(session.rpcs[i].videoElement);
} catch (e){
try{
session.rpcs[i].videoElement.parentNode.parentNode.removeChild(session.rpcs[i].videoElement.parentNode);
}catch(e){}
}
}
} catch(e){errorlog(e);}
}
}
} catch(e){
errorlog(e);
}
}
}
};
function pokeIframeAPI(action){
@ -1438,16 +1630,6 @@ function sleep(ms = 0){
session.connect();
// session.volume = 100; // needs to be set after?
var url = window.location.pathname;
var filename = url.substring(url.lastIndexOf('/')+1);
if (filename.split(".").length==1){
if (filename.length<2){
filename=false;
}
} else {
filename = false;
}
////////// Canvas Effects ///////////////
@ -1540,11 +1722,11 @@ function drawFace(){
if (window.FaceDetector == undefined) {
console.error('Face Detection not supported');
//console.error('Face Detection not supported');
var faceDetector = false;
} else {
var faceDetector = new FaceDetector();
console.log('FaceD Loaded');
//console.log('FaceD Loaded');
setTimeout(function(){detect();},300);
setTimeout(function(){draw();},33);
}
@ -1781,7 +1963,7 @@ if (urlParams.has('hidemenu') || urlParams.has('hm')){ // needs to happen the r
getById("header").style.opacity = 0;
}
if (urlParams.has('hideheader') || urlParams.has('hh')){ // needs to happen the room and permaid applications
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;
}
@ -2031,7 +2213,81 @@ function toggleChat(ele=null){ // TODO: I need to have this be MUTE, toggle, wit
}
getById("chatNotification").classList.remove("notification");
}
function directorSendMessage(ele){
var target = document.createElement("div");
target.style = "position:absolute;float:left;width:275px;height:222px;background-color:#7E7E7E;";
var inputField = document.createElement("textarea");
inputField.placeholder = "Enter your message here";
inputField.style.width="255px";
inputField.style.height="170px";
inputField.style.margin = "5px 10px 5px 10px";
inputField.style.padding = "5px";
target.appendChild(inputField);
var sendButton = document.createElement("button");
sendButton.innerHTML = "<i class='las la-reply'></i> send message ";
sendButton.style.left = "5px";
sendButton.style.position = "relative";
sendButton.onclick = function(){
var chatMsg = {};
chatMsg.chat=inputField.value;
if (sendButton.parentNode.overlay){
chatMsg.overlay = sendButton.parentNode.overlay;
}
session.sendRequest(chatMsg, ele.dataset.UUID);
inputField.value="";
//target.parentNode.removeChild(target);
};
var closeButton = document.createElement("button");
closeButton.innerHTML = "<i class='las la-times'></i> close";
closeButton.style.left = "5px";
closeButton.style.position = "relative";
closeButton.onclick = function(){
inputField.value = "";
target.parentNode.removeChild(target);
};
var overlayMsg = document.createElement("span");
overlayMsg.style.left = "16px";
overlayMsg.style.top = "6px";
overlayMsg.style.position = "relative";
overlayMsg.innerHTML = "<i class='lar la-bell' style='font-size:170%; color:#FFF; cursor:pointer;'></i>";
target.overlay=true;
overlayMsg.onclick = function(e){
log(e.target.parentNode.parentNode);
if (e.target.parentNode.parentNode.overlay===true){
e.target.parentNode.parentNode.overlay = false;
e.target.parentNode.innerHTML = "<i class='lar la-bell-slash' style='font-size:170%; color:#DDD; cursor:pointer;'></i>";
} else {
e.target.parentNode.parentNode.overlay=true;
e.target.parentNode.innerHTML = "<i class='lar la-bell' style='font-size:170%; color:#FFF; cursor:pointer;'></i>";
}
}
inputField.addEventListener("keydown",function(e){
if(e.keyCode == 13){
e.preventDefault();
sendButton.click();
} else if (e.keyCode == 27){
e.preventDefault();
inputField.value="";
target.parentNode.removeChild(target);
}
});
target.appendChild(closeButton);
target.appendChild(sendButton);
target.appendChild(overlayMsg);
ele.parentNode.appendChild(target);
inputField.focus();
inputField.select();
}
function toggleVideoMute(apply=false){ // TODO: I need to have this be MUTE, toggle, with volume not touched.
if (apply){
session.videoMuted=!session.videoMuted;
@ -2353,6 +2609,21 @@ function publishScreen(){
log("Session SInk: "+session.sink);
if (session.sink=="default"){session.sink=false;}
/* if (session.sink ===false){
if (session.outputDevice){
try {
for (var i in outputSelect.options){
log(outputSelect.options[i].label.replace(/[\W]+/g,"_").toLowerCase());
log(session.outputDevice);
if (outputSelect.options[i].label.replace(/[\W]+/g,"_").toLowerCase().includes(session.outputDevice)){
session.sink = outputSelect.options[i].value;
break;
}
}
} catch (e){}
}
} */
log("*");
session.publishScreen(constraints, title, audioSelect).then((res)=>{
if (res==false){return;} // no screen selected
@ -2891,7 +3162,7 @@ function enumerateDevices() {
function requestOutputAudioStream(){
try {
warnlog("GET USER MEDIA");
//warnlog("GET USER MEDIA");
return navigator.mediaDevices.getUserMedia({audio:true, video:false }).then(function(stream1){ // Apple needs thi to happen before I can access EnumerateDevices.
log("get media sources; request audio stream");
return enumerateDevices().then(function(deviceInfos){
@ -2937,7 +3208,7 @@ function requestOutputAudioStream(){
function requestAudioStream(){
try {
warnlog("GET USER MEDIA");
//warnlog("GET USER MEDIA");
return navigator.mediaDevices.getUserMedia({audio:true, video:false }).then(function(stream1){ // Apple needs thi to happen before I can access EnumerateDevices.
log("get media sources; request audio stream");
return enumerateDevices().then(function(deviceInfos){
@ -3068,14 +3339,14 @@ function gotDevices(deviceInfos) { // https://github.com/webrtc/samples/blob/gh-
var tmp = [];
for (let i = 0; i !== deviceInfos.length; ++i) {
deviceInfo = deviceInfos[i];
if ((deviceInfo.kind === 'audioinput') && (deviceInfo.label.replace(/[\W]+/g,"_").toLowerCase().startsWith(session.audioDevice))){
if ((deviceInfo.kind === 'audioinput') && (deviceInfo.label.replace(/[\W]+/g,"_").toLowerCase().includes(session.audioDevice))){
tmp.push(deviceInfo);
log("A DEVICE FOUND = "+deviceInfo.label.replace(/[\W]+/g,"_").toLowerCase());
}
}
for (let i = 0; i !== deviceInfos.length; ++i) {
deviceInfo = deviceInfos[i];
if (!((deviceInfo.kind === 'audioinput') && (deviceInfo.label.replace(/[\W]+/g,"_").toLowerCase().startsWith(session.audioDevice)))){
if (!((deviceInfo.kind === 'audioinput') && (deviceInfo.label.replace(/[\W]+/g,"_").toLowerCase().includes(session.audioDevice)))){
tmp.push(deviceInfo);
}
}
@ -3090,14 +3361,14 @@ function gotDevices(deviceInfos) { // https://github.com/webrtc/samples/blob/gh-
var tmp = [];
for (let i = 0; i !== deviceInfos.length; ++i) {
deviceInfo = deviceInfos[i];
if ((deviceInfo.kind === 'videoinput') && (deviceInfo.label.replace(/[\W]+/g,"_").toLowerCase().startsWith(session.videoDevice))){
if ((deviceInfo.kind === 'videoinput') && (deviceInfo.label.replace(/[\W]+/g,"_").toLowerCase().includes(session.videoDevice))){
tmp.push(deviceInfo);
log("V DEVICE FOUND = "+deviceInfo.label.replace(/[\W]+/g,"_").toLowerCase());
}
}
for (let i = 0; i !== deviceInfos.length; ++i) {
deviceInfo = deviceInfos[i];
if (!((deviceInfo.kind === 'videoinput') && (deviceInfo.label.replace(/[\W]+/g,"_").toLowerCase().startsWith(session.videoDevice)))){
if (!((deviceInfo.kind === 'videoinput') && (deviceInfo.label.replace(/[\W]+/g,"_").toLowerCase().includes(session.videoDevice)))){
tmp.push(deviceInfo);
}
}
@ -3164,8 +3435,8 @@ function gotDevices(deviceInfos) { // https://github.com/webrtc/samples/blob/gh-
option.value = deviceInfo.deviceId || "default";
if (option.value == session.sink){
option.selected = true;
}
option.text = deviceInfo.label || `Speaker ${outputSelect.length + 1}`;
}
option.text = deviceInfo.label || `Speaker ${audioOutputSelect.length + 1}`;
audioOutputSelect.appendChild(option);
} else {
log('Some other kind of source/device: ', deviceInfo);
@ -3702,8 +3973,8 @@ async function getAudioOnly(selector, trackid=null){
}
}
constraint.video = false;
warnlog(constraint);
var stream = await navigator.mediaDevices.getUserMedia(constraint).timeout(15000).then(function (stream2){
//warnlog(constraint);
var stream = await navigator.mediaDevices.getUserMedia(constraint).then(function (stream2){
log("pushing stream2");
return stream2;
}).catch(errorlog); // More error reporting maybe?
@ -4058,10 +4329,9 @@ async function grabVideo(quality=0, eleName='previewWebcam', selector="select#vi
}
session.mirrorExclude = mirror;
log(constraints);
setTimeout(function(){
navigator.mediaDevices.getUserMedia(constraints).timeout(10000).then(function(stream){
navigator.mediaDevices.getUserMedia(constraints).then(function(stream){
log("adding video tracks 2412");
stream.getVideoTracks().forEach(function(track){
@ -4341,7 +4611,7 @@ function tryAgain(event){ // audio or video agnostic track reconnect ----------
return; // no idea what this is? fail gently.
}
warnlog(constraint);
//warnlog(constraint);
navigator.mediaDevices.getUserMedia(constraint).timeout(3000).then(function (stream){
stream.getTracks().forEach(function(track){
@ -4357,7 +4627,7 @@ function tryAgain(event){ // audio or video agnostic track reconnect ----------
if (sender.track){
if (sender.track.id == track.id){
added=true;
warnlog(sender.track);
//warnlog(sender.track);
if (sender.track.readyState=="ended"){
sender.replaceTrack(track);
}
@ -4516,9 +4786,9 @@ function updateConstraintSliders(){
}
try {
var currentCameraConstaints={};
if (track0.getSettings){
currentCameraConstaints = track0.getSettings();
session.currentCameraConstaints = track0.getSettings();
}
} catch(e){
errorlog(e);
@ -4526,15 +4796,18 @@ function updateConstraintSliders(){
for (var i in session.cameraConstaints){
try {
log(i);
log(session.cameraConstaints[i]);
if ((typeof session.cameraConstaints[i] ==='object') && (session.cameraConstaints[i] !== null) && ("max" in session.cameraConstaints[i]) && ("min" in session.cameraConstaints[i])){
log(i);
log(session.cameraConstaints[i]);
if (i==="aspectRatio"){continue;}
else if (i==="width"){continue;}
else if (i==="height"){continue;}
else if (i==="frameRate"){continue;}
if (getById("popupSelector_constraints").style.display == "none"){
getById("advancedOptions").style.display="inline-block";
}
var label = document.createElement("label");
label.id= "label_"+i;
label.htmlFor = "constraints_"+i;
@ -4544,9 +4817,9 @@ function updateConstraintSliders(){
input.min = session.cameraConstaints[i].min;
input.max = session.cameraConstaints[i].max;
if (i in currentCameraConstaints){
input.value = currentCameraConstaints[i];
label.innerHTML = i+": "+currentCameraConstaints[i];
if (i in session.currentCameraConstaints){
input.value = session.currentCameraConstaints[i];
label.innerHTML = i+": "+session.currentCameraConstaints[i];
} else {
label.innerHTML = i;
}
@ -4556,7 +4829,7 @@ function updateConstraintSliders(){
input.type = "range";
input.dataset.keyname = i;
input.id = "constraints_"+i;
input.style="display:block; width:95%;";
input.style="display:block; width:100%;";
input.name = "constraints_"+i;
@ -4566,9 +4839,79 @@ function updateConstraintSliders(){
};
getById("popupSelector_constraints").appendChild(label);
getById("popupSelector_constraints").appendChild(input);
} else if ((typeof session.cameraConstaints[i] ==='object') && (session.cameraConstaints[i] !== null)){
if (i == "resizeMode"){continue;}
var div = document.createElement("div");
var label = document.createElement("label");
label.id= "label_"+i;
label.htmlFor = "constraints_"+i;
label.innerHTML = i+":";
label.style="display:inline-block; padding:0;margin: 15px 0px 29px;";
label.dataset.keyname = i;
var input = document.createElement("select");
var c = document.createElement("option");
if (session.cameraConstaints[i].length>1){
for (var opts in session.cameraConstaints[i]) {
log(opts);
var opt = new Option(session.cameraConstaints[i][opts], session.cameraConstaints[i][opts]);
input.options.add(opt);
}
} else if (i.toLowerCase == "torch"){
var opt = new Option("Off", false);
input.options.add(opt);
opt = new Option("On", true);
input.options.add(opt);
} else {
continue;
}
input.id = "constraints_"+i;
input.className="constraintCameraInput";
input.name = "constraints_"+i;
input.style = "display:inline; padding:2px; margin:0 10px;";
input.dataset.keyname = i;
input.onchange = function(e){
//getById("label_"+e.target.dataset.keyname).innerHTML =e.target.dataset.keyname+": "+e.target.value;
updateCameraConstraints(e.target.dataset.keyname, e.target.value);
log(e.target.dataset.keyname, e.target.value);
};
getById("popupSelector_constraints").appendChild(div);
div.appendChild(label);
div.appendChild(input);
} else if (typeof session.cameraConstaints[i] === 'boolean'){
var div = document.createElement("div");
var label = document.createElement("label");
label.id= "label_"+i;
label.htmlFor = "constraints_"+i;
label.innerHTML = i+":";
label.style="display:inline-block; padding:0;margin: 15px 0px 29px;";
label.dataset.keyname = i;
var input = document.createElement("select");
var c = document.createElement("option");
var opt = new Option("Off", false);
input.options.add(opt);
opt = new Option("On", true);
input.options.add(opt);
input.id = "constraints_"+i;
input.className="constraintCameraInput";
input.name = "constraints_"+i;
input.style = "display:inline; padding:2px; margin:0 10px;";
input.dataset.keyname = i;
input.onchange = function(e){
//getById("label_"+e.target.dataset.keyname).innerHTML =e.target.dataset.keyname+": "+e.target.value;
updateCameraConstraints(e.target.dataset.keyname, e.target.value);
log(e.target.dataset.keyname, e.target.value);
};
getById("popupSelector_constraints").appendChild(div);
div.appendChild(label);
div.appendChild(input);
}
} catch(e){errorlog(e);}
@ -4578,7 +4921,17 @@ function updateConstraintSliders(){
function updateCameraConstraints(constraint, value=null){
var track0 = session.streamSrc.getVideoTracks();
track0 = track0[0];
if (value == parseFloat(value)){
value = parseFloat(value);
} else if (value == "true"){
value = true;
} else if (value == "false"){
value = false;
}
log({advanced: [ {[constraint]: value} ]});
track0.applyConstraints({advanced: [ {[constraint]: value} ]});
return;
}
function setupWebcamSelection(stream=null){
@ -4802,21 +5155,85 @@ function previewWebcam(){
constraint.video = true;
}
try{
try {
log("getting permission from navigator");
if ("permissions" in navigator){
log("queried0");
navigator.permissions.query({name: 'camera'}).then((permissionObj) => {
log("queried ok");
log("queried camera permissions successfully:");
log(permissionObj.state);
if (permissionObj.state!=="prompt"){
constraint.video = false;
if (permissionObj.state!=="prompt"){ // trying to bypass any NDI problems
enumerateDevices().then(function(devices){
var vtrue = false;
var atrue = false;
devices.forEach(function(device) {
if (device.kind === 'audioinput'){
atrue=true;
} else if (device.kind === 'videoinput'){
vtrue = true;
}
});
if (atrue===false){
constraint.audio = false;
requestBasicPermissions(constraint);
} else {
constraint.video = false;
requestBasicPermissions(constraint);
}
}).catch((error)=>{
requestBasicPermissions(constraint)
});
} else {
log("constraint: "+constraint);
enumerateDevices().then(function(devices){
log("enumeratated");
var vtrue = false;
var atrue = false;
devices.forEach(function(device) {
if (device.kind === 'audioinput'){
atrue=true;
} else if (device.kind === 'videoinput'){
vtrue = true;
}
});
if (atrue===false){
constraint.audio = false;
}
if (vtrue===false){
constraint.video = false;
}
requestBasicPermissions(constraint);
}).catch((error)=>{
log("enumeratated failed. Seeking permissions.");
requestBasicPermissions(constraint)
});
}
requestBasicPermissions(constraint)
}).catch((error) => {
log("queried err");
requestBasicPermissions(constraint)
errorlog('Got error :', error);
log("queried camera permissions returned error");
enumerateDevices().then(function(devices){
var vtrue = false;
var atrue = false;
devices.forEach(function(device) {
if (device.kind === 'audioinput'){
atrue=true;
} else if (device.kind === 'videoinput'){
vtrue = true;
}
});
if (atrue===false){
constraint.audio = false;
}
if (vtrue===false){
constraint.video = false;
}
requestBasicPermissions(constraint);
}).catch((error)=>{
requestBasicPermissions(constraint)
});
});
} else {
log("queried not supported");
@ -4832,11 +5249,14 @@ function requestBasicPermissions(constraint){
log("PreviewWebcam");
log(constraint);
try {
navigator.mediaDevices.getUserMedia(constraint).timeout(15000).then(function(stream){ // Apple needs thi to happen before I can access EnumerateDevices.
var timerBasicCheck = setTimeout(function(){alert("Camera Access Request Timed Out\n\nDid you accept camera permissions? Please do so first.\n\nOtherwise, do you have NDI Tools installed? Maybe try uninstalling NDI tools.\n\nPlease also double check that your camera and audio devices are correctly connected. You may also need to refresh the page.");},10000);
navigator.mediaDevices.getUserMedia(constraint).then(function(stream){ // Apple needs thi to happen before I can access EnumerateDevices.
clearTimeout(timerBasicCheck);
log("got first stream");
setupWebcamSelection(stream);
}).catch(function(err){
log("some error");
clearTimeout(timerBasicCheck);
warnlog("some error with GetUSERMEDIA");
errorlog(err); /* handle the error */
if (err.name == "NotFoundError" || err.name == "DevicesNotFoundError") {
//required track is missing
@ -4986,6 +5406,7 @@ function generateQRPageCallback(hash){
useSVG: false
});
qrcode.makeCode(sendstr);
setTimeout(function(){getById("qrcode").title="";},100); // i really hate the title overlay that the qrcode function makes
} catch(e){
errorlog(e);
@ -5457,7 +5878,16 @@ function sendChatMessage(){ // filtered + visual
updateMessages();
}
function getChatMessage(msg, label=false){
function toggleQualityDirector(bitrate, UUID, ele = null){
var eles = ele.parentNode.childNodes;
for (i in eles){
eles[i].className="";
}
ele.className="pressed";
session.requestRateLimit(bitrate, UUID);
}
function getChatMessage(msg, label=false, director=false, overlay=false){
msg = sanitize(msg); // keep it clean.
if (msg==""){return;}
data = {};
@ -5465,7 +5895,13 @@ function getChatMessage(msg, label=false){
data.msg = msg;
if (label){
data.label = label.replace(/[\W]+/g,"_").replace(/_+/g, ' ');
data.label = "<b>" + data.label +":</b> ";
if (director){
data.label = "<b><i>" + data.label +":</i></b> ";
} else {
data.label = "<b>" + data.label +":</b> ";
}
} else if (director){
data.label = "<b><i>Director:</i></b> ";
} else {
data.label = "";
}
@ -5473,6 +5909,20 @@ function getChatMessage(msg, label=false){
messageList.push(data);
messageList = messageList.slice(-100);
updateMessages();
if (overlay){
var textOverlay = getById("overlayMsgs");
if (textOverlay){
var spanOverlay = document.createElement("span");
spanOverlay.innerHTML = "<b><i>Director:</i></b> "+msg+"<br />";
textOverlay.appendChild(spanOverlay);
textOverlay.style.display="block";
var showtime = msg.length*200+3000;
if (showtime>8000){showtime=8000;}
setTimeout(function(ele){ele.parentNode.removeChild(ele);},showtime,spanOverlay);
}
}
if (session.chat==false){
getById("chattoggle").className="las la-comments my-float toggleSize puslate";
getById("chatbutton").className="float";
@ -5678,4 +6128,4 @@ function recordVideo(target, event, videoKbps = false, interval=30){ // event.c
video.recorder.mediaRecorder.start(100); // 100ms chunks
return;
}
}

View File

@ -59,9 +59,7 @@ button{
})(window);
var urlParams = new URLSearchParams(window.location.search);
function loadIframe(){
function loadIframe(){ // this is pretty important if you want to avoid camera permission popup problems. YOu need to load the iFRAME after you load the parent body. A quick solution is like: <body onload=>loadIframe();"> !!!
var streamID = "";
var possible = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz23456789";
@ -74,22 +72,37 @@ function loadIframe(){
var iframeContainer = document.createElement("span");
iframe.allow="autoplay";
var srcString = "./?push="+streamID+"&cleanoutput&privacy&audiodevice=0";
var srcString = "./?push="+streamID+"&cleanoutput&privacy&webcam&audiodevice=0&fullscreen";
if (urlParams.has('turn')){
iframe.src = srcString+"&turn="+urlParams.get("turn");
} else {
iframe.src = srcString;
srcString = srcString+"&turn="+urlParams.get("turn");
}
// we are changing some text on page load, just to demonstrate what's possible.
iframe.onload = function(e){e.target.contentWindow.postMessage({"function":"changeHTML", "target":"add_camera","value":"Select your Camera"}, '*');};
iframe.src = srcString;
iframeContainer.appendChild(iframe);
document.getElementById("container").appendChild(iframeContainer);
var iframe = document.createElement("iframe");
var iframeContainer = document.createElement("span");
iframe.allow="autoplay";
iframe.src = "./?view="+streamID+"&cleanoutput&privacy&noaudio";
var srcString = "./?view="+streamID+"&cleanoutput&privacy&noaudio";
if (urlParams.has('turn')){
srcString = srcString+"&turn="+urlParams.get("turn");
}
if (urlParams.has('buffer')){
srcString = srcString+"&buffer="+urlParams.get("buffer");
}
iframe.src = srcString;
iframeContainer.appendChild(iframe);
document.getElementById("container").appendChild(iframeContainer);
@ -107,6 +120,9 @@ function loadIframe(){
button.onclick = function(){iframe.contentWindow.postMessage({"bitrate":30}, '*');}
document.getElementById("container").appendChild(button);
var button = document.createElement("button");
button.innerHTML = "High Bitrate";
button.onclick = function(){iframe.contentWindow.postMessage({"bitrate":6000}, '*');}

5623
thirdparty/adapter-latest.js vendored Normal file

File diff suppressed because it is too large Load Diff

55669
thirdparty/video.js vendored Normal file

File diff suppressed because it is too large Load Diff

52728
thirdparty/videojs-vr.js vendored Normal file

File diff suppressed because one or more lines are too long

68
translations/cs.json Normal file
View File

@ -0,0 +1,68 @@
{
"GO": "Spustit",
"add-group-chat": "Přidat skupinový chat OBS",
"add-to-group": "Přidat do skupiny",
"add-your-camera": "Přidat kameru do OBS",
"added-notes": "\n\t\t\t\t<u><i>Poznámka:</i></u>\n\t\t\t\t<li>Kdokoliv se může připojit, když zná jméno místnosti</li>\n\t\t\t\t<li>Není doporučeno mít v místnosti víc než 4 lidi kvůli náročnosti na Váš počítač, ale za zkoušku nic nedáte.</li>\n\t\t\t\t<li>iOS zařízení jsou omezena pouze na dva účastníky. Toto omezení je způsobeno Applem.</li>\n\t\t\t\t<li> \"Nahrávat\" je nová a EXPERIMENTÁLNÍ funkce.</li>\n\t\t\t\t<li>Musíte \"Přidat\" zdroj video do \"Skupinová scéna\" aby se tu zobrazil.</li>\n\t\t\t\t<li>Tady je nové \"vylepšený fullscreen\" tlačítko přidané pro hostitele.</li>\n\t\t\t\t",
"advanced-paramaters": "Pokročilé nastavení",
"audio-sources": "Audio zdroje",
"back": "Zpět",
"balanced": "Vyrovnaný",
"copy-this-url": "Zkopírujte tuhle URL do OBS \"Browser Source\"",
"copy-to-clipboard": "Kopírovat do schránky",
"create-reusable-invite": "Vytvořit pozvánku na více použití",
"enable-stereo-and-pro": "Povolit Stereo a Pro HD Audio",
"enter-the-rooms-control": "Vstoupit do administrace místnosti",
"force-vp9-video-codec": "Vynutit VP9 Video Codec (méně artefaktů)",
"generate-invite-link": "GENEROVAT POZVÁNKU",
"here-you-can-pre-generate": "Zde můžete generovat linky do OBS i pozvánky na více použití.",
"high-security-mode": "Vysoce zabezpečený mód",
"info-blob": "\n\t\t\t\t\t\t<h2>Co je OBS.Ninja</h2><br>\n\t\t\t\t\t\t<li>100% <b>zdarma</b>; bez stahování; žádné osobní data; bez přihlašování</li>\n\t\t\t\t\t\t<li>Sdílejte video ze smartphonu, laptopu, počítače či svých kamarádů přímo do OBSka</li>\n\t\t\t\t\t\t<li>Používáme nejmodernější Peer-to-Peer forwarding technologii, která zaručuje bezpečnost a minimální lag</li>\n\t\t\t\t\t\t<br>\n\t\t\t\t\t\t<li>Youtube video <i class=\"fa fa-youtube-play\" aria-hidden=\"true\"></i> <a href=\"https://www.youtube.com/watch?v=6R_sQKxFAhg\">Demo</a> </li>\n\t\t\t\t\t\t<li>Open-source kód je dostupný zde: <i class=\"fa fa-github\" aria-hidden=\"true\"></i> <a href=\"https://github.com/steveseguin/obsninja\">https://github.com/steveseguin/obsninja</a> </li>\n\t\t\t\t\t\t<br>\n\t\t\t\t\t\t<i><font style=\"color:red\">Známé problémy:</font></i><br>\n\n\t\t\t\t\t\t<li><i class=\"fa fa-apple\" aria-hidden=\"true\"></i> Uživatelé MacOS musí používat OBS v23 nebo pozdější pro spřávné zachyceení <i>okna</i> prohlížeč Chrome s OBS v25</li>\n\t\t\t\t\t\t<li>Pokud máte problémy s \"pixelací\" videa. Prosím přidejte URL parameter <b>&amp;codec=vp9</b> do OBS Linku pro nápravu.</li>\n\t<h3><i>Koukněte na <a href=\"https://www.reddit.com/r/OBSNinja/\">sub-reddit</a> <i class=\"fa fa-reddit-alien\" aria-hidden=\"true\"></i> for pomoc a návody. Jsem také na <a href=\"https://discord.gg/EksyhGA\">Discord</a> a můžete mi psát na steve@seguin.email. Přeložil do CZ: <a href=\"http://karelvitek.cz\">Karel Vítek</a></i></h3>\n\t\t\t\t\t",
"joining-room": "Připojujete se",
"logo-header": "<font id=\"qos\" style=\"color: white;\">O</font>BS.Ninja ",
"max-resolution": "MAX rozlišení",
"mute": "Ztišit",
"no-audio": "Žádné Audio",
"note-share-audio": "\n\t\t\t\t\t<b>poznámka</b>: Nezapomeňte zakliknout \"Sdílet audio\" v Chromu.<br>(Firefox nepodporuje sdílení zvuku.)",
"open-in-new-tab": "Otevřít v nové záložce",
"record": "Nahrávat",
"remote-control-for-obs": "Vzdálené ovládání OBS",
"remote-screenshare-obs": "Vzdálené sdílení obrazovky do OBS",
"room-name": "Jméno místnost",
"rooms-allow-for": "Místnosti umožnůjí jednoduchý skupinový chat a pokročilou správu více streamů zároveň.",
"select-audio-source": "Zvolit zdroj Audia",
"select-audio-video": "Níže vyberte audio/video zdroj",
"select-screen-to-share": "VYBRAT OBRAZOVKU KE SDÍLENÍ",
"show-tips": "Zobrazit tipy..",
"smooth-cool": "Super and Cool",
"unlock-video-bitrate": "Rozvolnit limit Video Bitrate (20mbps)",
"video-source": "Video zdroj",
"volume": "Hlasitost",
"you-are-in-the-control-center": "Jsi v administraci místnosti",
"waiting-for-camera": "Čekám na načtení kamery",
"video-resolution": "Rozlišení videa: ",
"hide-screen-share": "Nezobrazovat možnost sdílet obrazovku",
"allow-remote-control": "Vzdálené ovládání přiblížení (android)",
"add-the-guest-to-a-room": " Přidat hosta do místosti:",
"invite-group-chat-type": "Člen této místnosti může:",
"can-see-and-hear": "Slyšet a vidět ostatní členy",
"can-hear-only": "Pouze slyšet ostatní členy",
"cant-see-or-hear": "Neslyšet ani nevidět ostatní členy",
"password-input-field": "Heslo",
"select-output-source": " Audio výstup: \n\t\t\t\t\t",
"add-a-password-to-stream": " Přidat heslo:",
"welcome-to-obs-ninja-chat": "\n\t\t\t\t\tVítejte na OBS.Ninja! můžete ihned poslat zprávy ostatním členům této místnosti\n\t\t\t\t",
"names-and-labels-coming-soon": "\n\t\t\t\t\tJména členů bude jedna z budoucích funkcí OBS.ninja.\n\t\t\t\t",
"send-chat": "Poslat",
"available-languages": "Dostupné jazyky:",
"add-more-here": "Přidat další!",
"invite-users-to-join": "Pozvat členy do místnosti a sdílet jejich feed. Tito členové uvidí všechny ostatní členy a jejich feedy.",
"link-to-invite-camera": "Pozvat členy do místnosti a sdílet jejich feed. Tito pouze sdílí avšak nic neuvidí ani neuslyší od ostatních.",
"this-is-obs-browser-source-link": "Tohle je link do OBS Browser Source link který je ve výchozím nastavení prázdný. Členové místnosti do této scény mohou být přidáni manuálně.",
"this-is-obs-browser-souce-link-auto": "Tohle je taky OBS Browser Source link. Všichni členové této místnosti tam jsou přidání antomaticky (vhodné např. na konference)",
"click-for-quick-room-overview": "❔ Klidni zde pro krátký přehled o funkcích",
"push-to-talk-enable": "🔊 Povolit administrátorovi Push-to-Talk mód",
"welcome-to-control-room": "Welcome. This is the control-room for the group-chat. There are different things you can use this room for:<br><br>\t<li>You can host a group chat with friends using a room. Share the blue link to invite guests who will join the chat automatically.</li>\t<li>A group room can handle around 4 to 30 guests, depending on numerous factors, including CPU and available bandwidth of all guests in the room.</li>\t<li>Solo-views of each video are offered under videos as they load. These can be used within an OBS Browser Source.</li>\t<li>You can use the auto-mixing Group Scene, the green link, to auto arrange multiple videos for you in OBS.</li>\t<li>You can use this control room to record isolated video or audio streams, but it is an experimental feature still.</li>\t<li>Videos in the Director's room will be of low quality on purpose; to save bandwidth/CPU</li>\t<li>Guest's in the room will see each other's videos at a very limited quality to conserve bandwidth/CPU.</li>\t<li>OBS will see a guest's video in high-quality; the default video bitrate is 2500kbps.</li>\t<br>\tAs guests join, their videos will appear below. You can bring their video streams into OBS as solo-scenes or you can add them to the Group Scene.\t<br>The Group Scene auto-mixes videos that have been added to the group scene. Please note that the Auto-Mixer requires guests be manually added to it for them to appear in it; they are not added automatically.<br><br>Apple mobile devices, such as iPhones and iPads, do not fully support Video Group Chat. This is a hardware constraint.<br><br>\tFor advanced options and parameters, <a href=\"https://github.com/steveseguin/obsninja/wiki/Guides-and-How-to's#urlparameters\">see the Wiki.</a>",
"guest-will-appaer-here-on-join": "(Zde se zobrazí členové až se připojí)",
"SOLO-LINK": "SOLO LINK pro OBS:"
}

View File

@ -64,4 +64,4 @@
"push-to-talk-enable": "🔊 Ativar Push-to-talk do diretor",
"welcome-to-control-room": "Bem-vindo. Esta é a sala de controlo para o chat de grupo. Há diferentes coisas que pode fazer aqui:<br><br>\t<li>Pode hospedar um chat de grupo com amigos. Partilhe o link azul para os convidados se juntarem ao chat de forma automática.</li>\t<li>Uma sala de grupo pode hospedar entre 4 a 30 4 to 30 convidados, dependendo de inúmeros factores, incluindo CPU e largura de banda de todos os convidados na sala.</li>\t<li>Visualizações individuais de cada vídeo serão mostradas quando carregam. Estas podem ser usadas em Fontes do tipo Browser no OBS.</li>\t<li>Pode usar a cena de grupo automática, o link verde, para dispôr automaticamente os vídeos por si no OBS.</li>\t<li>Pode usar esta sala de controlo para gravar streams isolados de vídeo ou áudio, mas isto é ainda experimental.</li>\t<li>Vídeos na sala de controle são de baixa qualidade propositadamente; para poupar largura de banda/CPU</li>\t<li>Convidados na sala irão ver-se numa qualidade muito reduzida para conservar largura de banda/CPU.</li>\t<li>OBS tem acesso ao vídeo do convidado em alta qualidade; o bitrate de vídeo por omissão é 2500kbps.</li>\t<br>\tÀ medida que os convidados entram, os seus vídeos são mostrados abaixo. Pode levar os seus sinais para o OBS como cenas individuais ou pode adicioná-los à cena de grupo.\t<br>A Cena de grupo auto-mistura vídeos que lhe forem adicionados. Note que a auto-mistura requer que os convidados sejam manualmente adicionados; não são adicionados automaticamente.<br><br>Dispositivos móveis Apple, como iPhones e iPads, não suportam totalmente o Chat de Grupo. Este é um constrangimento de hardware.<br><br>\tPara opções avançadas e parâmetros, <a href=\"https://github.com/steveseguin/obsninja/wiki/Guides-and-How-to's#urlparameters\">veja o Wiki.</a>", "guest-will-appaer-here-on-join": "(Aparece aqui o vídeo quando um convidado entrar)",
"SOLO-LINK": "Link individual para OBS:"
}
}

View File

@ -65,4 +65,4 @@
"welcome-to-control-room": "Welcome. This is the control-room for the group-chat. There are different things you can use this room for:<br><br>\t<li>You can host a group chat with friends using a room. Share the blue link to invite guests who will join the chat automatically.</li>\t<li>A group room can handle around 4 to 30 guests, depending on numerous factors, including CPU and available bandwidth of all guests in the room.</li>\t<li>Solo-views of each video are offered under videos as they load. These can be used within an OBS Browser Source.</li>\t<li>You can use the auto-mixing Group Scene, the green link, to auto arrange multiple videos for you in OBS.</li>\t<li>You can use this control room to record isolated video or audio streams, but it is an experimental feature still.</li>\t<li>Videos in the Director's room will be of low quality on purpose; to save bandwidth/CPU</li>\t<li>Guest's in the room will see each other's videos at a very limited quality to conserve bandwidth/CPU.</li>\t<li>OBS will see a guest's video in high-quality; the default video bitrate is 2500kbps.</li>\t<br>\tAs guests join, their videos will appear below. You can bring their video streams into OBS as solo-scenes or you can add them to the Group Scene.\t<br>The Group Scene auto-mixes videos that have been added to the group scene. Please note that the Auto-Mixer requires guests be manually added to it for them to appear in it; they are not added automatically.<br><br>Apple mobile devices, such as iPhones and iPads, do not fully support Video Group Chat. This is a hardware constraint.<br><br>\tFor advanced options and parameters, <a href=\"https://github.com/steveseguin/obsninja/wiki/Guides-and-How-to's#urlparameters\">see the Wiki.</a>",
"guest-will-appaer-here-on-join": "(Видео появится здесь, когда гость присоединится)",
"SOLO-LINK": "ПЕРСОНАЛЬНАЯ ССЫЛКА для OBS:"
}
}

68
translations/tr.json Normal file
View File

@ -0,0 +1,68 @@
{
"GO": "BAŞLA",
"add-group-chat": "OBS'ye Grup Konuşması Ekle",
"add-to-group": "Grup Sahnesine Ekle",
"add-your-camera": "Kamera'nı OBS'ye Ekle",
"added-notes": "\n\t\t\t\t<u><i>Ek Notlar:</i></u>\n\t\t\t\t<li>Odanın ismini bilen herkes giriş yapabilir, bu yüzden olabildiğince özgün bir isim seçin.</li>\n\t\t\t\t<li>Performans sebeplerinden ötürü bir odada dört (4) kişiden fazla olmasını tavsiye etmiyoruz, ancak bu donanımınızla ölçeklenen bir durumdur.</li>\n\t\t\t\t<li>iOS cihazları sadece iki (2) kişilik gruplarla sınırldır, bu bir donanım sınırlamasıdır.</li>\n\t\t\t\t<li>\"Kayır\" seçeneği yeni ve deneyseldir.</li>\n\t\t\t\t<li>Görünebilmesi için \"Grup Sahnesine\" bir kamera akışı \"eklemeniz\" gerekiyor.</li>\n\t\t\t\t<li>Misafirlerin ekranlarına yeni bir \"geliştirilmiş tam ekran\" düğmesi eklendi.</li>\n\t\t\t\t",
"advanced-paramaters": "Gelişimişi Özellikler",
"audio-sources": "Ses Kaynakları",
"back": "Geri",
"balanced": "Dengeli",
"copy-this-url": "Bu URL'yi bir OBS \"Tarayıcı Kaynağına\" kopyalayın",
"copy-to-clipboard": "Panoya Kopyala",
"create-reusable-invite": "Yeniden Kullanılabilir Davet Oluştur",
"enable-stereo-and-pro": "Stereo ve Pro HD Sesi Etkinleştir",
"enter-the-rooms-control": "Oda'nın Kontrol Merkezine Gir",
"force-vp9-video-codec": "VP9 Codec'e Zorla (görüntüde daha az bozulma)",
"generate-invite-link": "DAVET BAĞLANTISINI OLUŞTUR",
"here-you-can-pre-generate": "Burada tekrar kullanılabilir bir Tarayıcı Kaynak bağlantısı ve onunla ilişkili misafir davet bağlantısı oluşturabilirsin.",
"high-security-mode": "Yüksek Güvenlik Modu",
"info-blob": "\n\t\t\t\t\t\t<h2>OBS.Ninja Nedir?</h2><br>\n\t\t\t\t\t\t<li>100% <b>bedava</b>; indirme yok; kişisel veri toplama yok; giriş yok</li>\n\t\t\t\t\t\t<li>Bilgisayarınızdan, dizüstünden, telefonunuzdan - hatta arkadaşlarınızdan görüntüleri OBS akışınızın içine alın</li>\n\t\t\t\t\t\t<li>Biz yeni nesil Peer-to-Peer (Kişiden-Kişiye) yönlendirme teknolojisi kullanıyoruz, bu sayede çok düşük gecikme ve gizlilik sağlayabiliyoruz</li>\n\t\t\t\t\t\t<br>\n\t\t\t\t\t\t<li>Youtube video <i class=\"fa fa-youtube-play\" aria-hidden=\"true\"></i> <a href=\"https://www.youtube.com/watch?v=6R_sQKxFAhg\">Burada demoyu görebilirsiniz (ingilizce)</a> </li>\n\t\t\t\t\t\t<li>Koda buradan erişebilirsiniz: <i class=\"fa fa-github\" aria-hidden=\"true\"></i> <a href=\"https://github.com/steveseguin/obsninja\">https://github.com/steveseguin/obsninja</a> </li>\n\t\t\t\t\t\t<br>\n\t\t\t\t\t\t<i><font style=\"color:red\">Known issues:</font></i><br>\n\n\t\t\t\t\t\t<li><i class=\"fa fa-apple\" aria-hidden=\"true\"></i> MacOS kullanıcıları OBS v23 kullanmalı, v25 kullanırlarsa Chrome ile <i>pencere yakalama</i> yöntemine baş vurmak zorunda kalabilirler.</li>\n\t\t\t\t\t\t<li>Bazı kullanıcıların videolarla ilgili \"pikselasyon\" problemleri olacaktır. Bu durumda OBS tarayıcı bağlantı URL'sine şu parametreyi ekleyin <b>&amp;codec=vp9</b>.</li>\n\t<h3><i>Yardım ve daha fazla bilgi için Reddit'de <a href=\"https://www.reddit.com/r/OBSNinja/\">sub-reddit</a> <i class=\"fa fa-reddit-alien\" aria-hidden=\"true\"></i>'ine göz atın. Beni ayrıca <a href=\"https://discord.gg/EksyhGA\">Discord'da</a> bulabilirsiniz, e-mail ile ulaşmak için: steve@seguin.email</i></h3>\n\t\t\t\t\t",
"joining-room": "Odaya bağlanıyorsunuz",
"logo-header": "<font id=\"qos\" style=\"color: white;\">O</font>BS.Ninja ",
"max-resolution": "Maksimum Çözünürlük",
"mute": "Sesi Kıs",
"no-audio": "Sessiz",
"note-share-audio": "\n\t\t\t\t\t<b>note</b>: Chrome'da \"Sesi paylaş\" 'ı seçmeyi unutma.<br>(Firefox ses paylaşımını desteklemiyor.)",
"open-in-new-tab": "Yeni Sekmede Aç",
"record": "Kaydet",
"remote-control-for-obs": "OBS için Uzaktan Kontrol",
"remote-screenshare-obs": "OBS'ye uzaktan ekran paylaşımı",
"room-name": "Oda İsmi",
"rooms-allow-for": "Odalar basit konuşma ve sohbet'in yanında çoklu video akışların gelişmiş yönetimini de sağlar.",
"select-audio-source": "Ses Kaynaklarını Seçin",
"select-audio-video": "Ses/Görüntü kaynağını aşağıdan seçin",
"select-screen-to-share": "PAYLAŞILACAK EKRANI SEÇİN",
"show-tips": "Bana ipuları göster",
"smooth-cool": "Pürüzsüz ve Soğukkanlı",
"unlock-video-bitrate": "Video Bitrate Sınırını Kaldır (20mbps)",
"video-source": "Video kaynağı",
"volume": "Ses düzeyi",
"you-are-in-the-control-center": "Odanın kontrol merkezindesiniz",
"waiting-for-camera": "Kameranın yüklenmesi bekleniyor",
"video-resolution": "Video Çözünürlüğü: ",
"hide-screen-share": "Ekran Paylaşma'yı Gizle",
"allow-remote-control": "Kamera Zoom'una Uzaktan Kontrol (android)",
"add-the-guest-to-a-room": " Odaya Misafiri Ekle:",
"invite-group-chat-type": "Bu oda misafiri:",
"can-see-and-hear": "Grup konuşmasını görebilir ve duyabilir",
"can-hear-only": "Grup konunşmasını sadece duyabilir",
"cant-see-or-hear": "Gup konuşmasını duyamaz ve göremez",
"password-input-field": "Şifre",
"select-output-source": " Ses Çıkışı: \n\t\t\t\t\t",
"add-a-password-to-stream": " Şifre Ekle:",
"welcome-to-obs-ninja-chat": "\n\t\t\t\t\tOBS.Ninja'ya hoş geldin! Bağlı olan kişilere buradan yazılı mesajlar gönderebilirsin.\n\t\t\t\t",
"names-and-labels-coming-soon": "\n\t\t\t\t\tBağlanan kişileri tanımlayan isimler ileriki bir geliştirmede yer alacak.\n\t\t\t\t",
"send-chat": "Gönder",
"available-languages": "Diller:",
"add-more-here": "Daha fazla ekle!",
"invite-users-to-join": "Gruba katılmaları ve akışlarını yansıtmaları için misafirleri davetet. Bu kullanıcılar odada yer alan tüm akışları görebilecek.",
"link-to-invite-camera": "Akışlarını gruba yansıtmaları için misafir bağlantısı. Bu kullanıcılar diğer akışları duyamayacak ve göremeyecek.",
"this-is-obs-browser-source-link": "Bu boş bir OBS tarayıcı kaynak bağlantısıdır. Odada yer alan videolar el ile buraya eklenebilir.",
"this-is-obs-browser-souce-link-auto": "Yine bir OBS tarayıcı kaynak bağlantısı. Bu odada yer alan tüm misafirler otomatik olarak bu sahneye eklenecektir.",
"click-for-quick-room-overview": "❔ Buraya tıklayarak hızlı yardım ve genel bakışa göz atın",
"push-to-talk-enable": "🔊 Yönetmen bas-konuş'u etkinleştir",
"welcome-to-control-room": "Hoş geldiniz. Bu grup konuşması için kontrol odasıdır. Bu odayı farklı amaçlar için kullanabilirsiniz:<br><br>\t<li>Arkadaşlarınız ile grup konunşması yapmak için bir oda kullanabilirsiniz. Otomatik olarak gruba dahil etmek için misafirleriniz ile mavi bağlantıyı paylaşın.</li>\t<li>Bir grup odası 4 - 30 sayıda misafir ağırlayabilir. Ancak bu bir çok etkene göre değişebilir, yeterli CPU ve internet bant genişliği gibi.</li>\t<li>Her videonun tekil görüntüsü bağlantıları misafirler bağlandıkça videolarının altında yer alacak. Bunları OBS tarayıcı kaynağı olarak kullanabilirsiniz.</li>\t<li>Otomatik-karıştırma grup sahnesi (yeşil bağlantı) bir çok videoyu OBS'de otomatik ayarlamak için kullanabilirsiniz.</li>\t<li>Bu odayı kullanarak her bir video için ayrı ayrı video ve ses kaynaklarını kaydedebilirsiniz, ancak bu özellik halen deneysel aşamadadır.</li>\t<li>Yönetmen odasında yer alan videolar kasten düşük kalitede tutulmuştur; CPU ve internetbant genişliğinden tasarruf için</li>\t<li>Odada yer alan misafirler, CPU ve internetten tasarruf etmek amacıyla bir birlerinin videolarını düşük kalitede görecek.</li>\t<li>OBS misafirlerin videolarını çok yüksek kalitede alacak, varsayılan kalite 2500kbps'dir.</li>\t<br>\tMisafirler eklendikçe videoları aşağıda belirecek. OBS'ye videolarını tekil sahneler olarak ekleyebilir, ya da grup sahnelerine ekleyebilirsiniz.\t<br>Grup sahnesi, eklenmiş videoları otomatik olarak karıştırır. Otomatik karıştırmanın çalışması için misafirlerin el ile bu sahneye eklenmesi gerektiğini unutmayın; otomatik olarak sahnelere eklenmeyeceklerdir.<br><br>iPhone iPad gibi Apple mobil cihazlar, tam olarak video grup görüşmeyi desteklemiyor. Bu bir donanım sınırlamasıdır.<br><br>\tGekişmiş özellik ve parametreler için <a href=\"https://github.com/steveseguin/obsninja/wiki/Guides-and-How-to's#urlparameters\">Wiki'ye göz atın.</a>",
"guest-will-appaer-here-on-join": "(Bir misafir bağlandığında burada bir video görünecek)",
"SOLO-LINK": "OBS İÇİN TEKİL BAĞLANTI:"
}

File diff suppressed because one or more lines are too long