version v15.1

This commit is contained in:
Steve Seguin 2021-01-27 10:05:42 -05:00 committed by GitHub
parent 2d0908f326
commit 5b86a4f906
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 10171 additions and 9037 deletions

View File

@ -1,37 +1,33 @@
$("body").append('<style id="lightbox-animations" type="text/css"></style>');
$(".column").on('click', function() {
if ($(this).hasClass("skip-animation")){
if ( $(this).hasClass( "skip-animation" )){
return;
}
const bounding_box = $(this).get(0).getBoundingClientRect();
$(this).css({ top: `${bounding_box.top}px`, left: `${bounding_box.left - 20}px` });
var bounding_box = $(this).get(0).getBoundingClientRect();
$(this).css({ top: bounding_box.top + 'px', left: bounding_box.left -20+ 'px' });
$(this).addClass('in-animation').removeClass('pointer');
$("#empty-container").remove();
$('<div id="empty-container" class="column"></div>').insertAfter(this);
const styles = `
@keyframes outlightbox {
0% {
height: 100%;
width: 100%;
top: 0px;
left: 0px;
}
50% {
height: 200px;
top: ${bounding_box.y}px;
}
100% {
height: 200px;
width: ${bounding_box.width}px;
top: ${bounding_box.y}px;
left: ${bounding_box.x}px;
}
}
`;
var styles = '';
styles = '@keyframes outlightbox {';
styles += '0% {';
styles += 'height: 100%;';
styles += 'width: 100%;';
styles += 'top: 0px;';
styles += 'left: 0px;';
styles += '}';
styles += '50% {';
styles += 'height: 200px;';
styles += 'top: ' + bounding_box.y + 'px;';
styles += '}';
styles += '100% {';
styles += 'height: 200px;';
styles += 'width: '+bounding_box.width+'px;';
styles += 'top: ' + bounding_box.y + 'px;';
styles += 'left: ' + bounding_box.x + 'px;';
styles += '}';
styles += '}';
$("#lightbox-animations").empty();
$("#lightbox-animations").get(0).sheet.insertRule(styles, 0);
@ -42,20 +38,19 @@ $(".close").on('click', function(e) {
$(this).hide();
$(".container-inner").hide();
$("body").css('overflow', 'auto');
const bounding_box = $(this).parent().get(0).getBoundingClientRect();
$(this).parent().css({ top: `${bounding_box.top}px`, left: `${bounding_box.left}px` });
var bounding_box = $(this).parent().get(0).getBoundingClientRect();
$(this).parent().css({ top: bounding_box.top + 'px', left: bounding_box.left + 'px' });
$(this).parent().addClass('out-animation');
cleanupMediaTracks();
e.stopPropagation();
});
$(".column").on('animationend', function(e){
if (e.originalEvent.animationName === 'inlightbox') {
if (e.originalEvent.animationName == 'inlightbox') {
$(this).children(".close").show();
$(this).children(".container-inner").show();
}
else if (e.originalEvent.animationName === 'outlightbox') {
else if (e.originalEvent.animationName == 'outlightbox') {
$(this).removeClass('in-animation').removeClass('out-animation').removeClass('columnfade').addClass('pointer');
$("#empty-container").remove();
$("#lightbox-animations").get(0).sheet.deleteRule(0);
@ -63,64 +58,64 @@ $(".column").on('animationend', function(e){
});
$('#audioSource').on('mousedown touchend focusin focusout', (_e) => {
const state = $('#multiselect-trigger').data('state') || 0;
if (state === 0) {
$('#multiselect-trigger').data('state', '1').addClass('open').removeClass('closed');
$('#multiselect-trigger').find('.chevron').removeClass('bottom');
$('#multiselect-trigger').parent().find('.multiselect-contents').show();
$('#multiselect-trigger').parent().find('.multiselect-contents').find('input[type="checkbox"]').parent().show();
$('#multiselect-trigger').parent().find('.multiselect-contents').find('input[type="checkbox"]').show();
}
$('#audioSource').on('mousedown touchend focusin focusout', function(e) {
var state = $('#multiselect-trigger').data('state') || 0;
if( state == 0 ) {
$('#multiselect-trigger').data('state', '1').addClass('open').removeClass('closed');
$('#multiselect-trigger').find('.chevron').removeClass('bottom');
$('#multiselect-trigger').parent().find('.multiselect-contents').show();
$('#multiselect-trigger').parent().find('.multiselect-contents').find('input[type="checkbox"]').parent().show();;
$('#multiselect-trigger').parent().find('.multiselect-contents').find('input[type="checkbox"]').show();;
}
});
$('#audioSource3').on('mousedown touchend focusin focusout', (_e) => {
const state = $('#multiselect-trigger3').attr('data-state') || 0;
if (state === 0) {
$('#multiselect-trigger3').attr('data-state', '1').addClass('open').removeClass('closed');
$('#multiselect-trigger3').find('.chevron').removeClass('bottom');
$('#multiselect-trigger3').parent().find('.multiselect-contents').show();
$('#multiselect-trigger3').parent().find('.multiselect-contents').find('input[type="checkbox"]').parent().show();
$('#multiselect-trigger3').parent().find('.multiselect-contents').find('input[type="checkbox"]').show();
}
$('#audioSource3').on('mousedown touchend focusin focusout', function(e) {
var state = $('#multiselect-trigger3').attr('data-state') || 0;
if( state == 0 ) {
$('#multiselect-trigger3').attr('data-state', '1').addClass('open').removeClass('closed');
$('#multiselect-trigger3').find('.chevron').removeClass('bottom');
$('#multiselect-trigger3').parent().find('.multiselect-contents').show();
$('#multiselect-trigger3').parent().find('.multiselect-contents').find('input[type="checkbox"]').parent().show();;
$('#multiselect-trigger3').parent().find('.multiselect-contents').find('input[type="checkbox"]').show();;
}
});
$('#multiselect-trigger').on('mousedown touchend focusin focusout', function(e) {
const state = $(this).data('state') || 0;
if( state === 0 ) {
var state = $(this).data('state') || 0;
if( state == 0 ) {
// open the dropdown
$(this).data('state', '1').addClass('open').removeClass('closed');
$(this).find('.chevron').removeClass('bottom');
$(this).parent().find('.multiselect-contents').show();
$(this).parent().find('.multiselect-contents').find('input[type="checkbox"]').parent().show();
$(this).parent().find('.multiselect-contents').find('input[type="checkbox"]').show();
$(this).parent().find('.multiselect-contents').find('input[type="checkbox"]').parent().show();;
$(this).parent().find('.multiselect-contents').find('input[type="checkbox"]').show();;
} else {
// close the dropdown
$(this).data('state', '0').addClass('closed').removeClass('open');
$(this).find('.chevron').addClass('bottom');
$(this).parent().find('.multiselect-contents').find('input[type="checkbox"]').not(":checked").parent().hide();
$(this).parent().find('.multiselect-contents').find('input[type="checkbox"]').hide();
$(this).parent().find('.multiselect-contents').find('input[type="checkbox"]').not(":checked").parent().hide();;
$(this).parent().find('.multiselect-contents').find('input[type="checkbox"]').hide();;
}
e.preventDefault();
});
// multiselect dropdowns
$('#multiselect-trigger3').on('mousedown touchend focusin focusout', function(e) {
const state = $(this).attr('data-state') || 0;
var state = $(this).attr('data-state') || 0;
if(state === 0) {
if( state == 0 ) {
// open the dropdown
errorlog(`STATE: ${state}`);
errorlog("STATE: "+state);
$(this).attr('data-state', '1').addClass('open').removeClass('closed');
$(this).find('.chevron').removeClass('bottom');
$(this).parent().find('.multiselect-contents').show();
$(this).parent().find('.multiselect-contents').find('input[type="checkbox"]').parent().show();
$(this).parent().find('.multiselect-contents').find('input[type="checkbox"]').show();
$(this).parent().find('.multiselect-contents').find('input[type="checkbox"]').parent().show();;
$(this).parent().find('.multiselect-contents').find('input[type="checkbox"]').show();;
} else {
// close the dropdown
$(this).attr('data-state', '0').addClass('closed').removeClass('open');
$(this).find('.chevron').addClass('bottom');
$(this).parent().find('.multiselect-contents').find('input[type="checkbox"]').not(":checked").parent().hide();
$(this).parent().find('.multiselect-contents').find('input[type="checkbox"]').hide();
$(this).parent().find('.multiselect-contents').find('input[type="checkbox"]').not(":checked").parent().hide();;
$(this).parent().find('.multiselect-contents').find('input[type="checkbox"]').hide();;
}
e.preventDefault();
});

View File

@ -1,20 +1,76 @@
<body>
<head>
<link rel="stylesheet" href="./main.css?ver=39" />
<style>
body {
}
input {padding:10px;}
h3 { margin-top:10px; padding:5px;}
hr {
margin: 20px 0;
}
video{
max-width:640px;
max-height:360px;
padding:20px;
}
audio{
max-width:640px;
max-height:360px;
padding:20px;
}
</style>
</head>
<body style='color:white'>
<video id="player" controls style="display:none"></video>
<audio id="player2" controls style="display:none"></audio>
<div id="info">
<h3>This tool can be used to convert WebM videos of dynamic resolution to MP4 files of a fixed 1280x720 resolution.</h3> Just select a video file and wait. It takes about 60-seconds to transcode 1-second of video. Very sloowww...<br />
<p>You can use FFMpeg locally to achieve much faster results.</p>
<p>This tool performs the following action in your browser: <i>fmpeg -i input.webm -vf scale=1280:720 output.mp4</i><p>
<h1>Web-based Media Conversion Tools</h1>
<hr>
<h3>Transcodes WebM files to MP4 files with a fixed 1280x720 resolution. (very slow!)</h3><br />
<small><p>This tool performs the following action in your browser: <i> fmpeg -i input.webm -vf scale=1280:720 output.mp4</i></p></small>
<input type="file" id="uploader" title="Convert WebM to MP4">
<hr>
<h3>Bonus: This option converts MKV files to MP4 files without transcoding.</h3> </p><i>fmpeg -i INPUTFILE -vcodec copy -acodec copy output.mp4</i>
<br /><br /><input type="file" id="uploader2" accept=".mkv" title="Convert MKV to MP4">
<p>You can use FFMpeg locally to achieve much faster results with either option.</p>
<h3>This option converts WebM files to MP4 files without transcoding, and attempting to force high resolutions.
<br /><br /><input type="file" id="uploader3" accept=".webm" title="Convert WebM to MP4">
</div>
<hr>
<h3>Remuxes MKV files to MP4 files without transcoding.</h3> </p><br /><small><i> fmpeg -i INPUTFILE -vcodec copy -acodec copy output.mp4</i></small>
<br /><input type="file" id="uploader2" accept=".mkv" title="Convert MKV to MP4">
<hr>
<h3>Remuxes WebM files to MP4 files without transcoding (attempts to force high resolutions, also)</h3>
<input type="file" id="uploader3" accept=".webm" title="Convert WebM to MP4">
<hr>
<h3>Remuxes WebM to Audio-only files (opus or wav)</h3>
<input type="file" id="uploader4" accept=".webm" title="Convert WebM to OPUS">
<hr>
</div>
<script src="https://unpkg.com/@ffmpeg/ffmpeg@0.9.6/dist/ffmpeg.min.js"></script>
<script>
function download(data, filename) {
const blob = new Blob([data.buffer]);
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
setTimeout(() => {
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}, 100);
}
const { createFFmpeg, fetchFile } = FFmpeg;
const ffmpeg = createFFmpeg({ log: true });
const transcode = async ({ target: { files } }) => {
@ -44,6 +100,7 @@
const data = ffmpeg.FS('readFile', 'output.mp4');
const video = document.getElementById('player');
video.src = URL.createObjectURL(new Blob([data.buffer], { type: 'video/mp4' }));
video.style.display="block";
document.getElementById('info').innerText = "Operation Done. Play video or download it.";
}
@ -67,8 +124,35 @@
document.getElementById('info').innerText = "Operation Done. Play video or download it.";
}
const convertToAudioOnly = async ({ target: { files } }) => {
const { name } = files[0];
document.getElementById('info').innerText = "Transcoding file... this will take a while";
await ffmpeg.load();
ffmpeg.FS('writeFile', name, await fetchFile(files[0]));
const video = document.getElementById('player');
await ffmpeg.run('-i', name, '-vn', '-acodec', 'copy', 'output.opus');
const data = ffmpeg.FS('readFile', 'output.opus');
console.log(data.buffer.byteLength);
if (data.buffer.byteLength){
video.src = URL.createObjectURL(new Blob([data.buffer], { type: 'audio/opus' }));
download(data, name.split(".")[0]+".opus");
} else {
delete data;
await ffmpeg.run('-i', name, '-vn', '-acodec', 'copy', 'output.wav');
const data2 = ffmpeg.FS('readFile', 'output.wav');
video.src = URL.createObjectURL(new Blob([data.buffer], { type: 'audio/pcm' }));
download(data2, name.split(".")[0]+".wav");
}
video.style.display="block";
document.getElementById('info').innerText = "Operation Done. Play audio or download it.";
}
document.getElementById('uploader').addEventListener('change', transcode);
document.getElementById('uploader2').addEventListener('change', transmux);
document.getElementById('uploader3').addEventListener('change', force1080);
document.getElementById('uploader4').addEventListener('change', convertToAudioOnly);
</script>
</body>

View File

@ -117,22 +117,25 @@ function generateInvite(){
if (getById("invite_vp9").checked){
viewstr+="&codec=vp9";
}
if (getById("invite_h264").checked){
viewstr+="&codec=h264";
}
if (getById("invite_stereo").checked){
viewstr+="&stereo";
sendstr+="&stereo";
}
if (getById("invite_secure").checked){
sendstr+="&secure";
}
//if (getById("invite_secure").checked){
// sendstr+="&secure";
//}
if (getById("invite_hidescreen").checked){
sendstr+="&webcam";
}
if (getById("invite_remotecontrol").checked){ //
var remote_gen_id = generateStreamID();
sendstr+="&remote="+remote_gen_id; // security
viewstr+="&remote="+remote_gen_id;
}
//if (getById("invite_remotecontrol").checked){ //
// var remote_gen_id = generateStreamID();
// sendstr+="&remote="+remote_gen_id; // security
// viewstr+="&remote="+remote_gen_id;
//}
if (getById("invite_joinroom").value.trim().length){
sendstr+="&room="+getById("invite_joinroom").value.replace(/[\W]+/g,"_");
@ -231,7 +234,9 @@ document.addEventListener("dragstart", event => {
<input type="checkbox" id="invite_bitrate" /><label for="invite_bitrate"> <span data-translate="unlock-video-bitrate">Unlock Video Bitrate (20mbps)</span></label>
<br />
<input type="checkbox" id="invite_vp9" /><label for="invite_vp9"> <span data-translate="force-vp9-video-codec">VP9 Video Codec</span></label>
<input type="checkbox" id="invite_vp9" onclick="getById('invite_h264').checked=false;" /><label for="invite_vp9"> <span data-translate="force-vp9-video-codec">VP9 Video Codec</span></label>
<br />
<input type="checkbox" id="invite_h264" onclick="getById('invite_vp9').checked=false;" /><label for="invite_h264"> <span data-translate="force-h264-video-codec">H264 Video Codec</span></label>
<br />
<input type="checkbox" id="invite_stereo" /><label for="invite_stereo"> <span data-translate="enable-stereo-and-pro">Stereo and Pro HD Audio</span></label>
<br />
@ -245,17 +250,11 @@ document.addEventListener("dragstart", event => {
</select>
<br />
<br />
<input type="checkbox" id="invite_secure" />
<label for="invite_secure"> <span data-translate="high-security-mode">High Security Mode</span></label>
<br />
<input type="checkbox" id="invite_hidescreen" />
<label for="invite_hidescreen"> <span data-translate="hide-screen-share">Hide Screenshare Option</span></label>
<br />
<input type="checkbox" id="invite_remotecontrol" />
<label for="invite_remotecontrol"> <span data-translate="allow-remote-control">Remote Control Camera Zoom</span></label>
<br />
<br />
<label for="videoname">Stream label:</label>
<label for="videoname">Stream Label:</label>
<input id="videoname" placeholder="Give stream a description" />
<br />
<br />

View File

@ -125,7 +125,7 @@ input[type='checkbox']:checked {
<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>
<div id='warning4mac' style="border:2px dotted; display:none;max-width:800px; padding:10px; margin:0 90px 20px 90px;color:white;font-size:1.3em"> ✨ Great News! OBS v26.1.2 <a href="https://github.com/obsproject/obs-browser/issues/209#issuecomment-748683083">now supports</a> OBS.Ninja without needing the Electron Capture app! 🥳</div>
<input type="checkbox" class="check" id="prefervp9" name="prefervp9" value="false" onclick="modURL(this);">
<label for="prefervp9">Force VP9 Codec</label>
@ -164,9 +164,9 @@ input[type='checkbox']:checked {
*
*/
//if (navigator.userAgent.indexOf('Mac OS X') != -1){
// document.getElementById("warning4mac").style.display="block";
//}
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);

View File

@ -52,7 +52,9 @@
<script type="text/javascript" crossorigin="anonymous" src="./thirdparty/qrcode.min.js"></script>
<script type="text/javascript" src="./thirdparty/jquery.min.js"></script>
<script type="text/javascript" src="./thirdparty/aes.js"></script>
<link rel="stylesheet" href="./main.css?ver=30" />
<script type="text/javascript" src="./thirdparty/polyfill.min.js"></script>
<script type="text/javascript" src="./thirdparty/StreamSaver.js"></script>
<link rel="stylesheet" href="./main.css?ver=40" />
</head>
<body id="main" class="hidden">
<span itemprop="image" itemscope itemtype="image/png">
@ -63,7 +65,7 @@
<link itemprop="url" href="./images/obsNinja_logo_full.png" />
</span>
<script type="text/javascript" crossorigin="anonymous" src="./thirdparty/CodecsHandler.js?ver=26"></script>
<script type="text/javascript" crossorigin="anonymous" src="./webrtc.js?ver=146"></script>
<script type="text/javascript" crossorigin="anonymous" src="./webrtc.js?ver=155"></script>
<input id="zoomSlider" type="range" style="display: none;" />
<div id="header">
<a id="logoname" href="./" style="text-decoration: none; color: white; margin: 2px;">
@ -72,8 +74,8 @@
</span>
</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 onclick="jumptoroom();">GO</button>
<input id="joinroomID" autofocus name="joinroomID" size="22" placeholder="Join by Room Name here" alt="Enter a room name to join" title="Enter a room name to quick join" onkeyup="jumptoroom(event)"/>
<button onclick="jumptoroom();" role="button" aria-pressed="false" alt="Join room" title="Join room" >GO</button>
</div>
<div id="head3" style="display: inline-block;" class="advanced">
<font style="color: #888;" id="copythisurl"> &nbsp;
@ -105,32 +107,41 @@
</div>
<div id="controlButtons" >
<div id="chatbutton" title="Toggle the Chat" onclick="toggleChat()" class="advanced float" style="cursor: pointer;" alt="Toggle the Chat">
<i id="chattoggle" class="toggleSize las la-comment-alt my-float"></i>
<div id="chatNotification"></div>
<div id="subControlButtons">
<div id="chatbutton" title="Toggle the Chat" alt="Toggle the Chat" onmousedown="event.preventDefault(); event.stopPropagation();" onclick="toggleChat()" tabindex="16" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="advanced float" style="cursor: pointer;" alt="Toggle the Chat">
<i id="chattoggle" class="toggleSize las la-comment-alt my-float"></i>
<div id="chatNotification"></div>
</div>
<div id="mutespeakerbutton" onmousedown="event.preventDefault(); event.stopPropagation();" title="Mute the Speaker" alt="Mute the Speaker" onclick="toggleSpeakerMute()" tabindex="17" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" 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" onmousedown="event.preventDefault(); event.stopPropagation();" title="Mute the Mic" alt="Mute the Mic" onclick="toggleMute()" tabindex="18" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" 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>
<div id="mutevideobutton" onmousedown="event.preventDefault(); event.stopPropagation();" title="Disable the Camera" alt="Disable the Camera" onclick="toggleVideoMute()" tabindex="19" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="advanced float" style="cursor: pointer;" alt="Toggle the camera">
<i id="mutevideotoggle" onmousedown="event.preventDefault(); event.stopPropagation();" class="toggleSize las la-eye my-float"></i>
</div>
<div id="screensharebutton" onmousedown="event.preventDefault(); event.stopPropagation();" title="Share a Screen with others" alt="Share a Screen with others" onclick="toggleScreenShare()" tabindex="20" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="float advanced" style="cursor: pointer;">
<i id="screensharetoggle" onmousedown="event.preventDefault(); event.stopPropagation();" class="toggleSize las la-desktop my-float"></i>
</div>
<div id="settingsbutton" onmousedown="event.preventDefault(); event.stopPropagation();" title="Settings" alt="Settings" onclick="toggleSettings()" class="advanced float" tabindex="21" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" style="cursor: pointer;" alt="Toggle the Settings Menu">
<i id="settingstoggle" class="toggleSize las la-cog my-float"></i>
</div>
<div id="hangupbutton" onmousedown="event.preventDefault(); event.stopPropagation();" title="Hangup the Call" alt="Hangup the Call" onclick="hangup()" class="advanced float" tabindex="22" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" style="cursor: pointer;" alt="Disconnect and End">
<i class="toggleSize my-float las la-phone rotate225" aria-hidden="true"></i>
</div>
<div id="raisehandbutton" onmousedown="event.preventDefault(); event.stopPropagation();" data-raised="0" title="Alert the host you want to speak" alt="Alert the host you want to speak" tabindex="23" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" onclick="raisehand()" class="advanced float" style="cursor: pointer;">
<i class="toggleSize my-float las la-hand-paper" style="position: relative; right: 1px;" aria-hidden="true"></i>
</div>
<div id="recordLocalbutton" onmousedown="event.preventDefault(); event.stopPropagation();" data-state="0" title="Record your stream to disk" alt="Record your stream to disk" tabindex="24" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" onclick="recordLocalVideoToggle(this);" class="advanced float" style="cursor: pointer;">
<i class="toggleSize my-float las la-dot-circle" style="position: relative;" aria-hidden="true"></i>
</div>
<span id="miniPerformer" style="pointer-events: auto;" class="advanced"></span>
<div id="hangupbutton2" onmousedown="event.preventDefault(); event.stopPropagation();" title="Cancel the Director's Video/Audio" alt="Hangup the Call" onclick="hangup2()" class="advanced float" tabindex="25" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" style="cursor: pointer;" alt="Disconnect Direcotor's cam">
<i class="toggleSize my-float las la-phone rotate225" aria-hidden="true"></i>
</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>
<div id="mutevideobutton" title="Disable the Camera" onclick="toggleVideoMute()" class="advanced float" style="cursor: pointer;" alt="Toggle the camera">
<i id="mutevideotoggle" class="toggleSize las la-eye my-float"></i>
</div>
<div id="screensharebutton" title="Share a Screen with others" onclick="toggleScreenShare()" class="float advanced" style="cursor: pointer;">
<i id="screensharetoggle" class="toggleSize las la-desktop my-float"></i>
</div>
<div id="settingsbutton" title="Settings" onclick="toggleSettings()" class="advanced float" style="cursor: pointer;" alt="Toggle the Settings Menu">
<i id="settingstoggle" class="toggleSize las la-cog my-float"></i>
</div>
<div id="hangupbutton" title="Hangup the Call" onclick="hangup()" class="advanced float" style="cursor: pointer;" alt="Disconnect and End">
<i class="toggleSize my-float las la-phone rotate225" aria-hidden="true"></i>
</div>
<div id="raisehandbutton" data-raised="0" title="Alert the host you want to speak" onclick="raisehand()" class="advanced float" style="cursor: pointer;">
<i class="toggleSize my-float las la-hand-paper" style="position: relative; right: 1px;" aria-hidden="true"></i>
</div>
</div>
<span
@ -154,91 +165,100 @@
<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 pointer card" 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 group-chat and the tools to manage multiple guests.</span>
<br />
<br />
<table >
<tr>
<th style="text-align:right;"><b>
<span data-translate="room-name">Room Name</span>:
</b></th>
<th style="text-align:left;"><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 style="text-align:right;"><b>
<span data-translate="password-input-field">Password</span>:
</b>
</th><th style="text-align:left;">
<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 style="line-height: 4em;">
<th style="text-align:right; padding: 5px;">
<input id="broadcastFlag" type="checkbox" title="For large group rooms, this option can reduce the load on remote guests substaintailly" />
</th><th style="text-align:left;">
<b>
<span data-translate="guests-only-see-director" title="For large group rooms, this option can reduce the load on remote guests substaintailly" >Guests can only see the Director's video</span>
</b>
</th>
</tr><tr>
<th style="text-align:right; padding: 5px;">
<select id="codecGroupFlag" type="checkbox" title="For large group rooms, this option can reduce the load on remote guests substaintailly" >
<option value="default" selected>Default</option>
<option value="vp9">VP9</option>
<option value="h264">H264</option>
<option value="vp8">VP8</option>
</select >
</th>
<th style="text-align:left;">
<b>
<span data-translate="default-codec-select" title="Which video codec would you want used by default?" >Preferred Video Codec</span>
</b>
</th>
</tr><tr>
<th></th>
<th style="text-align:right;">
<button onclick="createRoom()" class="gobutton" style="float: left;" title="You'll enter as the room's director">
<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 />
<ul style=" margin: auto auto; max-width: 500px; display: none; text-align: left;" id="roomnotes">
<div id="container-1" title="Add Group Chat to OBS" alt="Add Group Chat to OBS" tabindex="2" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="column columnfade pointer card" style=" overflow-y: auto;">
<h2>
<span data-translate="add-group-chat">Create a Room</span>
</h2>
<div class="container-inner">
<br />
<span data-translate="added-notes">
<u>
<i>Important Tips:</i><br /><br />
</u>
<li>Disabling video sharing between guests will improve performance</li>
<li>Invite only guests to the room that you trust.</li>
<li>The "Recording" option is considered experimental.</li>
<li><a href="https://params.obs.ninja" style="color:black;"><u>Advanced URL parameters</u></a> are available to customize rooms.</li>
</span>
</ul>
</div>
<div class="outer close">
<div class="inner">
<label class="labelclass">
<span data-translate="back">Back</span>
</label>
<br />
<span data-translate="rooms-allow-for">Rooms allow for group-chat and the tools to manage multiple guests.</span>
<br />
<br />
<table >
<tr>
<th style="text-align:right;">
<b>
<span data-translate="room-name">Room Name</span>:
</b>
</th>
<th style="text-align:left;">
<input id="videoname1" placeholder="Enter a Room Name here" onkeydown="checkStrengthRoom(event, 'securityLevelRoom');" onchange="checkStrengthRoom(event, 'securityLevelRoom');" onkeyup="enterPressed(event, createRoom);" size="30" maxlength="30" style="font-size: 110%; padding: 5px;" />
<div id="securityLevelRoom" style="display:none;margin-top:3px;position:relative;top:3px;font-size:0.8em;"></div>
</th>
</tr><tr>
<th style="text-align:right;">
<b>
<span data-translate="password-input-field">Password</span>:
</b>
</th><th style="text-align:left;">
<input id="passwordRoom" placeholder="Optional room password here" onkeydown="checkStrengthRoom(event, 'securityLevelRoom');" onchange="checkStrengthRoom(event, 'securityLevelRoom');" onkeyup="enterPressed(event, createRoom);" size="30" maxlength="30" style="font-size: 110%; padding: 5px;" />
</th>
</tr><tr style="line-height: 4em;">
<th style="text-align:right; padding: 5px;">
<input id="broadcastFlag" type="checkbox" title="For large group rooms, this option can reduce the load on remote guests substantially" />
</th><th style="text-align:left;">
<b>
<span data-translate="guests-only-see-director" title="For large group rooms, this option can reduce the load on remote guests substantially" >Guests can only see the Director's Video</span>
</b>
</th>
</tr><tr>
<th style="text-align:right; padding: 5px;">
</th>
<th style="text-align:left;">
<b>
<span data-translate="default-codec-select" title="Which video codec would you want used by default?" >Preferred Video Codec: </span>
<select style="font-size:1.1em" id="codecGroupFlag" type="checkbox" title="For large group rooms, this option can reduce the load on remote guests substantially" >
<option value="default" selected>Default</option>
<option value="vp9">VP9</option>
<option value="h264">H264</option>
<option value="vp8">VP8</option>
</select >
</b>
</th>
</tr><tr>
<th></th>
<th style="text-align:right;">
<button onclick="createRoom()" class="gobutton" style="float: left;" alt="Enter the room as the group's director" title="You'll enter as the room's director">
<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 />
<ul style=" margin: auto auto; max-width: 500px; display: none; text-align: left;" id="roomnotes">
<br />
<span data-translate="added-notes">
<u>
<i>Important Tips:</i><br /><br />
</u>
<li>Disabling video sharing between guests will improve performance</li>
<li>Invite only guests to the room that you trust.</li>
<li>The "Recording" option is considered experimental.</li>
<li><a href="https://params.obs.ninja" style="color:black;"><u>Advanced URL parameters</u></a> are available to customize rooms.</li>
</span>
</ul>
</div>
<div class="outer close">
<div class="inner">
<label class="labelclass">
<span data-translate="back">Back</span>
</label>
</div>
</div>
</div>
</div>
<div id="container-3" class="column columnfade pointer card" onclick="previewWebcam()" style=" overflow-y: auto;">
<div id="container-3" title="Add your Camera to OBS" onkeyup="enterPressedClick(event,this);" alt="Add your Camera to OBS" tabindex="3" role="button" aria-pressed="false" class="column columnfade pointer card" onclick="previewWebcam()" style=" overflow-y: auto;">
<h2 id="add_camera">
<span data-translate="add-your-camera">Add your Camera to OBS</span>
</h2>
@ -251,7 +271,7 @@
<button onclick="this.disabled=true;setTimeout(function(){requestBasicPermissions();},20);" id="getPermissions" style="display:none;" data-ready="false" >
<span data-translate="ask-for-permissions">Allow Access to Camera/Microphone</span>
</button>
<button onclick="publishWebcam(this)" id="gowebcam" class="gowebcam" disabled data-ready="false" >
<button onclick="publishWebcam(this)" tabindex="15" id="gowebcam" class="gowebcam" alt="Click this to start streaming when the camera is ready" disabled data-ready="false" >
<span data-translate="waiting-for-camera">Waiting for Camera to Load</span>
</button>
<br />
@ -293,7 +313,7 @@
</span>
</center>
<div id="audioMenu" class="form-group multiselect" alt="tip: Hold CTRL (command) to select Multiple" title="tip: Hold CTRL (command) to select Multiple">
<a id="multiselect-trigger" class="form-control multiselect-trigger" tabindex="3">
<a id="multiselect-trigger" class="form-control multiselect-trigger" >
<div class="audioTitle">
<i class="las la-microphone-alt"></i><span data-translate="select-audio-source"> Audio Source(s) </span>
<i id='chevarrow1' class="chevron bottom" aria-hidden="true"></i>
@ -327,19 +347,18 @@
</div>
</div>
</div>
<div id="container-2" class="column columnfade pointer card" style=" overflow-y: auto;">
<div id="container-2" title="Remote Screenshare into OBS" onkeyup="enterPressedClick(event,this);" alt="Remote Screenshare into OBS" tabindex="4" role="button" aria-pressed="false" class="column columnfade pointer card" style=" overflow-y: auto;">
<h2 id="add_screen">
<span data-translate="remote-screenshare-obs">Remote Screenshare into OBS</span>
</h2>
<div class="container-inner">
<span data-translate="note-share-audio">
<p>
<video id="screenshare" autoplay="true" muted="true" loop src="./images/screenshare.webm" />
<video id="screenshare" autoplay="true" muted="true" loop src="./images/screenshare.webm" ></video>
</p>
</span>
<br />
<button class='gobutton' style="padding: 10px; font-size: 120%;" onclick="publishScreen()">
<button class='gobutton' style="padding: 10px; font-size: 120%;" alt="clilck to select you screen to share" onclick="publishScreen()">
<span data-translate="select-screen-to-share">SELECT SCREEN TO SHARE</span>
</button>
<span id="gear_screen" style="display: inline-block; cursor: pointer;" onclick="toggle(document.getElementById('videoSettings2'));">
@ -373,10 +392,10 @@
<br />
<select id="audioSourceScreenshare" multiple alt="tip: Hold CTRL (command) to select Multiple" title="tip: Hold CTRL (command) to select Multiple" style="height: 60px; min-width: 290px; resize: both; overflow: auto; padding: 5px;" onchange="requestAudioStream();">
<option value="screenshare" selected>
<span data-translate="screen-shrae-audio">Screen Share Audio (default)</span>
Screen Share Audio (default)
</option>
<option value="microphones">
<span data-translate="other-audio-sources">Other Audio Sources</span>
Other Audio Sources
</option>
</select>
</p>
@ -388,7 +407,7 @@
<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">
<span data-translate="default">Default Device</span>
Default Device
</option>
</select>
</span>
@ -401,7 +420,7 @@
</div>
</div>
</div>
<div id="container-4" class="column columnfade pointer card" style=" overflow-y: auto;">
<div id="container-4" tabindex="5" alt="Create Reusable Invite" onkeyup="enterPressedClick(event,this);" title="Create Reusable Invite" role="button" aria-pressed="false" class="column columnfade pointer card" style=" overflow-y: auto;">
<h2>
<span data-translate="create-reusable-invite">Create Reusable Invite</span>
</h2>
@ -414,7 +433,7 @@
<br />
<br />
<p>
<input style="padding: 5px; font-size: 120%;" id="videoname4" onkeyup="enterPressed(event, generateQRPage);" placeholder="Give this media source a name (optional)" size="35" maxlength="70" style="padding: 5px;" />
<input style="padding: 5px; font-size: 120%;" id="videoname4" onkeyup="enterPressed(event, generateQRPage);" placeholder="Give this media source a name (optional)" size="35" maxlength="70" />
<br />
<br />
</p>
@ -514,7 +533,7 @@
</div>
</div>
<div id="dropButton" onclick="dropDownButtonAction()"><i class="las la-chevron-down" ></i></div>
<div id="dropButton" onclick="dropDownButtonAction()" title="More Options"><i class="las la-chevron-down" ></i></div>
<div id="container-5" class="column columnfade pointer card advanced" style=" overflow-y: auto;">
<h2><span data-translate="share-local-video-file">Stream Media File</span></h2>
@ -548,7 +567,11 @@
<input id="iframeURL" type="text" style="margin:10px; border:2px solid; padding:10px; width:400px;" title="Enter an HTTPS URL" multiple/><br />
<button onclick="previewIframe(getById('iframeURL').value);" >Preview</button>
<button onclick="this.innerHTML = 'Update'; session.publishIFrame(getById('iframeURL').value);" >Share</button><br />
<small>Remote website must be CORS/IFrame compatible with full SSL-encryption enabled.</small>
<small class="iframeblob">
<li>Remote website must be CORS/IFrame compatible with full SSL-encryption enabled.</li>
<li>Not all websites will work with this feature and many will actively not allow embedding like this.</li>
<li>If sharing a Youtube video, try using the embeddable link version and ensure that the video is set to allow embedding.</li>
</small>
<div id="iFramePreview" style=" width: 1280px; height: 720px; margin: auto; padding: 10px;"></div>
</div>
<div class="outer close">
@ -561,14 +584,14 @@
</div>
<div id="container-7" class="column columnfade pointer card advanced" style=" overflow-y: auto;" onclick="window.location = './speedtest';">
<div id="container-7" class="column columnfade pointer card advanced" style="overflow: hidden;" onclick="window.location = './speedtest';">
<h2><span data-translate="run-a-speed-test">Run a Speed Test</span></h2>
<i style="margin-top:30px;font-size:600%;overflow:hidden;" <i class="las la-tachometer-alt"></i>
<i style="margin-top:30px;font-size:600%;overflow:hidden;" class="las la-tachometer-alt"></i>
</div>
<div id="container-8" class="column columnfade pointer card advanced" style=" overflow-y: auto;" onclick="window.location = 'https://guides.obs.ninja';">
<div id="container-8" class="column columnfade pointer card advanced" style="overflow: hidden;" onclick="window.location = 'https://guides.obs.ninja';">
<h2><span data-translate="read-the-guides">Browse the Guides</span></h2>
<i style="margin-top:30px;font-size:600%;overflow:hidden;" <i class="las la-book-open"></i>
<i style="margin-top:30px;font-size:600%;overflow:hidden;" class="las la-book-open"></i>
</div>
<p></p>
@ -586,7 +609,7 @@
<br />
<li>Youtube video
<i class="lab la-youtube"></i>
<a href="https://www.youtube.com/watch?v=vLpRzMjUDaE&list=PLWodc2tCfAH1WHjl4WAOOoRSscJ8CHACe&index=2">Demoing it here</a>
<a href="https://www.youtube.com/watch?v=vLpRzMjUDaE&list=PLWodc2tCfAH1WHjl4WAOOoRSscJ8CHACe&index=2" alt="Youtube video demoing OBS.Ninja">Demoing it here</a>
</li>
<br />
<i>
@ -594,8 +617,7 @@
</i>
<br />
<li>
<a href="https://github.com/steveseguin/obsninja/wiki/FAQ#mac-os">MacOS <i class="lab la-apple"></i> users</a> currently need to use a
<a href="https://github.com/obsproject/obs-browser/issues/209#issuecomment-748683083">preview version of OBS Studio 26</a> or resort to
MacOS users using OBS will need to update to <a href="https://github.com/obsproject/obs-studio/releases/tag/26.1.2">OBS Studio 26.1.2</a> or resort to
window-capturing with the provided <a href="https://github.com/steveseguin/electroncapture">Electron-Capture app</a>.
</li>
@ -609,8 +631,8 @@
</li>
<br />
🎁 Site Updated: <a href="https://github.com/steveseguin/obsninja/wiki/v14-release-notes">Jan 2nd, 2021</a>. The previous version can be found at
<a href="https://obs.ninja/v134/">https://obs.ninja/v134/</a> if you are having new issues.
🥳 Site Updated: <a href="https://github.com/steveseguin/obsninja/wiki/v15-release-notes">Jan 12th, 2021</a>. The previous version can be found at
<a href="https://obs.ninja/v14/">https://obs.ninja/v14/</a> if you are having new issues.
<br />
<br />
@ -619,7 +641,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/T4xpQVv">Discord</a> and you can email me at steve@seguin.email
<a href="https://discord.gg/T4xpQVv">Discord <i class="lab la-discord"></i></a> or email me at steve@seguin.email
</i>
</h3>
@ -644,19 +666,21 @@
<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 id="roomHeader" style="display:none">
<div class='directorContainer half' style="padding-top:10px;margin-bottom:0;margin-left:10px;padding-bottom:0">
<div class="hideLinksClass">
<span style='color:white' id="directorLinksButton" onclick="hideDirectorinvites(this);">
<i class="las la-caret-down"></i><span data-translate="hide-the-links"> LINKS (GUEST INVITES & SCENES)</span>
</span>
<span id="help_directors_room" style='color:white;text-align: right;' 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="help_directors_room" style='float: right;color:white;text-align: right;' 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
</span>
</div>
<div id='roomnotes2' style='max-width:1190px;display:none;padding:0 0 0 10px;' >
<div id='roomnotes2' style='max-width:1191px;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>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 <a href="https://www.youtube.com/watch?v=bpRa8-UYCGc" title="Youtube Video demoing how to do this">disable video sharing between guests</a>. Using &broadcast, &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>
@ -678,135 +702,144 @@
</font>
</div>
</div>
<div class='directorContainer' id='directorLinks' style='display:none;margin-top:0;padding-top:0'>
<div class='directorContainer half' id='directorLinks1' style='display:none;margin-top:0;'>
<div class='directorBlock'>
<h2>GUEST INVITE</h2>
<span data-translate='invite-users-to-join'>Invites users to join the group and broadcast their feed to it. They will see and hear other guests in the same room.
These users will see every feed in the room.</span>
<a onclick='popupMessage(event);copyFunction(this)' id="director_block_1" onmousedown='copyFunction(this)' class='task grabLinks' style='cursor:copy'></a>
<button class='pull-left grey' style='font-size:1.15em' id="showCustomizerButton1" onclick='showCustomizer(1,this)'><i class='las la-tools'></i>Customize</button>
<button class='pull-right grey' style='font-size:1.15em' onclick='popupMessage(event);copyFunction(getById("director_block_1"))'><i class='las la-video'></i>Copy link</button>
<h2 title="Invite a guest or camera source to publish into the group room" style="margin-top: 5px;"><i class="las la-video director-link-icons" ></i> INVITE A GUEST</h2>
<span style="margin:5px; line-height: 1.6;" data-translate='invite-users-to-join'>Guests can use the link to join the group room</span>
<a onclick='popupMessage(event);copyFunction(this)' id="director_block_1" onmousedown='copyFunction(this)' class='task grabLinks' style='cursor:copy;background-color: #0003;'></a>
<span style="display:block;">
<span style="bottom: 0; margin: 0 0 0 10px; top: 22px; position: relative;">
<label class="switch" title="If enabled, the invited guest will not be able to see or hear anyone in the room.">
<input type="checkbox" checked data-param="&view=" onchange="updateLinkInverse(1,this);">
<span class="slider"></span>
</label>
Guests hear others
</span>
<button class='pull-right grey' style='font-size:1.15em' onclick='popupMessage(event);copyFunction(getById("director_block_1"))'><i class='las la-copy'></i>Copy link</button>
<button class='pull-right grey' style='font-size:1.15em' id="showCustomizerButton1" onclick='showCustomizer(1,this)'><i class='las la-tools'></i>Customize</button>
<span>
</div>
<div class='directorBlock'>
<h2>BROADCAST INVITE</h2>
<span data-translate='link-to-invite-camera'>Link to invite users to broadcast their feeds to the group. These users will not see or hear any feed from the group.</span>
<a class='task grabLinks' id="director_block_2" style='cursor:copy' onclick='popupMessage(event);copyFunction(this)' onmousedown='copyFunction(this)'></a>
<button class='pull-left grey' style='font-size:1.15em' id="showCustomizerButton2" onclick='showCustomizer(2,this)'><i class='las la-tools'></i>Customize</button>
<button class='pull-right grey' style='font-size:1.15em;' onclick='popupMessage(event);copyFunction(getById("director_block_2"))'><i class='las la-video'></i>Copy link</button>
</div>
<div class='directorBlock'>
<h2>SCENE LINK: MANUAL</h2>
<span data-translate='this-is-obs-browser-source-link'>This is an OBS Browser Source link that is empty by default. Click 'add to scene' for each guest you want included in this scene</span>
<a class='task grabLinks' id="director_block_3" onmousedown='copyFunction(this)' data-drag='1' draggable='true' onclick='popupMessage(event);copyFunction(this)' ></a>
<button class='pull-left grey' style='font-size:1.15em' id="showCustomizerButton3" onclick='showCustomizer(3,this)'><i class='las la-tools'></i>Customize</button>
<button class='pull-right grey' style='font-size:1.15em;' onclick='popupMessage(event);copyFunction(getById("director_block_3"))'><i class='las la-th-large' aria-hidden='true'></i></i>Copy link</button>
</div>
<div class='directorBlock'>
<h2>SCENE LINK: AUTO</h2>
<span data-translate='this-is-obs-browser-souce-link-auto'>Also an OBS Browser Source link. All guest videos in this group chat room will automatically be added into this scene.</span>
<a class='task grabLinks' id="director_block_4" onmousedown='copyFunction(this)' draggable='true' data-drag='1' onclick='popupMessage(event);copyFunction(this)'></a>
<button class='pull-left grey' style='font-size:1.15em' id="showCustomizerButton4" onclick='showCustomizer(4,this)'><i class='las la-tools'></i>Customize</button>
<button class='pull-right grey' style='font-size:1.15em;' onclick='popupMessage(event);copyFunction(getById("director_block_4"))'><i class='las la-th-large'></i>Copy link</button>
</div>
<div class='directorContainer half' id='directorLinks2' style='margin-left: 5px;display:none;margin-top:0;'>
<div class='directorBlock' style="background-color: var(--green-accent);" >
<h2 title="Use this link in the OBS Browser Source to capture the video or audio" style="margin-left: 1px;margin-top: 5px;"><i class="las la-th-large director-link-icons" style="margin-right: 6px;" ></i> CAPTURE A GROUP SCENE</h2>
<span style="margin:5px; line-height: 1.6;" data-translate='this-is-obs-browser-source-link'>Use in OBS or other studio software to capture the group video mix</span>
<a onclick='popupMessage(event);copyFunction(this)' id="director_block_3" onmousedown='copyFunction(this)' class='task grabLinks' style='cursor:copy;background-color: #0003;'></a>
<span style="display:block;">
<span style="bottom: 0; margin: 0 0 0 10px; top: 22px; position: relative;">
<label class="switch" title="If enabled, you must manually add a video to a scene for it to appear.">
<input type="checkbox" checked data-param="&scene" onchange="updateLinkScene(3,this);">
<span class="slider"></span>
</label>
Auto-add guests
</span>
<button class='pull-right grey' style='font-size:1.15em' onclick='popupMessage(event);copyFunction(getById("director_block_3"))'><i class='las la-copy'></i>Copy link</button>
<button class='pull-right grey' style='font-size:1.15em' id="showCustomizerButton3" onclick='showCustomizer(3,this)'><i class='las la-tools'></i>Customize</button>
<span>
</div>
</div>
<div class='directorContainer' id="customizeLinks" style='display:none;margin-top:0;padding-top:10px'>
<div class='directorContainer' id="customizeLinks" style='display:none;margin-top:0;padding-top:15px'>
<div class='directorBlock' id="customizeLinks1" style='display:none;margin-top:0;padding-bottom:0;'>
<div style="display:inline-block;top: 12px; position: relative;">
<label class="switch">
<label class="switch" title="Disables Echo Cancellation and improves audio quality">
<input type="checkbox" data-param="&s" onchange="updateLink(1,this);">
<span class="slider"></span>
</label>
Pro-audio mode
<Br />
<label class="switch">
<label class="switch" title="Audio-only sources are visually hidden from scenes">
<input type="checkbox" data-param="&st" onchange="updateLink(1,this);">
<span class="slider"></span>
</label>
Hide audio-only sources
<Br />
<label class="switch">
<label class="switch" title="Guest will be prompted to enter a Display Name">
<input type="checkbox" data-param="&l" onchange="updateLink(1,this);">
<span class="slider"></span>
</label>
Ask for Display Name
<Br />
<label class="switch">
<label class="switch" title="Display Names will be shown in the bottom-left corner of videos">
<input type="checkbox" data-param="&sl" onchange="updateLink(1,this);">
<span class="slider"></span>
</label>
Show Display Names
</div>
<div style="display:inline-block;top: 12px; position: relative; margin-left:10px;">
<label class="switch">
<label class="switch" title="Request 1080p60 from the Guest instead of 720p60, if possible">
<input type="checkbox" data-param="&q" onchange="updateLink(1,this);">
<span class="slider"></span>
</label>
1080p60 Video if Available
<Br />
<label class="switch">
<label class="switch" title="The default microphone will be pre-selected for the guest">
<input type="checkbox" data-param="&ad" onchange="updateLink(1,this);">
<span class="slider"></span>
</label>
Auto-select Default Microphone
<Br />
<label class="switch">
<label class="switch" title="The default camera device will selected automatically">
<input type="checkbox" data-param="&vd" onchange="updateLink(1,this);">
<span class="slider"></span>
</label>
Auto-select Default Camera
<br />
<label class="switch">
<input type="checkbox" data-param="&m" onchange="updateLink(1,this);">
<Br />
<label class="switch" title="The guest won't have access to changing camera settings or screenshare">
<input type="checkbox" data-param="&ns" onchange="updateLink(1,this);">
<span class="slider"></span>
</label>
Mute Microphone by Default
Hide Settings Button
</div>
<div style="display:inline-block;top: 12px; position: relative; margin-left:10px;">
<label class="switch">
<label class="switch" title="The guest will not see their own self-preview after joining">
<input type="checkbox" data-param="&np" onchange="updateLink(1,this);">
<span class="slider"></span>
</label>
Disable Self-Preview
<Br />
<label class="switch">
<label class="switch" title="Guests will have an option to poke the Director by pressing a button">
<input type="checkbox" data-param="&hand" onchange="updateLink(1,this);">
<span class="slider"></span>
</label>
Display 'Raise-Hand' button
<Br />
<label class="switch">
<label class="switch" title="Add an audio compressor to the guest's microphone">
<input type="checkbox" data-param="&comp" onchange="updateLink(1,this);">
<span class="slider"></span>
</label>
Enable Audio Compressor
<Br />
<label class="switch">
<label class="switch" title="Add an Equalizer to the guest's microphone that the director can control">
<input type="checkbox" data-param="&eq" onchange="updateLink(1,this);">
<span class="slider"></span>
</label>
Enable Equalizer as Option
</div>
<div style="display:inline-block;top: 12px; position: relative; margin-left:10px;">
<label class="switch">
<div style="display:inline-block;top: 12px; position: relative; margin-left:10px; height: 20px;">
<label class="switch" title="The guest can only see the Director's video, if provided">
<input type="checkbox" data-param="&broadcast" id="broadcastSlider" onchange="updateLink(1,this);">
<span class="slider"></span>
</label>
Only see the Director's Feed
<Br />
<label class="switch">
<input type="checkbox" data-param="&ns" onchange="updateLink(1,this);">
<br />
<label class="switch" title="The guest's microphone will be muted on joining. They can unmute themselves.">
<input type="checkbox" data-param="&m" onchange="updateLink(1,this);">
<span class="slider"></span>
</label>
Hide Settings Button
Mute Microphone by Default
<Br />
<label class="switch">
<input type="checkbox" data-param="&mono" onchange="updateLink(1,this);">
<label class="switch" title="Have the guest join muted, so only the director can Unmute the guest.">
<input type="checkbox" data-param="&g=0" onchange="updateLink(1,this);">
<span class="slider"></span>
</label>
Force Mono Audio
Unmute by Director Only
<Br />
<label class="switch">
<label class="switch" title="Make the invite URL encoded, so parameters are harder to tinker with by guests">
<input type="checkbox" data-param="" id="obfuscate_director_1" onchange="updateLink(1,this);">
<span class="slider"></span>
</label>
@ -814,104 +847,7 @@
</div>
</div>
<div class='directorBlock' id="customizeLinks2" style='display:none;margin-top:0;padding-bottom:0;'>
<div style="display:inline-block;top: 12px; position: relative;">
<label class="switch">
<input type="checkbox" data-param="&s" onchange="updateLink(2,this);">
<span class="slider"></span>
</label>
Pro-audio mode
<Br />
<label class="switch">
<input type="checkbox" data-param="&st" onchange="updateLink(2,this);">
<span class="slider"></span>
</label>
Hide audio-only sources
<Br />
<label class="switch">
<input type="checkbox" data-param="&l" onchange="updateLink(2,this);">
<span class="slider"></span>
</label>
Ask for Display Name
<Br />
<label class="switch">
<input type="checkbox" data-param="&sl" onchange="updateLink(2,this);">
<span class="slider"></span>
</label>
Show Display Names
</div>
<div style="display:inline-block;top: 12px; position: relative; margin-left:10px;">
<label class="switch">
<input type="checkbox" data-param="&q" onchange="updateLink(2,this);">
<span class="slider"></span>
</label>
1080p60 Video if Available
<Br />
<label class="switch">
<input type="checkbox" data-param="&ad" onchange="updateLink(2,this);">
<span class="slider"></span>
</label>
Auto-select Default Microphone
<Br />
<label class="switch">
<input type="checkbox" data-param="&vd" onchange="updateLink(2,this);">
<span class="slider"></span>
</label>
Auto-select Default Camera
<br />
<label class="switch">
<input type="checkbox" data-param="&m" onchange="updateLink(2,this);">
<span class="slider"></span>
</label>
Mute Microphone by Default
</div>
<div style="display:inline-block;top: 12px; position: relative; margin-left:10px;">
<label class="switch">
<input type="checkbox" data-param="&np" onchange="updateLink(2,this);">
<span class="slider"></span>
</label>
Disable Self-Preview
<Br />
<label class="switch">
<input type="checkbox" data-param="&hand" onchange="updateLink(2,this);">
<span class="slider"></span>
</label>
Display 'Raise-Hand' button
<Br />
<label class="switch">
<input type="checkbox" data-param="&comp" onchange="updateLink(2,this);">
<span class="slider"></span>
</label>
Enable Audio Compressor
<Br />
<label class="switch">
<input type="checkbox" data-param="&eq" onchange="updateLink(2,this);">
<span class="slider"></span>
</label>
Enable Equalizer as Option
</div>
<div style="display:inline-block;top: 12px; position: relative; margin-left:10px;">
<label class="switch">
<input type="checkbox" data-param="&ns" onchange="updateLink(2,this);">
<span class="slider"></span>
</label>
Hide Settings Button
<Br />
<label class="switch">
<input type="checkbox" data-param="&mono" onchange="updateLink(2,this);">
<span class="slider"></span>
</label>
Force Mono Audio
<Br />
<label class="switch">
<input type="checkbox" id="obfuscate_director_2" data-param="" onchange="updateLink(2,this);">
<span class="slider"></span>
</label>
Obfuscate Link and Parameters
</div>
</div>
<div class='directorBlock' id="customizeLinks3" style='display:none;margin-top:0;padding-bottom:0;'>
<div class='directorBlock' id="customizeLinks3" style='display:none;margin-top:5px;padding-bottom:0;'>
<div style="display:inline-block;top: 12px; position: relative;">
<label class="switch">
<input type="checkbox" data-param="&s" onchange="updateLink(3,this);">
@ -933,60 +869,28 @@
</label>
Show Display Names
</div>
<div style="display:inline-block;top: 12px; position: relative; margin-left:10px;">
<div style="display:inline-block;top: 12px; height: 20px; position: relative; margin-left:10px;">
<label class="switch">
<input type="checkbox" data-param="&vb=20000" onchange="updateLink(3,this);">
<span class="slider"></span>
</label>
Unlock Video Bitrate (20-mbps)
Unlock Max Video Bitrate
</div>
<div style="display:inline-block;top: 12px; position: relative; margin-left:10px;">
<div style="display:inline-block;top: 12px; height: 20px;position: relative; margin-left:10px;">
<label class="switch">
<input type="checkbox" data-param="&mono" onchange="updateLink(3,this);">
<span class="slider"></span>
</label>
Force Mono Audio
</div>
</div>
<div class='directorBlock' id="customizeLinks4" style='display:none;margin-top:0;padding-bottom:0;'>
<div style="display:inline-block;top: 12px; position: relative;">
<label class="switch">
<input type="checkbox" data-param="&s" onchange="updateLink(4,this);">
<span class="slider"></span>
</label>
Pro-audio mode
</div>
<div style="display:inline-block;top: 12px; position: relative; margin-left:10px;">
<label class="switch">
<input type="checkbox" data-param="&st" onchange="updateLink(4,this);">
<span class="slider"></span>
</label>
Hide audio-only sources
</div>
<div style="display:inline-block;top: 12px; position: relative; margin-left:10px;">
<label class="switch">
<input type="checkbox" data-param="&sl" onchange="updateLink(4,this);">
<span class="slider"></span>
</label>
Show Display Names
</div>
<div style="display:inline-block;top: 12px; position: relative; margin-left:10px;">
<label class="switch">
<input type="checkbox" data-param="&vb=20000" onchange="updateLink(4,this);">
<span class="slider"></span>
</label>
Unlock Video Bitrate (20-mbps)
</div>
<div style="display:inline-block;top: 12px; position: relative; margin-left:10px;">
<label class="switch">
<input type="checkbox" data-param="&mono" onchange="updateLink(4,this);">
<span class="slider"></span>
</label>
Force Mono Audio
<a href="https://params.obs.ninja" style="color:#888;" target="_blank" >
<div style="display: block;float:right;font-size:70%;z-index:30;bottom:6px;right:10px;position:relative;color:#888;" >Learn more about URL parameters at <font style="text-decoration: underline;">params.obs.ninja</font>
</div>
</a>
</div>
</div>
<div></div>
<div id='guestFeeds' style="display:none"><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>
@ -995,6 +899,7 @@
<h4 style='color:#CCC;margin:20px 20px 0 20px;' data-translate='more-than-four-can-join' >These four guest slots are just for demonstration. More than four guests can actually join a room.</h4>
</div></div>
</div>
<div id="overlayMsgs" onclick="function(e){e.target.innerHTML = '';}" style="display:none"></div>
<div id="bigPlayButton" onclick="function(e){e.target.innerHTML = '';}" style="display:none"></div>
<div id="controls_blank" style="display: none;">
@ -1021,7 +926,7 @@
</button>
<input data-action-type="volume" class="slider" type="range" min="1" max="100" value="100" title="Remotely change the volume of this guest" onclick="remoteVolume(this);" style="grid-column: 1; margin:5px; width: 93%; position: relative; top: 0px; background-color:#fff0;"/>
<input data-action-type="volume" type="range" min="1" max="100" value="100" title="Remotely change the volume of this guest" onclick="remoteVolume(this);" style="grid-column: 1; margin:5px; width: 93%; position: relative; top: 0px; background-color:#fff0;"/>
<button data-action-type="mute-guest" style="grid-column: 2;" title="Mute this guest everywhere" onclick="remoteMute(this, event);">
<i class="las la-volume-off"></i>
@ -1029,13 +934,13 @@
</button>
<span>
<button style="width: 36px" data-action-type="change-quality" title="Disable Video Preview" onclick="toggleQualityDirector(0, this.dataset.UUID, this);">
<button style="width: 36px" data-action-type="change-quality1" title="Disable Video Preview" onclick="toggleQualityDirector(0, this.dataset.UUID, this);">
<span data-translate="change-to-low-quality">&nbsp;&nbsp;<i class="las la-video-slash"></i></span>
</button>
<button class="pressed" style="width:36px;" data-action-type="change-quality" title="Low-Quality Preview" onclick="toggleQualityDirector(50, this.dataset.UUID, this);">
<button class="pressed" style="width:36px;" data-action-type="change-quality2" 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);">
<button style="width: 36px" data-action-type="change-quality3" 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>
@ -1044,10 +949,15 @@
<i class="las la-sign-out-alt"></i>
<span data-translate="disconnect-guest" >Hangup</span>
</button>
<button data-action-type="recorder" title="Start Recording this stream. *experimental*' views" onclick="recordVideo(this, event)">
<button data-action-type="recorder-local" title="Start Recording this remote stream to this local drive. *experimental*'" onclick="recordVideo(this, event)">
<i class="las la-circle"></i>
<span data-translate="record"> Record</span>
<span data-translate="record-local"> Record Local</span>
</button>
<button data-action-type="recorder-remote" data-value="0" title="The Remote Guest will record their local stream to their local drive. *experimental*" onclick="requestVideoRecord(this)">
<i class="las la-circle"></i>
<span data-translate="record-remote"> Record Remote</span>
</button>
<button class="advanced" data-action-type="voice-chat" title="Toggle Voice Chat with this Guest">
<span data-translate="voice-chat"><i class="las la-microphone"></i> Voice Chat</span>
</button>
@ -1075,6 +985,7 @@
</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>
@ -1085,7 +996,7 @@
<br />
<div class="form-group multiselect" alt="tip: Hold CTRL (command) to select Multiple" title="tip: Hold CTRL (command) to select Multiple" style="padding: 10px; background-color:#f3f3f3;">
<a id="multiselect-trigger3" class="form-control multiselect-trigger" tabindex="3">
<a id="multiselect-trigger3" class="form-control multiselect-trigger" >
<div id="audioTitle2" class="title">
<i class="las la-microphone-alt"></i><span data-translate="select-audio-source"> Audio Source(s) </span>
<i id="chevarrow2" class="chevron bottom" aria-hidden="true"></i>
@ -1099,7 +1010,8 @@
<br />
<span id="headphonesDiv3" style="display: inline-block;">
<div class="title">
<i class="las la-headphones"></i><span data-translate="select-output-source"> Audio Output Destination:
<i class="las la-headphones"></i>
<span data-translate="select-output-source"> Audio Output Destination: </span>
</div>
<select id="outputSource3" ></select>
</span>
@ -1123,7 +1035,7 @@
<span id="popupSelector_constraints_loading" style="display: none; visibility:hidden">
<i class='las la-spinner icn-spinner' style='margin:30px;font-size:400%;color:white;'></i>
</span>
</p>
</div>
<nav id="context-menu" class="context-menu">
<ul class="context-menu__items">
@ -1219,7 +1131,7 @@
}
var session = WebRTC.Media; // session is a required global variable if configuring manually. Run before loading main.js but after webrtc.js.
session.version = "14.3";
session.version = "15.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.
@ -1284,7 +1196,7 @@
<script type="text/javascript" id="main-js" src="./main.js" data-translation="blank"></script>
<script type="text/javascript" crossorigin="anonymous" id="mixer-js" src="./mixer.js?ver=2"></script>
-->
<script type="text/javascript" crossorigin="anonymous" id="main-js" src="./main.js?ver=131"></script>
<script type="text/javascript" crossorigin="anonymous" src="./animations.js?ver=16"></script>
<script type="text/javascript" crossorigin="anonymous" id="main-js" src="./main.js?ver=147"></script>
<script type="text/javascript" crossorigin="anonymous" src="./animations.js?ver=19"></script>
</body>
</html>

180
main.css
View File

@ -2,9 +2,9 @@
--background-color: #141926;
--container-color: #373737;
--button-color: #2A2A2A;
--blue-accent: #4B4959;
--blue-accent: #4a4c63;
--red-accent: #553737;
--green-accent: #304F2B;
--green-accent: #3f4f50;
--olive-accent: #535D32;
--regular-margin: 10px;
}
@ -130,13 +130,25 @@ button.grey {
color: white;
}
#miniPerformer>#videosource{
width: 80px;
height: 45px;
margin: 5px;
background-color: #464749 !important;
background-size: 50%;
}
.red {
background-color: #840000 !important;
}
button.red {
-webkit-app-region: no-drag;
padding: 10px;
margin: 10px 0px;
cursor: pointer;
border-radius: 2px;
background-color: var(--red-accent);
color: white;
}
@ -149,6 +161,7 @@ button {
border-radius: 2px;
}
button.white {
-webkit-app-region: no-drag;
padding: 6px 10px 4px 9px;
@ -185,8 +198,8 @@ button.white:active {
background-color: #0000;
color: white;
font-family: Cousine, monospace;
font-size: 4em;
line-height: 1.5em;
font-size: 6vh;
line-height: 8vh;
letter-spacing: 0.0em;
text-shadow: 0.05em 0.05em 0px rgba(0,0,0,1);
width:100%;
@ -197,13 +210,13 @@ button.white:active {
top: 0;
position: fixed;
overflow-wrap: anywhere;
padding:3%;
padding:2% 3%;
pointer-events: none
}
#overlayMsgs span{
background-color: #000A;
padding: 8px 8px 0px 8px;
margin:10px;
background-color: #000B;
padding: 2px;
margin: 0.5vh;
text-align: center;
width:100%;
pointer-events: none
@ -334,7 +347,6 @@ hr {
#gridlayout {
padding: 0;
display: grid;
width: 100%;
height: 100%;
grid-gap: 0;
@ -374,14 +386,14 @@ hr {
.directorsgrid .vidcon video {
max-width: 100%;
padding: 5px;
padding: 0 5px;
width: 100%;
height: 157px;
height: 148px;
}
.directorsgrid .vidcon {
display: inline-block !important;
width: 269.2px !important;
width: 269.7px !important;
background: #7E7E7E;
color: #FCFCFC;
vertical-align: top;
@ -433,6 +445,45 @@ button.glyphicon-button.active.focus {
height:100%;
}
#subControlButtons {
display: flex;
border-radius: 38px;
background-color: #030303dd;
padding: 5px 7px;
bottom: 2.25em;
position: absolute;
pointer-events: auto;
}
@media only screen and (max-width: 500px){
#subControlButtons {
padding: 2px 4px;
zoom: .9;
-moz-transform: scale(.9);
}
}
@media only screen and (max-width: 400px){
#subControlButtons {
padding: 1px 2px;
bottom: 2.1em;
zoom: .8;
-moz-transform: scale(.8);
}
}
@media only screen and (max-width: 300px){
#subControlButtons {
padding: 0px;
bottom: 2.0em;
}
}
@media only screen and (max-height: 500px){
#subControlButtons {
padding: 0;
bottom: .7em;
-moz-transform: scale(.9);
}
}
@keyframes pulse {
@ -913,13 +964,14 @@ label {
/* Create four equal columns that floats next to each other */
.column {
display: inline-block;
margin: 1.8%;
min-width: 300px;
width: 20%;
padding: 25px;
height: 220px;
height: 200px;
/* Should be removed. Only for demonstration */
border-radius: 5px;
text-align: center;
@ -945,7 +997,7 @@ label {
@media only screen and (max-height: 650px) {
.column {
height: 180px;
}
}
@ -972,7 +1024,7 @@ img {
width: 20%;
min-width: 300px;
padding: 28px;
height: 220px;
height: 200px;
/* Should be removed. Only for demonstration */
margin: 1.8%;
@ -1037,6 +1089,7 @@ img {
text-align: center;
margin: 5px;
pointer-events: auto;
outline:none;
}
.float2 {
opacity: 0.8;
@ -1049,6 +1102,7 @@ img {
z-index: 10;
margin: 5px;
pointer-events: auto;
outline:none;
}
.rotate225 {
@ -1087,6 +1141,7 @@ img {
border: 0;
pointer-events: none;
}
.my-float {
margin-top: 7px;
opacity: 0.9;
@ -1135,7 +1190,7 @@ img {
max-width: 100%;
}
.in-animation {
animation: inlightbox 0.8s forwards;
animation: inlightbox 0.5s forwards;
position: fixed !important;
margin: 0 !important;
}
@ -1153,7 +1208,7 @@ img {
}
.out-animation {
animation: outlightbox 0.8s forwards;
animation: outlightbox 0.5s forwards;
}
.pointer {
@ -1163,7 +1218,7 @@ img {
50% {
width: 100%;
left: 0;
height: 220px;
height: 200px;
}
100% {
@ -1470,7 +1525,7 @@ video.clean::-webkit-media-controls-timeline-container {
font-weight: bold;
font-size: 1em;
padding: 10px;
margin: 5px;
margin: 5px 0;
word-break: break-all;
}
.grabLinks a:hover {
@ -1803,24 +1858,32 @@ input[type=checkbox] {
font-size: 100%;
}
}
.hideLinksClass {
background-color: var(--container-color);
width:1191px;
padding: 10px;
margin: 10px;
}
.directorContainer {
background-color: var(--container-color);
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
margin: 10px;
padding: 5px 10px;
max-width: 1190px
grid-template-columns: 1fr ;
margin: 10px 0px 10px 10px;
padding: 10px;
max-width: 1191px
}
#directorLinksButton{
cursor:pointer;
}
.directorContainer.half {
background-color: var(--container-color);
display: grid;
grid-template-columns: 1fr 1fr;
padding: 5px 20px;
max-width: 1190px
padding: 10px 10px;
width: 591px;
}
.directorBlock {
cursor: grab;
padding: 10px 10px 50px 10px;
padding: 10px 10px 5px 10px;
margin: 10px;
color: white;
position:relative;
@ -1831,16 +1894,15 @@ input[type=checkbox] {
background-color: var(--blue-accent);
}
.directorBlock:nth-child(2) {
background-color: var(--red-accent);
}
.directorBlock:nth-child(3) {
background-color: var(--green-accent);
}
.directorBlock:nth-child(4) {
.directorBlock:nth-child(3) {
background-color: var(--olive-accent);
}
.directorBlock:nth-child(4) {
background-color: var(--red-accent);
}
.directorBlock button {
position: absolute;
bottom: 0;
margin: 10px;
}
@ -1854,8 +1916,9 @@ input[type=checkbox] {
}
.directorBlock h2 {
text-transform: uppercase;
font-size: 1em;
margin-bottom: 10px;
margin-left: 5px;
font-size:1.2em;
}
#toggleroomnotes {
grid-column: 4;
@ -1864,7 +1927,7 @@ input[type=checkbox] {
div#roomnotes2 {
background: var(--container-color);
padding: 10px !important;
margin: 0px var(--regular-margin);
margin: 0 var(--regular-margin) 10px var(--regular-margin);
width: 100%;
}
.directorsgrid .directorContainer:nth-child(2) button {
@ -1910,7 +1973,6 @@ i.las.la-circle {
text-align: right;
margin: 5px;
font-size: 0.7em;
cursor: copy;
text-overflow: ellipsis;
overflow: hidden;
}
@ -1936,7 +1998,7 @@ i.las.la-circle {
}
div#guestFeeds {
background: var(--container-color);
padding: 0 0 15px 20px;
padding: 5px 0 15px 20px;
display: inline-block;
margin: 0px var(--regular-margin);
}
@ -2014,9 +2076,10 @@ span#guestTips {
padding: 5px 10px;
background: rgba(0, 0, 0, .5);
pointer-events:none;
font-size: 4vh;
font-size: 1em;
}
.video-label.zoom {
position: absolute;
bottom: 0;
@ -2026,7 +2089,6 @@ span#guestTips {
padding: 5px 10px;
background: rgba(0, 0, 0, .5);
pointer-events:none;
font-size: 4vh;
}
.video-label.teams {
@ -2038,7 +2100,6 @@ span#guestTips {
padding: 5px 10px;
background: rgba(0, 0, 0, .4);
pointer-events:none;
font-size: 4vh;
border-radius: 5px;
}
@ -2052,8 +2113,8 @@ span#guestTips {
padding: 5px 10px;
background: rgba(0, 0, 0, .8);
pointer-events:none;
font-size: 4vh;
border-radius: 5px;
font-size: 0.8em;
}
.video-label.ninjablue {
@ -2062,7 +2123,6 @@ span#guestTips {
left: 0;
background: #141926;
padding: 10px 5%;
font-size: 4vh;
}
.video-label.toprounded {
@ -2071,7 +2131,6 @@ span#guestTips {
bottom: unset;
background: rgb(0 0 0 / 70%);
padding: 10px 5%;
font-size: 4vh;
left: 50%;
transform: translateX(-50%);
width: 50%;
@ -2081,13 +2140,13 @@ span#guestTips {
text-transform: uppercase;
letter-spacing: 3;
box-shadow: 0px 0px 10px #00000059;
font-size: 0.7em
}
.video-label.fire {
color: #FFFFFF;
text-shadow: 0 -1px 4px #FFF, 0 -2px 10px #ff0, 0 -10px 20px #ff8000, 0 -18px 40px #F00;
font-weight: bold;
font-size: 5vh;
position: absolute;
bottom: 2vh;
width: 100%;
@ -2111,6 +2170,13 @@ span#guestTips {
cursor:pointer;
}
.iframeblob{
padding-top:18px;
text-align: left;
width: 600px;
display: block;
margin: auto;
}
#shareScreenGear{
display:none;
}
@ -2139,46 +2205,54 @@ span#guestTips {
position: absolute;
}
.director-link-icons {
font-size: 1.5em;
float: left;
bottom: 4px;
position: relative;
margin-right: 9px;
}
.switch {
position: relative;
display: inline-block;
margin:5px 5px 10px 5px;
width: 40px;
height: 24px;
margin:5px 5px 10px 5px;
bottom:20px;
border-radius: 2px;
display: inline-block;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
opacity: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: .4s;
transition: .4s;
-webkit-transition: .3s;
transition: .3s;
position: absolute;
}
.slider:before {
position: absolute;
content: "";
height: 17px;
width: 17px;
left: 3px;
bottom: 3px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
-webkit-transition: .3s;
transition: .3s;
position: absolute;
}
input:checked + .slider {
background-color: #86b98f;
}

18149
main.js

File diff suppressed because it is too large Load Diff

4
thirdparty/StreamSaver.js vendored Normal file
View File

@ -0,0 +1,4 @@
/* global chrome location ReadableStream define MessageChannel TransformStream */
// https://github.com/jimmywarting/StreamSaver.js
// MIT License
((e,t)=>{"undefined"!=typeof module?module.exports=t():"function"==typeof define&&"object"==typeof define.amd?define(t):this.streamSaver=t()})(0,()=>{"use strict";const e="object"==typeof window?window:this;e.HTMLElement||console.warn("streamsaver is meant to run on browsers main thread");let t=null,a=!1;const r=e.WebStreamsPolyfill||{},n=e.isSecureContext;let o=/constructor/i.test(e.HTMLElement)||!!e.safari||!!e.WebKitPoint;const s=n||"MozAppearance"in document.documentElement.style?"iframe":"navigate",i={createWriteStream:function(r,m,d){let c={size:null,pathname:null,writableStrategy:void 0,readableStrategy:void 0},p=0,f=null,u=null,w=null;Number.isFinite(m)?([d,m]=[m,d],console.warn("[StreamSaver] Depricated pass an object as 2nd argument when creating a write stream"),c.size=d,c.writableStrategy=m):m&&m.highWaterMark?(console.warn("[StreamSaver] Depricated pass an object as 2nd argument when creating a write stream"),c.size=d,c.writableStrategy=m):c=m||{};if(!o){t||(t=n?l(i.mitm):function(t){const a=document.createDocumentFragment(),r={frame:e.open(t,"popup","width=200,height=100"),loaded:!1,isIframe:!1,isPopup:!0,remove(){r.frame.close()},addEventListener(...e){a.addEventListener(...e)},dispatchEvent(...e){a.dispatchEvent(...e)},removeEventListener(...e){a.removeEventListener(...e)},postMessage(...e){r.frame.postMessage(...e)}},n=t=>{t.source===r.frame&&(r.loaded=!0,e.removeEventListener("message",n),r.dispatchEvent(new Event("load")))};return e.addEventListener("message",n),r}(i.mitm)),u=new MessageChannel,r=encodeURIComponent(r.replace(/\//g,":")).replace(/['()]/g,escape).replace(/\*/g,"%2A");const o={transferringReadable:a,pathname:c.pathname||Math.random().toString().slice(-6)+"/"+r,headers:{"Content-Type":"application/octet-stream; charset=utf-8","Content-Disposition":"attachment; filename*=UTF-8''"+r}};c.size&&(o.headers["Content-Length"]=c.size);const m=[o,"*",[u.port2]];if(a){const e="iframe"===s?void 0:{transform(e,t){if(!(e instanceof Uint8Array))throw new TypeError("Can only wirte Uint8Arrays");p+=e.length,t.enqueue(e),f&&(location.href=f,f=null)},flush(){f&&(location.href=f)}},t=(w=new i.TransformStream(e,c.writableStrategy,c.readableStrategy)).readable;u.port1.postMessage({readableStream:t},[t])}u.port1.onmessage=(e=>{e.data.download&&("navigate"===s?(t.remove(),t=null,p?location.href=e.data.download:f=e.data.download):(t.isPopup&&(t.remove(),t=null,"iframe"===s&&l(i.mitm)),l(e.data.download)))}),t.loaded?t.postMessage(...m):t.addEventListener("load",()=>{t.postMessage(...m)},{once:!0})}let g=[];return!o&&w&&w.writable||new i.WritableStream({write(e){if(!(e instanceof Uint8Array))throw new TypeError("Can only wirte Uint8Arrays");o?g.push(e):(u.port1.postMessage(e),p+=e.length,f&&(location.href=f,f=null))},close(){if(o){const e=new Blob(g,{type:"application/octet-stream; charset=utf-8"}),t=document.createElement("a");t.href=URL.createObjectURL(e),t.download=r,t.click()}else u.port1.postMessage("end")},abort(){g=[],u.port1.postMessage("abort"),u.port1.onmessage=null,u.port1.close(),u.port2.close(),u=null}},c.writableStrategy)},WritableStream:e.WritableStream||r.WritableStream,supported:!0,version:{full:"2.0.5",major:2,minor:0,dot:5},mitm:"./thirdparty/mitm.html?version=2.0.0"};function l(e){if(!e)throw new Error("meh");const t=document.createElement("iframe");return t.hidden=!0,t.src=e,t.loaded=!1,t.name="iframe",t.isIframe=!0,t.postMessage=((...e)=>t.contentWindow.postMessage(...e)),t.addEventListener("load",()=>{t.loaded=!0},{once:!0}),document.body.appendChild(t),t}try{new Response(new ReadableStream),!n||"serviceWorker"in navigator||(o=!0)}catch(e){o=!0}return(e=>{try{e()}catch(e){}})(()=>{const{readable:e}=new TransformStream,t=new MessageChannel;t.port1.postMessage(e,[e]),t.port1.close(),t.port2.close(),a=!0,Object.defineProperty(i,"TransformStream",{configurable:!1,writable:!1,value:TransformStream})}),i});

7
thirdparty/mitm.html vendored Normal file
View File

@ -0,0 +1,7 @@
<!--
https://github.com/jimmywarting/StreamSaver.js/blob/master/mitm.html
// MIT License
-->
<script>
let keepAlive=()=>{keepAlive=(()=>{});var e=location.href.substr(0,location.href.lastIndexOf("/"))+"/ping",a=setInterval(()=>{sw?sw.postMessage("ping"):fetch(e).then(e=>e.text(!e.ok&&clearInterval(a)))},1e4)},messages=[];window.onmessage=(e=>messages.push(e));let sw=null,scope="";function registerWorker(){return navigator.serviceWorker.getRegistration("./").then(e=>e||navigator.serviceWorker.register("sw.js",{scope:"./thirdparty/"})).then(e=>{const a=e.installing||e.waiting;return scope=e.scope,(sw=e.active)||new Promise(r=>{a.addEventListener("statechange",fn=(()=>{"activated"===a.state&&(a.removeEventListener("statechange",fn),sw=e.active,r())}))})})}function onMessage(e){let{data:a,ports:r,origin:t}=e;if(!r||!r.length)throw new TypeError("[StreamSaver] You didn't send a messageChannel");if("object"!=typeof a)throw new TypeError("[StreamSaver] You didn't send a object");a.origin=t,a.referrer=a.referrer||document.referrer||t,a.streamSaverVersion=new URLSearchParams(location.search).get("version"),"1.2.0"===a.streamSaverVersion&&console.warn("[StreamSaver] please update streamsaver"),a.headers?new Headers(a.headers):console.warn("[StreamSaver] pass `data.headers` that you would like to pass along to the service worker\nit should be a 2D array or a key/val object that fetch's Headers api accepts"),"string"==typeof a.filename&&(console.warn("[StreamSaver] You shouldn't send `data.filename` anymore. It should be included in the Content-Disposition header option"),a.filename=a.filename.replace(/\//g,":")),a.size&&console.warn("[StreamSaver] You shouldn't send `data.size` anymore. It should be included in the content-length header option"),a.readableStream&&console.warn("[StreamSaver] You should send the readableStream in the messageChannel, not throught mitm"),a.pathname||(console.warn("[StreamSaver] Please send `data.pathname` (eg: /pictures/summer.jpg)"),a.pathname=Math.random().toString().slice(-6)+"/"+a.filename),a.pathname=a.pathname.replace(/^\/+/g,"");let n=t.replace(/(^\w+:|^)\/\//,"");if(a.url=new URL(`${scope+n}/${a.pathname}`).toString(),!a.url.startsWith(`${scope+n}/`))throw new TypeError("[StreamSaver] bad `data.pathname`");const s=a.readableStream?[r[0],a.readableStream]:[r[0]];return a.readableStream||a.transferringReadable||keepAlive(),sw.postMessage(a,s)}window.opener&&window.opener.postMessage("StreamSaver::loadedPopup","*"),navigator.serviceWorker?registerWorker().then(()=>{window.onmessage=onMessage,messages.forEach(window.onmessage)}):keepAlive();
</script>

4
thirdparty/polyfill.min.js vendored Normal file

File diff suppressed because one or more lines are too long

4
thirdparty/sw.js vendored Normal file
View File

@ -0,0 +1,4 @@
/* global self ReadableStream Response */
// https://github.com/jimmywarting/StreamSaver.js/blob/master/sw.js
// MIT License
self.addEventListener("install",()=>{self.skipWaiting()}),self.addEventListener("activate",e=>{e.waitUntil(self.clients.claim())});const map=new Map;function createStream(e){return new ReadableStream({start(t){e.onmessage=(({data:e})=>{if("end"===e)return t.close();"abort"!==e?t.enqueue(e):t.error("Aborted the download")})},cancel(){console.log("user aborted")}})}self.onmessage=(e=>{if("ping"===e.data)return;const t=e.data,n=t.url||self.registration.scope+Math.random()+"/"+("string"==typeof t?t:t.filename),a=e.ports[0],s=new Array(3);s[1]=t,s[2]=a,e.data.readableStream?s[0]=e.data.readableStream:e.data.transferringReadable?a.onmessage=(e=>{a.onmessage=null,s[0]=e.data.readableStream}):s[0]=createStream(a),map.set(n,s),a.postMessage({download:n})}),self.onfetch=(e=>{const t=e.request.url;if(t.endsWith("/ping"))return e.respondWith(new Response("pong"));const n=map.get(t);if(!n)return null;const[a,s,o]=n;map.delete(t);const r=new Headers({"Content-Type":"application/octet-stream; charset=utf-8","Content-Security-Policy":"default-src 'none'","X-Content-Security-Policy":"default-src 'none'","X-WebKit-CSP":"default-src 'none'","X-XSS-Protection":"1; mode=block"});let i=new Headers(s.headers||{});i.has("Content-Length")&&r.set("Content-Length",i.get("Content-Length")),i.has("Content-Disposition")&&r.set("Content-Disposition",i.get("Content-Disposition")),s.size&&(console.warn("Depricated"),r.set("Content-Length",s.size));let l="string"==typeof s?s:s.filename;l&&(console.warn("Depricated"),l=encodeURIComponent(l).replace(/['()]/g,escape).replace(/\*/g,"%2A"),r.set("Content-Disposition","attachment; filename*=UTF-8''"+l)),e.respondWith(new Response(a,{headers:r})),o.postMessage({debug:"Download started"})});

File diff suppressed because one or more lines are too long