v24.b ; cloudflare as meshcast support; motiondetection

This commit is contained in:
steveseguin 2023-09-08 00:25:35 -04:00
parent 7154b90c03
commit 883bd18410
8 changed files with 400 additions and 128 deletions

View File

@ -1,4 +1,4 @@
<html>
<html lang='en'>
<head>
<script type="text/javascript">
// MS Internet Explorer must not be given a chance to fail before I can give the user an error message.
@ -57,8 +57,8 @@
<meta property="twitter:description" content="Bring live video from your smartphone, computer, or friends directly into OBS Studio. 100% free." />
<meta property="twitter:image" content="./media/vdoNinja_logo_full.png" />
<meta name="msapplication-TileColor" content="#da532c" />
<meta name="theme-color" content="#ffffff" />
<link rel="stylesheet" href="./main.css?ver=361" />
<meta name="theme-color" content="#0f131d" />
<link rel="stylesheet" href="./main.css?ver=363" />
<script type="text/javascript" crossorigin="anonymous" src="./thirdparty/adapter.js"></script>
<style id="lightbox-animations" type="text/css"></style>
@ -92,7 +92,7 @@
<script type="text/javascript" crossorigin="anonymous" src="./thirdparty/CodecsHandler.js?ver=49"></script>
<script type="text/javascript" crossorigin="anonymous" src="./thirdparty/aes.js"></script>
<script type="text/javascript" crossorigin="anonymous" src="./webrtc.js?ver=686"></script>
<script type="text/javascript" crossorigin="anonymous" src="./webrtc.js?ver=693"></script>
<input id="zoomSlider" type="range" style="display: none;" />
<span id="electronDragZone" style="pointer-events: none; z-index:-10; position:absolute;top:0;left:0;width:100%;height:2%;-webkit-app-region: drag;min-height:20px;"></span>
<div id="header">
@ -103,8 +103,8 @@
</span>
</a>
<div id="head1">
<input type="text" autocorrect="off" autocapitalize="none" id="joinroomID" 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();" id='jumptoroomButton' role="button" aria-pressed="false" alt="Join room" title="Join room" >GO</button>
<input type="text" autocorrect="off" autocapitalize="none" id="joinroomID" name="joinroomID" tabindex="1" 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();" id='jumptoroomButton' role="button" aria-pressed="false" tabindex="1" alt="Join room" title="Join room" >GO</button>
</div>
<div id="head1a" class="hidden">
<input type="text" autocorrect="off" autocapitalize="none" id="joinbyURL" name="joinbyURL" size="22" placeholder="Load a website URL" alt="Enter the URL to load" title="Enter the URL to load"/>
@ -112,7 +112,7 @@
</div>
<div id="head5" class="hidden"></div>
<div id="head3" style="display: inline-block;" class="hidden">
<span style="color: #888;" id="copythisurl"> &nbsp;
<span style="color: #888;" id="copythisurl" tabindex="1" > &nbsp;
<span data-translate="copy-this-url">Copy this URL into an OBS "Browser Source"</span> <i style="color: #CCC;" class="las la-long-arrow-alt-right"></i> &nbsp;
</span>
</div>
@ -161,100 +161,100 @@
</div>
<div id="subControlButtons">
<div id="mediafileshare" onmousedown="event.preventDefault(); event.stopPropagation();" title="Stream a media file" aria-label="Stream a media file" alt="Stream a media file to others" onclick="getById('fileselector2').click();" tabindex="21" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="float hidden" style="cursor: pointer;">
<div id="mediafileshare" onmousedown="event.preventDefault(); event.stopPropagation();" title="Stream a media file" aria-label="Stream a media file" alt="Stream a media file to others" onclick="getById('fileselector2').click();" tabindex="2" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="float hidden" style="cursor: pointer;">
<i id="mediafilesharetoggle" onmousedown="event.preventDefault(); event.stopPropagation();" class="toggleSize las la-file-video"></i>
<input id="fileselector2" class="hidden" onchange="session.changePublishFile(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 id="blindAllGuests" title="Blind all guests in room (toggle)" alt="Blind all guests in room (toggle)" aria-label="Blind all guests in room" onmousedown="event.preventDefault(); event.stopPropagation();" onclick="blindAllGuests(this, event)" tabindex="16" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="hidden float" style="cursor: pointer;" >
<div id="blindAllGuests" title="Blind all guests in room (toggle)" alt="Blind all guests in room (toggle)" aria-label="Blind all guests in room" onmousedown="event.preventDefault(); event.stopPropagation();" onclick="blindAllGuests(this, event)" tabindex="2" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="hidden float" style="cursor: pointer;" >
<i class="toggleSize las la-eye"></i>
</div>
<div id="queuebutton" title="Load the next guest in queue" alt="Load the next guest in queue" aria-label="Load next guest in queue" onmousedown="event.preventDefault(); event.stopPropagation();" onclick="nextQueue()" tabindex="16" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="hidden float" style="cursor: pointer;" >
<div id="queuebutton" title="Load the next guest in queue" alt="Load the next guest in queue" aria-label="Load next guest in queue" onmousedown="event.preventDefault(); event.stopPropagation();" onclick="nextQueue()" tabindex="2" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="hidden float" style="cursor: pointer;" >
<i id="queuetoggle" class="toggleSize las la-stream"></i>
<div id="queueNotification"></div>
</div>
<div id="sharefilebutton" title="Transfer any file to the group" alt="Transfer any file to the group" aria-label="Select file to transfer" onmousedown="event.preventDefault(); event.stopPropagation();" onclick="toggleFileshare()" tabindex="16" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="float hidden" style="cursor: pointer;display:none!important;" >
<div id="sharefilebutton" title="Transfer any file to the group" alt="Transfer any file to the group" aria-label="Select file to transfer" onmousedown="event.preventDefault(); event.stopPropagation();" onclick="toggleFileshare()" tabindex="2" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="float hidden" style="cursor: pointer;display:none!important;" >
<i id="filesharetoggle" class="toggleSize las la-file-upload"></i>
<div id="transferNotification"></div>
</div>
<div id="chatbutton" title="Toggle the Chat" alt="Toggle the Chat" aria-label="Text chat" onmousedown="event.preventDefault(); event.stopPropagation();" onclick="toggleChat()" tabindex="16" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="hidden float" style="cursor: pointer;" >
<div id="chatbutton" title="Toggle the Chat" alt="Toggle the Chat" aria-label="Text chat" onmousedown="event.preventDefault(); event.stopPropagation();" onclick="toggleChat()" tabindex="2" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="hidden float" style="cursor: pointer;" >
<i id="chattoggle" class="toggleSize las la-comment-alt"></i>
<div id="chatNotification"></div>
</div>
<div id="mutespeakerbutton" onmousedown="event.preventDefault(); event.stopPropagation();" alt="Toggle the speaker output." aria-label="Mute Speaker output" title="Mute the Speaker" onclick="toggleSpeakerMute()" tabindex="17" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="hidden float" style="cursor: pointer;" >
<div id="mutespeakerbutton" onmousedown="event.preventDefault(); event.stopPropagation();" alt="Toggle the speaker output." aria-label="Mute Speaker output" title="Mute the Speaker" onclick="toggleSpeakerMute()" tabindex="2" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="hidden float" style="cursor: pointer;" >
<i id="mutespeakertoggle" class="toggleSize las la-volume-up" style="position: relative; top: 0.5px;"></i>
</div>
<div id="mutebutton" onmousedown="toggleMute(false, event);event.preventDefault(); event.stopPropagation();" alt="Mute the Mic" aria-label="Mute Microphone" title="Mute the Mic" ontouchstart="toggleMute(false, event);event.preventDefault(); event.stopPropagation();" tabindex="18" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="hidden float" style="cursor: pointer;">
<div id="mutebutton" onmousedown="toggleMute(false, event);event.preventDefault(); event.stopPropagation();" alt="Mute the Mic" aria-label="Mute Microphone" title="Mute the Mic" ontouchstart="toggleMute(false, event);event.preventDefault(); event.stopPropagation();" tabindex="2" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="hidden float" style="cursor: pointer;">
<i id="mutetoggle" class="toggleSize las la-microphone" style="position: relative; top: 0.5px;"></i>
</div>
<div id="mutevideobutton" onmousedown="event.preventDefault(); event.stopPropagation();" title="Disable the Camera" alt="Disable the Camera" aria-label="Mute Camera" onclick="toggleVideoMute()" tabindex="19" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="hidden float" style="cursor: pointer;">
<div id="mutevideobutton" onmousedown="event.preventDefault(); event.stopPropagation();" title="Disable the Camera" alt="Disable the Camera" aria-label="Mute Camera" onclick="toggleVideoMute()" tabindex="2" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="hidden float" style="cursor: pointer;">
<i id="mutevideotoggle" onmousedown="event.preventDefault(); event.stopPropagation();" class="toggleSize las la-video"></i>
</div>
<div id="screensharebutton" onmousedown="event.preventDefault(); event.stopPropagation();" title="Share a Screen with others" alt="Share a Screen with others" aria-label="Share a screen" onclick="screenshareTypeDecider(1)" tabindex="20" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="float hidden task" data-menu="context-menu-screen-share" style="cursor: pointer;">
<div id="screensharebutton" onmousedown="event.preventDefault(); event.stopPropagation();" title="Share a Screen with others" alt="Share a Screen with others" aria-label="Share a screen" onclick="screenshareTypeDecider(1)" tabindex="2" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="float hidden task" data-menu="context-menu-screen-share" style="cursor: pointer;">
<i id="screensharetoggle" onmousedown="event.preventDefault(); event.stopPropagation();" class="toggleSize las la-desktop"></i>
</div>
<div id="screenshare2button" onmousedown="event.preventDefault(); event.stopPropagation();" title="Add a Screen Share" alt="Add a Screen Share" aria-label="Share a screen" onclick="screenshareTypeDecider(2)" tabindex="20" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="float hidden task" data-menu="context-menu-screen-share" style="cursor: pointer;">
<div id="screenshare2button" onmousedown="event.preventDefault(); event.stopPropagation();" title="Add a Screen Share" alt="Add a Screen Share" aria-label="Share a screen" onclick="screenshareTypeDecider(2)" tabindex="2" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="float hidden task" data-menu="context-menu-screen-share" style="cursor: pointer;">
<i id="screenshare2toggle" onmousedown="event.preventDefault(); event.stopPropagation();" class="toggleSize las la-tv"></i>
</div>
<div id="screenshare3button" onmousedown="event.preventDefault(); event.stopPropagation();" title="Share a Screen with others" alt="Add a Screen Share" aria-label="Share a screen" onclick="screenshareTypeDecider(3)" tabindex="20" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="float hidden task" data-menu="context-menu-screen-share" style="cursor: pointer;">
<div id="screenshare3button" onmousedown="event.preventDefault(); event.stopPropagation();" title="Share a Screen with others" alt="Add a Screen Share" aria-label="Share a screen" onclick="screenshareTypeDecider(3)" tabindex="2" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="float hidden task" data-menu="context-menu-screen-share" style="cursor: pointer;">
<i id="screenshare3toggle" onmousedown="event.preventDefault(); event.stopPropagation();" class="toggleSize las la-tv"></i>
</div>
<div id="websitesharebutton" onmousedown="event.preventDefault(); event.stopPropagation();" title="Share a website with your guests (IFRAME)" aria-label="Share a website" alt="Share a website with your guests (IFRAME)" onclick="shareWebsite(false, event)" tabindex="21" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="float hidden" style="cursor: pointer;">
<div id="websitesharebutton" onmousedown="event.preventDefault(); event.stopPropagation();" title="Share a website with your guests (IFRAME)" aria-label="Share a website" alt="Share a website with your guests (IFRAME)" onclick="shareWebsite(false, event)" tabindex="2" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="float hidden" style="cursor: pointer;">
<i id="websitesharetoggle" onmousedown="event.preventDefault(); event.stopPropagation();" class="toggleSize las la-window-maximize"></i>
</div>
<div id="websitesharebutton2" onmousedown="event.preventDefault(); event.stopPropagation();" title="Hold CTRL (or CMD) and click to spotlight this video" alt="Share a website as an embedded iFRAME" aria-label="Share a website" onclick="shareWebsite(false, event)" tabindex="21" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="float2 orange shake hidden" style="cursor: pointer;max-width: 200px;margin: auto;padding: 0 10px;">
<div id="websitesharebutton2" onmousedown="event.preventDefault(); event.stopPropagation();" title="Hold CTRL (or CMD) and click to spotlight this video" alt="Share a website as an embedded iFRAME" aria-label="Share a website" onclick="shareWebsite(false, event)" tabindex="2" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="float2 orange shake hidden" style="cursor: pointer;max-width: 200px;margin: auto;padding: 0 10px;">
<i onmousedown="event.preventDefault(); event.stopPropagation();" class="toggleSize las la-window-close" style="display: inline-block;"></i>
<div style="display: inline-block;width: 85px;line-height: 1; font-size: 0.9em; background-color: unset;box-shadow: unset;">
Stop Sharing Website
</div>
</div>
<div id="fullscreenPage" onmousedown="event.preventDefault(); event.stopPropagation();" title="Full-screen the page" alt="Full-screen the page" aria-label="Full screen" onclick="fullscreenPageToggle()" tabindex="21" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="float hidden" style="cursor: pointer;">
<div id="fullscreenPage" onmousedown="event.preventDefault(); event.stopPropagation();" title="Full-screen the page" alt="Full-screen the page" aria-label="Full screen" onclick="fullscreenPageToggle()" tabindex="2" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="float hidden" style="cursor: pointer;">
<i id="fullscreenPageToggle" onmousedown="event.preventDefault(); event.stopPropagation();" class="toggleSize las la-expand-arrows-alt"></i>
</div>
<div id="PictureInPicturePage" onmousedown="event.preventDefault(); event.stopPropagation();" title="Picture-in-Picture the video mix" alt="Picture-in-Picture the page" aria-label="Picture-in-Picture" onclick="PictureInPicturePageToggle()" tabindex="21" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="float hidden" style="cursor: pointer;">
<div id="PictureInPicturePage" onmousedown="event.preventDefault(); event.stopPropagation();" title="Picture-in-Picture the video mix" alt="Picture-in-Picture the page" aria-label="Picture-in-Picture" onclick="PictureInPicturePageToggle()" tabindex="2" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="float hidden" style="cursor: pointer;">
<i id="PictureInPicturePageToggle" onmousedown="event.preventDefault(); event.stopPropagation();" class="toggleSize las la-external-link-square-alt"></i>
</div>
<div id="flipcamerabutton" onmousedown="event.preventDefault(); event.stopPropagation();" title="Cycle the Cameras" onclick="cycleCameras()" class="hidden float" tabindex="21" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" style="cursor: pointer;" aria-label="Cycle Cameras" alt="Cycle the Cameras">
<div id="flipcamerabutton" onmousedown="event.preventDefault(); event.stopPropagation();" title="Cycle the Cameras" onclick="cycleCameras()" class="hidden float" tabindex="2" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" style="cursor: pointer;" aria-label="Cycle Cameras" alt="Cycle the Cameras">
<i id="settingstoggle" class="toggleSize las la-sync-alt"></i>
</div>
<div id="obscontrolbutton" onmousedown="event.preventDefault(); event.stopPropagation();" title="OBS Remote Controller; start/stop and change scenes." onclick="toggleOBSControls();" class="hidden float" tabindex="22" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" style="cursor: pointer;" aria-label="Remote OBS control menu" alt="Toggle the Remote OBS Controls Menu">
<div id="obscontrolbutton" onmousedown="event.preventDefault(); event.stopPropagation();" title="OBS Remote Controller; start/stop and change scenes." onclick="toggleOBSControls();" class="hidden float" tabindex="2" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" style="cursor: pointer;" aria-label="Remote OBS control menu" alt="Toggle the Remote OBS Controls Menu">
<i id="obscontroltoggle" class="toggleSize las la-gamepad"></i>
</div>
<div id="roomsettingsbutton" onmousedown="event.preventDefault(); event.stopPropagation();" title="Room Settings" onclick="toggleRoomSettings();" class="hidden float" tabindex="22" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" style="cursor: pointer;" alt="Toggle the Room Settings Menu" aria-label="Room settings menu">
<div id="roomsettingsbutton" onmousedown="event.preventDefault(); event.stopPropagation();" title="Room Settings" onclick="toggleRoomSettings();" class="hidden float" tabindex="2" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" style="cursor: pointer;" alt="Toggle the Room Settings Menu" aria-label="Room settings menu">
<i id="roomsettingstoggle" class="toggleSize las la-users-cog"></i>
</div>
<div id="settingsbutton" onmousedown="event.preventDefault(); event.stopPropagation();" title="Your audio and video Settings" onclick="toggleSettings()" class="hidden float" tabindex="22" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" style="cursor: pointer;" alt="Toggle Settings Menu" aria-label="Settings menu">
<div id="settingsbutton" onmousedown="event.preventDefault(); event.stopPropagation();" title="Your audio and video Settings" onclick="toggleSettings()" class="hidden float" tabindex="2" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" style="cursor: pointer;" alt="Toggle Settings Menu" aria-label="Settings menu">
<i id="settingstoggle" class="toggleSize las la-cog"></i>
</div>
<div id="hangupbutton" onmousedown="event.preventDefault(); event.stopPropagation();" title="Hangup the Call" aria-label="Hang up" alt="Hangup the Call" onclick="hangup()" class="hidden float" tabindex="23" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" style="cursor: pointer;" >
<div id="hangupbutton" onmousedown="event.preventDefault(); event.stopPropagation();" title="Hangup the Call" aria-label="Hang up" alt="Hangup the Call" onclick="hangup()" class="hidden float" tabindex="2" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" style="cursor: pointer;" >
<i class="toggleSize 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" aria-label="Raise hand" alt="Alert the host you want to speak" tabindex="24" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" onclick="raisehand()" class="hidden float" style="cursor: pointer;">
<div id="raisehandbutton" onmousedown="event.preventDefault(); event.stopPropagation();" data-raised="0" title="Alert the host you want to speak" aria-label="Raise hand" alt="Alert the host you want to speak" tabindex="2" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" onclick="raisehand()" class="hidden float" style="cursor: pointer;">
<i class="toggleSize las la-hand-paper" style="position: relative; right: 1px;" aria-hidden="true"></i>
</div>
<div id="pptbackbutton" onmousedown="event.preventDefault(); event.stopPropagation();" title="Go back a slide" aria-label="Back a slide" alt="Go back a slide" tabindex="25" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" onclick="gobackSlide()" class="hidden float red" style="cursor: pointer;">
<div id="pptbackbutton" onmousedown="event.preventDefault(); event.stopPropagation();" title="Go back a slide" aria-label="Back a slide" alt="Go back a slide" tabindex="2" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" onclick="gobackSlide()" class="hidden float red" style="cursor: pointer;">
<i class="toggleSize las la-chevron-left" style="position: relative; right: 1px;" aria-hidden="true"></i>
</div>
<div id="pptnextbutton" onmousedown="event.preventDefault(); event.stopPropagation();" title="Next slide" aria-label="Next slide" alt="Next slide" tabindex="25" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" onclick="nextSlide()" class="hidden float" style="cursor: pointer;">
<div id="pptnextbutton" onmousedown="event.preventDefault(); event.stopPropagation();" title="Next slide" aria-label="Next slide" alt="Next slide" tabindex="2" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" onclick="nextSlide()" class="hidden float" style="cursor: pointer;">
<i class="toggleSize las la-chevron-right" 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" aria-label="Record your stream to disk" alt="Record your stream to disk" tabindex="25" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" onclick="recordLocalVideoToggle();" class="hidden float" style="cursor: pointer;">
<div id="recordLocalbutton" onmousedown="event.preventDefault(); event.stopPropagation();" data-state="0" title="Record your stream to disk" aria-label="Record your stream to disk" alt="Record your stream to disk" tabindex="2" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" onclick="recordLocalVideoToggle();" class="hidden float" style="cursor: pointer;">
<i class="toggleSize las la-dot-circle" style="position: relative;" aria-hidden="true"></i>
</div>
<div id="recordLocalScreenbutton" onmousedown="event.preventDefault(); event.stopPropagation();" data-state="0" title="Stop screen share recording" aria-label="Stop screen share recording" alt="Stop screen recording" tabindex="25" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" onclick="recordLocalScreenStopRecord();" class="hidden float" style="cursor: pointer;">
<div id="recordLocalScreenbutton" onmousedown="event.preventDefault(); event.stopPropagation();" data-state="0" title="Stop screen share recording" aria-label="Stop screen share recording" alt="Stop screen recording" tabindex="2" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" onclick="recordLocalScreenStopRecord();" class="hidden float" style="cursor: pointer;">
<small>Stop<br>Screen<br>Record</small>
</div>
<span id="miniPerformer" style="pointer-events: auto;" class="hidden"></span>
<span id="rooms" class="hidden" style="padding-top:3px;padding-left:6px;pointer-events: auto;color:#fff;"></span>
<span id="groups" class="hidden" style="padding-top:3px;padding-left:6px;pointer-events: auto;color:#fff;text-align: center;"></span>
<div id="hangupbutton2" onmousedown="event.preventDefault(); event.stopPropagation();" title="Cancel the Director's Video/Audio" onclick="hangup2()" class="hidden float" tabindex="26" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" style="cursor: pointer;" aria-label="stop publishing audio and video" alt="Disconnect Direcotor's cam">
<div id="hangupbutton2" onmousedown="event.preventDefault(); event.stopPropagation();" title="Cancel the Director's Video/Audio" onclick="hangup2()" class="hidden float" tabindex="2" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" style="cursor: pointer;" aria-label="stop publishing audio and video" alt="Disconnect Direcotor's cam">
<i class="toggleSize las la-phone rotate225" aria-hidden="true"></i>
</div>
</div>
@ -270,27 +270,31 @@
onclick="submitDebugLog();"
style="cursor: pointer;z-index:3;display:none;"
class="hidden"
role="button"
tabindex="3"
>
<i style="cursor: pointer; color: #d9e4eb; padding: 2px; margin: 2px 2px 0 0; font-size: 140%;" class="las la-bug" aria-hidden="true"></i>
</span>
<span
id="helpbutton"
title="Show Help Info"
title="Show help contact info"
onclick="warnUser('For support, please browse https://reddit.com/r/vdoninja or join the live chat on Discord at https://discord.vdo.ninja.\n\nThe Docs also contains many help guides and advanced settings, located at https://docs.vdo.ninja.\n\nTo access the video stats menu, hold CTRL (command) and Left-Click on a video. Most video issues can be fixed by using Wired Internet instead of Wi-Fi.')"
style="cursor: pointer; display:none;"
alt="How to Use This with OBS"
role="button"
tabindex="3"
>
<i style="cursor: pointer; color: #d9e4eb; padding: 2px; margin: 2px 2px 0 0; font-size: 140%;" class="las la-question-circle" aria-hidden="true"></i>
</span>
<span title="Language Options" onclick="toggle(document.getElementById('languages'));" aria-label="language options" aria-pressed="false" id="translateButton">
<span title="Language options" onclick="toggle(document.getElementById('languages'));" tabindex="3" role="button" aria-label="language options" aria-pressed="false" id="translateButton">
<i style="cursor: pointer;color: #d9e4eb; padding: 2px; margin: 2px 2px 0 0; font-size: 140%;" class="las la-language" aria-hidden="true"></i>
</span>
<span title="Add to Calendar" onclick="toggle(document.getElementById('calendar'));" id="calendarButton">
<span title="Add to Calendar" onclick="toggle(document.getElementById('calendar'));" tabindex="3" role="button" id="calendarButton">
<i style="cursor: pointer; color: #d9e4eb; padding: 2px; margin: 2px 2px 0 0; font-size: 140%;" class="las la-calendar" aria-hidden="true"></i>
</span>
</span>
<div id="mainmenu" class="row" style="opacity: 0;">
<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 rounded card" style=" overflow-y: auto;">
<div id="container-1" title="Add Group Chat to OBS" alt="Add Group Chat to OBS" tabindex="1" role="button" aria-pressed="false" onkeyup="enterPressedClick(event,this);" class="column columnfade pointer rounded card" style=" overflow-y: auto;">
<h2>
<span data-translate="add-group-chat">Create a Room</span>
@ -413,21 +417,21 @@
</div>
</div>
</div>
<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 rounded 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="1" role="button" aria-pressed="false" class="column columnfade pointer rounded card" onclick="previewWebcam()" style=" overflow-y: auto;">
<h2 id="add_camera">
<span data-translate="add-your-camera">Add your Camera to OBS</span>
</h2>
<div class="container-inner" id="add_camera_inner">
<br />
<p>
<video id="previewWebcam" class="previewWebcam task" title="Right-click this video for additional options" data-menu="context-menu-video" oncanplay="updateStats();" controlsList="nodownload" muted autoplay playsinline ></video>
<video id="previewWebcam" class="previewWebcam task" aria-hidden="true" title="Right-click this video for additional options" data-menu="context-menu-video" oncanplay="updateStats();" controlsList="nodownload" muted autoplay playsinline ></video>
</p>
<div id="infof"></div>
<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>
<span style="display:block;">
<button onclick="publishWebcam(this)" title="start streaming" aria-label="Start streaming" aria-pressed="false" tabindex="15" id="gowebcam" class="gowebcam" alt="Start Streaming" disabled data-audioready="false" data-ready="false" >
<button onclick="publishWebcam(this)" title="start streaming" aria-label="Start streaming" role="button" aria-pressed="false" tabindex="1" id="gowebcam" class="gowebcam" alt="Start Streaming" disabled data-audioready="false" data-ready="false" >
<span data-translate="waiting-for-camera">Waiting for Camera to Load</span>
</button>
</span>
@ -435,18 +439,18 @@
<i class="las la-exclamation-circle"></i>
<p><span data-translate="privacy-disabled">Privacy warning: The director will be able to remotely change your camera, microphone, and URL.</span></p>
</div>
<div id="guestTips" style="display:none">
<div id="guestTips" style="display:none" aria-hidden="true">
<p data-translate="for-the-best-possible-experience-make-sure">For the best possible experience, make sure</p>
<span><i class="las la-plug"></i><span data-translate="your-device-is-powered">Your device is powered</span></span>
<span><i class="las la-ethernet"></i><span data-translate="your-connection-is-hardwired-instead-of-wifi">Your connection is hardwired instead of wifi</span></span>
<span><i class="las la-headphones"></i><span data-translate="you-are-using-headphones-earphones">You are using headphones / earphones</span></span>
</div>
<div id="videoMenu" class="videoMenu">
<div id="videoMenu" class="videoMenu" aria-hidden="true">
<div class="title">
<i class="las la-video"></i><span data-translate="video-source"> Video Source </span>
</div>
<span style="display:inline-block;padding-top: 5px;">
<select id="videoSourceSelect" alt="Video source list"></select>
<select id="videoSourceSelect" tabindex="1" title="Video source list"></select>
<span id="gear_webcam" onclick="toggle(document.getElementById('videoSettings'));">
<i class="las la-cog" style="font-size: 140%; vertical-align: middle;" aria-hidden="true"></i>
</span>
@ -458,7 +462,7 @@
</div>
<br />
<center>
<div id="videoSettings" style="display: none;">
<div id="videoSettings" style="display: none;" aria-hidden="true">
<form id="webcamquality">
<input type="radio" id="fullhd" alt="1080p60 video capture" name="resolution" value="0" />
<label for="fullhd">
@ -478,7 +482,7 @@
</form>
</div>
</center>
<div id="audioMenu" class="form-group multiselect" alt="tip: Hold CTRL (command) to select Multiple" title="tip: Hold CTRL (command) to select Multiple">
<div id="audioMenu" class="form-group multiselect" alt="tip: Hold CTRL (command) to select Multiple" title="tip: Hold CTRL (command) to select Multiple" aria-hidden="true">
<span class='gear_microphone hidden'>
<input type="checkbox" id='micStereoMonoInput' alt="Mono microphone audio" onchange="toggleMonoStereoMic(this);">Mono
</span>
@ -503,9 +507,9 @@
</div>
</div>
<br style="line-height: 0;" />
<div id="headphonesDiv" class="audioMenu">
<div id="headphonesDiv" class="audioMenu" aria-hidden="true">
<div class="title">
<i class="las la-headphones"></i><span data-translate="select-output-source"> Audio Output Destination</span><button onclick="playtone()" class="testtonebutton" type="button">Test</button>
<i class="las la-headphones"></i><span data-translate="select-output-source"> Audio Output Destination</span><button onclick="playtone()" title="Play a sound out of the selected audio playback device" class="testtonebutton" type="button">Test</button>
</div>
<select id="outputSource" alt="Audio output device" ></select>
<div id="headphoneTip1" class="cameraTip hidden">
@ -514,7 +518,7 @@
</div>
</div>
<br style="line-height: 0;" />
<div id="avatarDiv" class="hidden">
<div id="avatarDiv" class="hidden" aria-hidden="true">
<div class="title">
<i class="las la-robot"></i><span data-translate="select-avatar-image"> Default Avatar / Placeholder Image </span>
</div>
@ -531,7 +535,7 @@
</div>
</div>
<br style="line-height: 0;" />
<div id="effectsDiv">
<div id="effectsDiv" aria-hidden="true">
<div class="title">
<i class="las la-robot"></i><span data-translate="select-digital-effect"> Digital Video Effects </span>
</div>
@ -565,25 +569,25 @@
</div>
<br style="line-height: 0;" />
<div id="addPasswordBasic">
<div class="title">
<div class="title" title="Add an optional password">
<i class="las la-key"></i><span data-translate="add-a-password"> Add a Password</span>
</div>
<input type="text" id="passwordBasicInput" placeholder="optional"/>
<input type="text" id="passwordBasicInput" title="Enter an optional password here" placeholder="optional"/>
</div>
<div id="SafariWarning" class="startupWarning hidden">
<div id="SafariWarning" class="startupWarning hidden" aria-hidden="true" title="Consider using Chrome instead of Safari">
<i class="las la-exclamation-circle"></i>
<p><span data-translate="use-chrome-instead">Consider using a Chromium-based browser instead.<br />
Safari is more prone to having audio issues</span></p>
</div>
<div id="oldiOSWarning" class="startupWarning hidden">
<div id="oldiOSWarning" class="startupWarning hidden" title="Please update your version of iOS for best performance">
<i class="las la-exclamation-circle"></i>
<p><span data-translate="update-your-device">We've detected that you are using an old version of Apple iOS, which is known to have many issues.<br /><br />Please consider updating.</span></p>
</div>
</div>
<div class="outer close">
<div class="outer close" role="button" aria-pressed="false" title="Go back">
<div class="inner">
<label class="labelclass">
<span data-translate="back">Back</span>
@ -591,11 +595,11 @@
</div>
</div>
</div>
<div id="container-3a" title="Add your Microphone to OBS" onkeyup="enterPressedClick(event,this);" alt="Add your Microphone to OBS" tabindex="3" role="button" aria-pressed="false" class="microphoneBackground column columnfade pointer rounded card hidden" onclick="previewWebcam(true)" style=" overflow-y: auto;">
<div id="container-3a" title="Add your Microphone to OBS" onkeyup="enterPressedClick(event,this);" alt="Add your Microphone to OBS" tabindex="1" role="button" aria-pressed="false" class="microphoneBackground column columnfade pointer rounded card hidden" onclick="previewWebcam(true)" style=" overflow-y: auto;">
<h2 id="add_microphone">
<span data-translate="add-your-microphone">Add your Microphone to OBS</span>
</h2>
<div class="outer close">
<div class="outer close" role="button" aria-pressed="false" title="Go back">
<div class="inner">
<label class="labelclass">
<span data-translate="back">Back</span>
@ -603,7 +607,7 @@
</div>
</div>
</div>
<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 rounded card" style=" overflow-y: auto;">
<div id="container-2" title="Remote Screenshare into OBS" onkeyup="enterPressedClick(event,this);" alt="Remote Screenshare into OBS" tabindex="1" role="button" aria-pressed="false" class="column columnfade pointer rounded card" style=" overflow-y: auto;">
<h2 id="add_screen">
<span data-translate="remote-screenshare-obs">Remote Screenshare into OBS</span>
</h2>
@ -676,7 +680,7 @@
<div id="audioScreenCaptureDocs2" data-translate="1080p-screen-capture-guide">For achieving 1080p60 game-capture, <a href='https://docs.vdo.ninja/guides/how-to-screen-share-in-1080p' target="_blank">see here</a></div>
</div>
<div class="outer close">
<div class="outer close" title="Go back">
<div class="inner">
<label class="labelclass">
<span data-translate="back">Back</span>
@ -684,7 +688,7 @@
</div>
</div>
</div>
<div id="container-4" tabindex="5" alt="Create Reusable Invite" onkeyup="enterPressedClick(event,this);" onclick="loadQR();" title="Create Reusable Invite" role="button" aria-pressed="false" class="column columnfade pointer rounded card" style=" overflow-y: auto;">
<div id="container-4" tabindex="1" alt="Create Reusable Invite" onkeyup="enterPressedClick(event,this);" onclick="loadQR();" title="Create Reusable Invite" role="button" aria-pressed="false" class="column columnfade pointer rounded card" style=" overflow-y: auto;">
<h2>
<span data-translate="create-reusable-invite">Create Reusable Invite</span>
</h2>
@ -802,7 +806,7 @@
</div>
</span>
</div>
<div class="outer close">
<div class="outer close" role="button" aria-pressed="false">
<div class="inner">
<label class="labelclass">
<span data-translate="back">Back</span>
@ -811,7 +815,7 @@
</div>
</div>
<div id="dropButton" onclick="dropDownButtonAction()" title="More Options"><i class="las la-chevron-down" ></i></div>
<div id="dropButton" onclick="dropDownButtonAction()" title="More Options" aria-hidden="true"><i class="las la-chevron-down" ></i></div>
<div id="container-5" class="column columnfade pointer rounded card hidden" style=" overflow-y: auto;">
<h2><span data-translate="share-local-video-file">Stream Media File</span></h2>
@ -976,27 +980,27 @@
<br />
<li>Youtube video
<i class="lab la-youtube"></i>
<a href="https://www.youtube.com/watch?v=QaA_6aOP9z8&list=PLWodc2tCfAH1l_LDvEyxEqFf42hOBKqQM&index=1" alt="Youtube video demoing VDO.Ninja">Demoing it here</a>
<a title="Open a YouTube video demoing the basics of VDO.Ninja" href="https://www.youtube.com/watch?v=QaA_6aOP9z8&list=PLWodc2tCfAH1l_LDvEyxEqFf42hOBKqQM&index=1" alt="Youtube video demoing VDO.Ninja">Demoing it here</a>
</li>
<br />
<i>
<a href="https://docs.vdo.ninja/common-errors-and-known-issues/known-issues" title="For more known issues, click here" target="_blank"><span style="color: red;">Known issues:</span></a>
<a href="https://docs.vdo.ninja/common-errors-and-known-issues/known-issues" title="For a list of common or known issues, click here" target="_blank"><span style="color: red;">Known issues:</span></a>
</i>
<br />
<li>
If the video fails to load in OBS Studio, where the browser source remains blank, try disabling hardware-acceleration or
<a href='https://docs.vdo.ninja/common-errors-and-known-issues/obs.ninja-doesnt-show-up-in-obs-or-is-choppy' title="link out to the vdo.ninja help guide for OBS Studio" target="_blank">refer to this help guide</a> for more.
<a href='https://docs.vdo.ninja/common-errors-and-known-issues/obs.ninja-doesnt-show-up-in-obs-or-is-choppy' title="Click to link out to the VDO.Ninja help guide for common OBS Studio problems" target="_blank">refer to this help guide</a> for more.
</li>
<li>
Samsung smartphones (A-series) may fail to publish video with some mobile browsers; try using Firefox Mobile or the native <a href='https://docs.vdo.ninja/getting-started/native-mobile-app-versions#android-download-link'>Android app</a> in these cases.
Samsung smartphones (A-series) may fail to publish video with some mobile browsers; try using Firefox Mobile or the native <a href='https://docs.vdo.ninja/getting-started/native-mobile-app-versions#android-download-link' title="Info on the native app versions of VDO.Ninja">Android app</a> in these cases.
</li>
<br />
<h4>
<span style="color:#daad09;">Welcome to VDO Ninja! We've rebranded! Nothing else is changing and we're staying 100% free.</span>
</h4>
<br />
🌻 Site last updated on July 25th. Development <a target="_blank" href='https://updates.vdo.ninja/'>updates are here.</a>
🌻 Site last updated on July 25th. Development <a target="_blank" title="Open a page with recent VDO.Ninja development and feature updates" href='https://updates.vdo.ninja/'>updates are here.</a>
<br />
<br />
<h3>
@ -1007,11 +1011,11 @@
</div>
</center>
</div>
<form method="post" onsubmit="setFormSubmitting()" style="display: none;">
<form method="post" onsubmit="setFormSubmitting()" style="display: none;" aria-hidden="true">
<input type="submit" />
</form>
<div id="credits" class="credits">
<a href='https://github.com/steveseguin/vdoninja'>VDO.Ninja, by Steve Seguin</a>
<div id="credits" class="credits" aria-hidden="true">
<a href='https://github.com/steveseguin/vdoninja' aria-hidden="true">VDO.Ninja, by Steve Seguin</a>
</div>
</div>
<div id="directorlayout" class="hidden directorsgrid">
@ -2328,7 +2332,7 @@
<input style="margin:0 10px;display:inline-block;padding: 8px 10px 6px 10px;" placeholder="Enter the remote OBS password here" />
</div>
<small style="margin: 20px 0 0 0;display:block;" >
See the <a href="https://docs.vdo.ninja/advanced-settings/upcoming-parameters/and-obs" style="color:#314350; cursor:pointer;" target="_blank">documentation</a> for help on using the remote OBS controller
See the <a href="https://docs.vdo.ninja/advanced-settings/upcoming-parameters/and-obs" style="cursor:pointer;" target="_blank">documentation</a> for help on using the remote OBS controller
</small>
<div id="debugRemoteOBSControl" class="hidden">
</div>
@ -2626,11 +2630,11 @@
// session.hidehome = true; // If used, 'hide home' will make the landing page inaccessible, along with hiding a few go-home elements.
// session.record = false; // uncomment to block users from being able to record via vdo.ninja's built in recording function
</script>
<script type="text/javascript" crossorigin="anonymous" id="lib-js" src="./lib.js?ver=897"></script>
<script type="text/javascript" crossorigin="anonymous" id="lib-js" src="./lib.js?ver=904"></script>
<!--
// If you wish to change branding, blank offers a good clean start.
<script type="text/javascript" id="main-js" src="./main.js" data-translation="blank"></script>
-->
<script type="text/javascript" crossorigin="anonymous" id="main-js" src="./main.js?ver=715"></script>
<script type="text/javascript" crossorigin="anonymous" id="main-js" src="./main.js?ver=725"></script>
</body>
</html>

206
lib.js
View File

@ -1461,6 +1461,7 @@ session.sceneSync = function(UUID){
}
}
var TriggerOnNewDetails = false;
session.obsStateSync = function(data2send=false, uid=false){
if (session.disableOBS){return;}
if (!window.obsstudio){return;} // this isn't OBS
@ -1468,6 +1469,15 @@ session.obsStateSync = function(data2send=false, uid=false){
log(data2send);
if (data2send && (data2send == "sourceActive") && session.obsState.sourceActive){
TriggerOnNewDetails = true;
} else if (data2send && (data2send == "details") && session.obsState.sourceActive && TriggerOnNewDetails){
if (session.obsState.details && session.obsState.details.currentScene && session.obsState.details.currentScene.name){
session.obsState.details.thisScene = session.obsState.details.currentScene.name;
TriggerOnNewDetails = false;
}
}
var needOptimize = false;
if (session.obsState.visibility!==null){
if (session.obsState.visibility===false){ /////////////////// I need to change tis to .state or whatever, anc catch/handle these events to update the buttons in the pop up menu
@ -1575,7 +1585,7 @@ session.getOBSOptimization = function(msg, UUID){
return msg;
}
function getOBSDetails(){
function getOBSDetails(callbackname = "details"){
if (session.disableOBS){return false;}
if (!window.obsstudio){return;}
@ -1609,7 +1619,7 @@ function getOBSDetails(){
session.obsState.details[shortkey] = out;
delete promises[key]
if (!Object.keys(promises).length){
session.obsStateSync("details");
session.obsStateSync(callbackname);
}
});
} catch(e){
@ -1631,7 +1641,7 @@ function getOBSDetails(){
});
delete promises.main;
if (!Object.keys(promises).length){
session.obsStateSync("details");
session.obsStateSync(callbackname);
}
}
@ -1797,8 +1807,10 @@ function manageSceneState(data, UUID){ // incoming obs details
if (control >= 4){
if (session.director || !session.roomid){
if (session.obsControls!==false){
getById("obscontrolbutton").classList.remove("hidden"); // so they get a tip.
if (session.pcs[UUID].remote){
if (session.obsControls!==false){
getById("obscontrolbutton").classList.remove("hidden"); // so they get a tip.
}
}
}
}
@ -2819,9 +2831,9 @@ function changeSceneLowBandwidth(state){
if (!session.lowBitrateSceneChange){return;}
if (!session.obsState){return;}
try {
if (session.obsState.visibility && session.obsState.details && session.obsState.details.currentScene){
if (session.obsState.sourceActive && session.obsState.details && session.obsState.details.currentScene){
changeSceneLowBandwidthRevert = session.obsState.details.currentScene.name || false;
} else if (("visibility" in session.obsState) && !session.obsState.visibilit && session.obsState.details && session.obsState.details.currentScene){
} else if (("sourceActive" in session.obsState) && !session.obsState.sourceActive && session.obsState.details && session.obsState.details.currentScene){
if (session.obsState.details.currentScene.name !== session.lowBitrateSceneChange){
return; // not the FML scene, nor are we visible, so we're not going to switch back. Assume the user has overtaken the setup.
}
@ -2955,6 +2967,9 @@ function setupIncomingScreenTracking(v, UUID){ // SCREEN element.
if (document.getElementById("mainmenu")){
var m = getById("mainmenu");
m.remove();
document.querySelectorAll(".hidden2").forEach(ele2=>{
ele2.classList.remove("hidden2");
});
}
if (session.director){
@ -3334,6 +3349,9 @@ function setupIncomingVideoTracking(v, UUID){ // video element.
if (document.getElementById("mainmenu")){
var m = getById("mainmenu");
m.remove();
document.querySelectorAll(".hidden2").forEach(ele2=>{
ele2.classList.remove("hidden2");
});
}
if (session.director){
@ -4325,7 +4343,6 @@ function updateMixerRun(e=false){ // this is the main auto-mixing code. It's a
}
}
var i = null;
var countOrder = 0;
try{
@ -4361,7 +4378,7 @@ function updateMixerRun(e=false){ // this is the main auto-mixing code. It's a
continue;
}
} catch(e){}
}
}
applyMuteState(i);
var doNotPush = false;
@ -4521,7 +4538,7 @@ function updateMixerRun(e=false){ // this is the main auto-mixing code. It's a
continue; // easiest is to just not show anything if no video and no audio track.
}
if (session.rpcs[i].videoElement.srcObject){
session.rpcs[i].videoElement.srcObject.getVideoTracks().forEach(track=>{
session.rpcs[i].videoElement.srcObject.getVideoTracks().forEach(track=>{
session.rpcs[i].videoElement.srcObject.removeTrack(track);
session.rpcs[i].videoElement.load();
});
@ -7166,6 +7183,64 @@ function drawFrameMirrored(mirror=true, flip=false) {
session.canvasCtx.restore();
}
function motionDetection(video, threshold = 15, sensitivity=75){
var targetSize = 16;
if (!video.motionDetector){
video.motionDetector = {};
video.motionDetector.canvas = document.createElement("canvas");
video.motionDetector.canvas.width = targetSize;
video.motionDetector.canvas.height = targetSize;
try {
video.motionDetector.ctx = video.motionDetector.canvas.getContext("2d", { willReadFrequently: true });
} catch(e){
video.motionDetector.ctx = video.motionDetector.canvas.getContext("2d");
}
video.motionDetector.previous = [];
for (var y = 0; y < targetSize; y++) {
for (var x = 0; x < targetSize; x++) {
video.motionDetector.previous.push(0);
}
}
}
var motionDetector = video.motionDetector;
motionDetector.ctx.drawImage(video, 0, 0, video.videoWidth, video.videoHeight, 0, 0, targetSize, targetSize);
var data = motionDetector.ctx.getImageData(0, 0, targetSize, targetSize).data;
var matches = 0;
for (var y = 0; y < targetSize; y++) {
for (var x = 0; x < targetSize; x++) {
var pos = y*targetSize+x;
var pos2 = pos*3;
var value = data[pos2] + data[pos2+1] + data[pos2+2]; // convert to to greyscale
if (motionDetector.previous[pos] && Math.abs(motionDetector.previous[pos] - value) > sensitivity) {
matches+=1;
}
motionDetector.previous[pos] = value;
}
}
if (matches >= threshold){
log("MOTION DETECTED: "+matches);
if (window.obsstudio && window.obsstudio["setCurrentScene"]){
if (!changeSceneEnabled){ // the bit cut scene change is already active.
if (session.obsState && session.obsState.details && session.obsState.details.thisScene && session.obsState.details.currentScene){
if (session.obsState.details.thisScene !== session.obsState.details.currentScene.name){ // don't trigger it multiple times; makes it hard to prep next scene
window.obsstudio["setCurrentScene"](session.obsState.details.thisScene);
}
}
}
}
pokeIframeAPI('motion-detected', true, video.dataset.UUID || true);
if (session.infocus!==(video.dataset.UUID || true)){
if (!session.layout){
session.infocus = video.dataset.UUID || true;
updateMixer();
}
}
}
}
function setupCanvas() {
log("SETUP CANVAS");
if (session.canvas === null) {
@ -11955,7 +12030,7 @@ function hangup2() {
getById("screensharebutton").ariaPressed = "false";
if (session.showDirector == false) {
if (!session.showDirector) {
getById("miniPerformer").innerHTML = '<button id="press2talk" onmousedown="event.preventDefault(); event.stopPropagation();" class="float" onclick="press2talk(true);" title="You can also enable the director`s Video Output afterwards by clicking the Setting`s button"><i class="las la-headset"></i><span data-translate="push-to-talk-enable"> enable director`s microphone or video<br />(only guests can see this feed)</span></button>';
miniTranslate(getById("miniPerformer"));
} else {
@ -12336,7 +12411,7 @@ function getDetailedState(sid=false){
} else {
item.featured = false;
}
} else if (session.infocus && session.infocus===session.rpcs[UUID].streamID){
} else if (session.infocus && session.infocus===UUID){
item.featured = true;
} else {
item.featured = false;
@ -12447,13 +12522,13 @@ function getDetailedState(sid=false){
if (session.director){
let featured = query("#highlightDirector[data-action-type='solo-video'], #container_director [data-action-type='solo-video']");
let featured = document.querySelector("#highlightDirector[data-action-type='solo-video'], #container_director [data-action-type='solo-video']");
if (featured && parseInt(featured.value)){
streamList[session.streamID].featured = true;
} else {
streamList[session.streamID].featured = false;
}
} else if (session.infocus && session.infocus===session.streamID){
} else if (session.infocus && session.infocus===true){
streamList[session.streamID].featured = true;
} else {
streamList[session.streamID].featured = false;
@ -16656,7 +16731,7 @@ async function toggleCoDirector(ele){
if (session.password===false){
getById("codirectorSettings_invite").value += "&password=false";
} else{
getById("codirectorSettings_invite").value += "&password";
getById("codirectorSettings_invite").value += "&password="+session.password;
}
}
@ -16826,9 +16901,13 @@ async function createRoomCallback(passAdd, passAdd2) {
formSubmitting = false;
var m = getById("mainmenu");
m.remove();
try {
var m = getById("mainmenu");
m.remove();
document.querySelectorAll(".hidden2").forEach(ele2=>{
ele2.classList.remove("hidden2");
});
} catch(e){}
getById("head1").className = 'hidden';
getById("head2").className = 'hidden';
@ -16869,7 +16948,7 @@ async function createRoomCallback(passAdd, passAdd2) {
if (session.password==false){
getById("codirectorSettings_invite").value += "&password=false";
} else{
getById("codirectorSettings_invite").value += "&password";
getById("codirectorSettings_invite").value += "&password="+session.password;
}
}
@ -16956,12 +17035,12 @@ async function createRoomCallback(passAdd, passAdd2) {
getById("roomsettingsbutton").classList.remove("hidden");
}
if (session.showDirector == false) {
if (!session.showDirector) { // if null or false, we want to show the solo link, since the director won't have their control box. The director will be visible in their solo link
getById("miniPerformer").innerHTML = '<button id="press2talk" onmousedown="event.preventDefault(); event.stopPropagation();" class="float" onclick="press2talk(true);" title="You can also enable the director`s Video Output afterwards by clicking the Setting`s button"><i class="las la-headset"></i><span data-translate="push-to-talk-enable"> enable director`s microphone or video<br />(only guests can see this feed)</span></button>';
miniTranslate(getById("miniPerformer"));
getById("grabDirectorSoloLink").dataset.raw = "https://" + location.host + location.pathname + "?solo&sd&r=" + session.roomid + "&v="+session.streamID + passAdd2 + wss + token;
getById("grabDirectorSoloLink").href = "https://" + location.host + location.pathname + "?solo&sd&r=" + session.roomid + "&v="+session.streamID + passAdd2 + wss + token;
getById("grabDirectorSoloLink").innerText = "https://" + location.host + location.pathname + "?solo&sd&r=" + session.roomid + "&v="+session.streamID + passAdd2 + wss + token;
getById("grabDirectorSoloLink").dataset.raw = "https://" + location.host + location.pathname + "?solo&r=" + session.roomid + "&v="+session.streamID + passAdd2 + wss + token;
getById("grabDirectorSoloLink").href = "https://" + location.host + location.pathname + "?solo&r=" + session.roomid + "&v="+session.streamID + passAdd2 + wss + token;
getById("grabDirectorSoloLink").innerText = "https://" + location.host + location.pathname + "?solo&r=" + session.roomid + "&v="+session.streamID + passAdd2 + wss + token;
getById("grabDirectorSoloLinkParent").classList.remove("hidden");
} else {
getById("miniPerformer").innerHTML = '<button id="press2talk" onmousedown="event.preventDefault(); event.stopPropagation();" class="float" onclick="press2talk(true);" title="You can also enable the director`s Video Output afterwards by clicking the Setting`s button"><i class="las la-headset"></i><span data-translate="push-to-talk-enable-2"> enable director`s microphone or video</span></button>';
@ -22078,6 +22157,7 @@ async function grabVideo(quality = 0, eleName = 'previewWebcam', selector = "sel
document.getElementById("gowebcam").disabled = false;
//document.getElementById("gowebcam").innerHTML = getTranslation("start");
miniTranslate(document.getElementById("gowebcam"),"start");
document.getElementById("gowebcam").focus();
}
}
}
@ -22461,6 +22541,7 @@ async function grabVideo(quality = 0, eleName = 'previewWebcam', selector = "sel
document.getElementById("gowebcam").disabled = false;
//document.getElementById("gowebcam").innerHTML = getTranslation("start");
miniTranslate(document.getElementById("gowebcam"),"start");
document.getElementById("gowebcam").focus();
}
}
}
@ -23625,6 +23706,7 @@ function senderAudioUpdate(callback=false, tracks=false){
if (document.getElementById("gowebcam").dataset.ready && (document.getElementById("gowebcam").dataset.ready=="true")){
document.getElementById("gowebcam").disabled = false;
miniTranslate(document.getElementById("gowebcam"),"start");
document.getElementById("gowebcam").focus();
}
}
}
@ -24980,6 +25062,9 @@ function updateReshareLink(){
try{
var m = getById("mainmenu");
m.remove();
document.querySelectorAll(".hidden2").forEach(ele2=>{
ele2.classList.remove("hidden2");
});
} catch (e){}
var added = "";
@ -29508,6 +29593,7 @@ function setupWebcamSelection(miconly=false) {
miniTranslate(document.getElementById("gowebcam"),"start");
document.getElementById("gowebcam").dataset.audioready = "true";
document.getElementById("gowebcam").dataset.ready = "true";
document.getElementById("gowebcam").focus();
setTimeout(function(){updateForceRotate();},1000);
if (session.autostart) {
@ -29535,6 +29621,7 @@ function setupWebcamSelection(miconly=false) {
if (document.getElementById("gowebcam").dataset.audioready == "true"){
document.getElementById("gowebcam").disabled = false;
miniTranslate(document.getElementById("gowebcam"),"start");
document.getElementById("gowebcam").focus();
//document.getElementById("gowebcam").innerHTML = getTranslation("start");
}
}
@ -30019,6 +30106,7 @@ function previewWebcam(miconly=false) {
document.getElementById("gowebcam").disabled = false;
//document.getElementById("gowebcam").innerHTML = getTranslation("start");
miniTranslate(document.getElementById("gowebcam"),"start");
document.getElementById("gowebcam").focus();
}
}
return;
@ -33347,7 +33435,7 @@ session.onTrack = function(event, UUID){
}
}
}
if (screenshare){break;}
if (screenshare){break;}
}
if (screenshare){break;}
}
@ -33414,9 +33502,9 @@ session.onTrack = function(event, UUID){
if ( e1.track.kind=="video"){
updateIncomingVideoElement(UUID, true, false);
} else {
updateIncomingVideoElement(UUID, false, true);
updateIncomingVideoElement(UUID, false, true);
}
updateIncomingVideoElement(UUID); // session.rpcs[UUID].videoElement.srcObject = session.rpcs[UUID].streamSrc;
// updateIncomingVideoElement(UUID); // session.rpcs[UUID].videoElement.srcObject = session.rpcs[UUID].streamSrc;
setTimeout(function(){updateMixer();},1);
} catch(e){}
};
@ -33588,6 +33676,15 @@ function updateIncomingVideoElement(UUID, video=true, audio=true){
}
}
});
if (session.motionSwitch && !session.rpcs[UUID].motionDetectionInterval){
session.rpcs[UUID].motionDetectionInterval = setTimeout(function(){
setInterval(function(){
motionDetection(session.rpcs[UUID].videoElement, session.motionSwitch);
},400);
},2000);
}
}
if (audio){
updateIncomingAudioElement(UUID) // do the same for audio now.
@ -36319,6 +36416,7 @@ async function whepIn(whepInput=false,whepInputToken=false, UUID=false){ // PLAY
session.rpcs[UUID].lockedVideoBitrate = false; // doesn't do anything
session.rpcs[UUID].lockedAudioBitrate = false;
session.rpcs[UUID].manualBandwidth = false; // doesn't do anything, except maybe help keep track of pause/play states
session.rpcs[UUID].motionDetectionInterval = false;
}
var config = {...session.configuration};
@ -37181,28 +37279,40 @@ function setupCommands(){
updateMixer();
return true;
} else if (session.layouts && session.layouts[value]){
session.layout = session.layouts[value];
pokeIframeAPI("layout-updated", session.layout);
pokeIframeAPI("layout-index", value+1);
if (session.director){
var combined = {};
for (var i=0;i<session.layout.length;i++){
if (!session.layout[i]){continue;}
if (!("slot" in session.layout[i])){
continue;
try {
console.log(session.layouts);
session.layout = session.layouts[value];
pokeIframeAPI("layout-updated", session.layout);
pokeIframeAPI("layout-index", value+1);
if (session.director){
var combined = {};
for (var i=0;i<session.layout.length;i++){
if (!session.layout[i]){continue;}
if (!("slot" in session.layout[i])){
continue;
}
var slot = document.querySelector("div.slotsbar[data-slot='"+(parseInt(session.layout[i].slot)+1)+"']") || false
if (!slot){
console.error("Slot target not found?");
continue;
}
var streamID = slot.dataset.sid;
combined[streamID] = session.layout[i];
}
var slot = document.querySelector("div.slotsbar[data-slot='"+(parseInt(session.layout[i].slot)+1)+"']") || false
if (!slot){continue;}
var streamID = slot.dataset.sid;
combined[streamID] = session.layout[i];
session.layout = combined;
console.log("issuing layout:");
console.log(session.layout);
issueLayout(session.layout, "0");
}
session.layout = combined;
issueLayout(combined, "0");
} catch(e){
console.error(e);
}
updateMixer();
return true;
} else {
console.error("no layout found");
console.log(session.layouts);
return false;
}
@ -37680,6 +37790,11 @@ addEventToAll(".column", 'click', function(e, ele) {
});
addEventToAll(".close", 'click', function(e, ele) {
cleanupMediaTracks();
document.querySelectorAll(".hidden2").forEach(ele2=>{
ele2.classList.remove("hidden2");
});
ele.style.display = "none";
mapToAll(".container-inner", function(target) {
target.style.display = "none";
@ -37699,8 +37814,13 @@ addEventToAll(".column", 'animationend', function(e, ele) {
mapToAll(".close", function(target) {
target.style.display = "block";
}, ele);
document.querySelectorAll("#header, #miniTaskBarm, #credits, .columnfade").forEach(ele2=>{
if (ele2!==ele){
ele2.classList.add("hidden2");
}
});
mapToAll(".container-inner", function(target) {
target.style.display = "block";
target.style.display = "block";
}, ele);
} else if (e.animationName == 'outlightbox') {
ele.classList.remove('in-animation');

View File

@ -808,6 +808,7 @@ hr {
#gridlayout, #directorlayout {
padding: 0;
width: 100%;
height: 100%;
overflow: hidden;
justify-items: stretch;
border: 0;
@ -1537,6 +1538,9 @@ body {
opacity: 1;
transition: opacity .1s linear;
scrollbar-color:#666 #201c29;
display:flex;
flex-direction: column;
position: fixed;
}
body.darktheme{
background-color: var(--dark-background-color);
@ -3151,6 +3155,13 @@ video::-webkit-media-controls-panel {
height: 0px;
opacity: 0;
}
.hidden2 {
display: none !important;
visibility: hidden;
width: 0px;
height: 0px;
opacity: 0;
}
.grabLinks a:visited {
color: black !important;
}

36
main.js
View File

@ -743,6 +743,11 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
if (urlParams.has('bitratecutoff') || urlParams.has('bitcut')) {
session.lowBitrateCutoff = parseInt(urlParams.get('bitratecutoff')) || parseInt(urlParams.get('bitcut')) || 300; // low bitrate cut off.
}
if (urlParams.has('motionswitch') || urlParams.has('motiondetection')) { // switch OBS to this scene when there is motion, and "solo view" this video in the VDO.Ninja auto-mixer, if used
session.motionSwitch = parseInt(urlParams.get('motionswitch')) || parseInt(urlParams.get('motiondetection')) || 15; // threshold of motion needed to trigger
}
if (urlParams.has('locked')) {
session.locked = urlParams.get('locked');
@ -771,6 +776,19 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
session.rotate = parseInt(session.rotate);
}
if (urlParams.has("rotatewindow") || urlParams.has("rotatepage")){
let rotateThis = parseInt(urlParams.get("rotatewindow")) || parseInt(urlParams.get("rotatepage")) || 90;
if (rotateThis==270){
document.body.setAttribute( "style", "transform: rotate(270deg);position: absolute;top: 100vh;left: 0;height: 100vw;width: 100vh;transform-origin: 0 0;");
} else if (rotateThis==90){
document.body.setAttribute( "style", "transform: rotate(90deg);position: absolute;top: 0;left: 100vw;height: 100vw;width: 100vh;transform-origin: 0 0;");
} else if (rotateThis==180){
document.body.setAttribute( "style", "transform: rotate(180deg);position: absolute;top: 100vh;left: 100vw;height: 100vh;width: 100vw;transform-origin: 0 0;");
} else {
document.body.setAttribute( "style", "");
}
}
if (urlParams.has('facing') ) {
session.facingMode = urlParams.get('facing') || false;
}
@ -1065,6 +1083,7 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
}
if (urlParams.has('layout')) {
session.accept_layouts = true;
try {
session.layout = JSON.parse(decodeURIComponent(urlParams.get('layout'))) || JSON.parse(urlParams.get('layout')) || {};
} catch(e){
@ -2467,6 +2486,10 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
if (urlParams.has('chroma')) {
log("Chroma ENABLED");
getById("main").style.backgroundColor = "#" + (urlParams.get('chroma') || "0F0");
//try {
// document.querySelector('meta[name="theme-color"]')?.setAttribute('content', "#" + (urlParams.get('chroma') || "0F0")); .. meh
//} catch(e){}
//const ogColor = document.querySelector('meta[name="theme-color"]')?.getAttribute('content');
} // else if (window.obsstudio || (navigator.userAgent.toLowerCase().indexOf(' electron/') > -1)){
// getById("main").style.backgroundColor = "rgba(0,0,0,0)";
//}
@ -4086,6 +4109,14 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
session.wss = "wss://" + session.wss;
}
}
} else if (urlParams.has('wss2')) {
session.wssSetViaUrl = true;
if (urlParams.get('wss2')) {
session.wss = urlParams.get('wss2');
if (!session.wss.startsWith("wss://")){
session.wss = "wss://" + session.wss;
}
}
}
if (urlParams.has("bypass")){
@ -4734,6 +4765,11 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
errorlog(e);
};
}
if (urlParams.get('auth')) {
session.auth = urlParams.get('auth');
}
if (urlParams.has('waitimage')){
session.waitImage = urlParams.get('waitimage') || false;

View File

@ -2610,9 +2610,13 @@
}
}
if (e.data.action === "layout-updated"){
console.log(e.data);
}
if (e.data.action === "layout-index"){
if ("value" in e.data){
console.log(e.data);
var idx = parseInt(e.data.value) || 0;
if (!idx){
var ele = document.getElementById("automix");

View File

@ -317,10 +317,88 @@
</div>
<script>
var domain = "./";
var urlParams = new URLSearchParams(window.location.search);
var iframe = document.createElement("iframe");
function changeParam(url, paramName, paramValue) {
paramName = paramName.replace("?", "");
var qind = url.indexOf('?');
url = url.replace("?", "&");
var params = url.substring(qind + 1).split('&');
var query = '';
var match = false;
for (var i = 0; i < params.length; i++) {
var tokens = params[i].split('=');
var name = tokens[0];
var value = "";
if (tokens.length > 1 && tokens[1] !== '') {
value = tokens[1];
}
if (name == paramName) {
if (match) {
continue;
} // already matched the first time.
match = true;
value = paramValue;
}
if (value !== "") {
value = '=' + value;
}
if (query == '') {
query = "?" + name + value;
} else {
query = query + '&' + name + value;
}
}
return url.substring(0, qind) + query;
}
function updateURL(param, force = false, cleanUrl = false) {
param = param.replace("?", "");
var para = param.split('=');
if (cleanUrl) {
if (history.pushState) {
var href = new URL(cleanUrl);
if (para.length == 1) {
href = changeParam(cleanUrl, para[0], "");
} else {
href = changeParam(cleanUrl, para[0], para[1]);
}
window.history.pushState({path: href.toString()}, '', href.toString());
}
} else if (!(urlParams.has(para[0]))) { // don't need to replace as it doesn't exist.
if (history.pushState) {
var href = window.location.href;
href = href.replace("??", "?");
var arr = href.split('?');
var newurl;
if (arr.length > 1 && arr[1] !== '') {
newurl = href + '&' + param;
} else {
newurl = href + '?' + param;
}
window.history.pushState({path: newurl.toString()}, '', newurl.toString());
}
} else if (force) {
if (history.pushState) {
var href = new URL(window.location.href);
if (para.length == 1) {
href = changeParam(window.location.href, para[0], "");
} else {
href = changeParam(window.location.href, para[0], para[1]);
}
window.history.pushState({path: href.toString()}, '', href.toString());
}
}
urlParams = new URLSearchParams(window.location.search);
}
if (urlParams.has("rotate")){
document.querySelector("#rotation").value = urlParams.get("rotate") || 0;
} else if (localStorage.getItem('rotation')){
@ -342,18 +420,41 @@ if (localStorage.getItem('sourceType')){
}
if (localStorage.getItem('iframeURL') || urlParams.has("link")){
document.querySelector("#iframeURL").value = decodeURIComponent(urlParams.get("link") || "") || localStorage.getItem('iframeURL') || "";
try {
document.querySelector("#iframeURL").value = decodeURIComponent(urlParams.get("link") || "") || localStorage.getItem('iframeURL') || "";
} catch(e){
document.querySelector("#iframeURL").value = urlParams.get("link") || localStorage.getItem('iframeURL') || "";
}
}
if (localStorage.getItem('iframeURL_twitch') || urlParams.has("twitch")){
document.querySelector("#iframeURL_twitch").value = decodeURIComponent(urlParams.get("twitch") || "") || localStorage.getItem('iframeURL_twitch') || "";
try {
document.querySelector("#iframeURL_twitch").value = decodeURIComponent(urlParams.get("twitch") || "") || localStorage.getItem('iframeURL_twitch') || "";
} catch(e){
document.querySelector("#iframeURL_twitch").value = urlParams.get("twitch") || localStorage.getItem('iframeURL_twitch') || "";
}
}
if (localStorage.getItem('iframeURL_youtube') || urlParams.has("youtube")){
document.querySelector("#iframeURL").value = decodeURIComponent(urlParams.get("youtube") || "") || localStorage.getItem('iframeURL_youtube') || "";
try {
document.querySelector("#iframeURL").value = decodeURIComponent(urlParams.get("youtube") || "") || localStorage.getItem('iframeURL_youtube') || "";
} catch(e){
document.querySelector("#iframeURL").value = urlParams.get("youtube") || localStorage.getItem('iframeURL_youtube') || "";
}
}
var menuOffset = "70px";
if ( urlParams.has("hidemenu") || urlParams.has("hide") || urlParams.has("hidebar")){
container.classList.add("hidden");
menuOffset = "0px";
}
loadPage();
function hidebar(){
// updateURL("hidemenu");
container.classList.add("hidden");
menuOffset = "0px";
loadPage();
@ -561,15 +662,8 @@ async function loadPage(){
var urlEdited = window.location.search.replace(/\?\?/g, "?");
urlEdited = urlEdited.replace(/\?/g, "&");
urlEdited = urlEdited.replace(/\&/, "?");
if (urlEdited !== window.location.search){
if (!urlParams.has("link")){
urlEdited += "&link="+ encodeURIComponent(iframeURL);
}
urlEdited = urlEdited.replace(/\?/g, "&");
urlEdited = urlEdited.replace(/\&/, "?");
window.history.pushState({path: urlEdited.toString()}, '', urlEdited.toString());
}
updateURL("link="+ encodeURIComponent(iframeURL), true);
iframe.allow = "encrypted-media;sync-xhr;usb;web-share;cross-origin-isolated;accelerometer;midi;geolocation;autoplay;camera;microphone;fullscreen;picture-in-picture;display-capture;";
iframe.src = iframeURL;
@ -586,6 +680,9 @@ function rotatePage(){
var rotate = document.querySelector("#rotation").value || 0;
var flip = document.querySelector("#transform").value;
updateURL("rotate="+rotate, true);
updateURL("flip="+flip, true);
localStorage.setItem('iframeURL', document.getElementById('iframeURL').value);
localStorage.setItem('rotation', document.getElementById('rotation').value);
//localStorage.setItem('backgroundColor', document.getElementById('backgroundColor').value);

File diff suppressed because one or more lines are too long