mirror of
https://github.com/eliasstepanik/vdo.ninja.git
synced 2026-01-10 21:28:34 +00:00
whip page only partially done in this push
This commit is contained in:
parent
7ee9653dfd
commit
5a42948014
@ -1,14 +1,19 @@
|
||||
<html>
|
||||
<head><title>Twitch + Video</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=0.7, maximum-scale=1.0, user-scalable=yes" />
|
||||
<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0,user-scalable=no" />
|
||||
<meta content="text/html;charset=utf-8" http-equiv="Content-Type" />
|
||||
<style>
|
||||
|
||||
body{
|
||||
padding:0;
|
||||
margin:0;
|
||||
background-color:#003;
|
||||
width:100%;
|
||||
height:100%;
|
||||
background-color:#000;
|
||||
width:100vw;
|
||||
height:100vh;
|
||||
color:white;
|
||||
overscroll-behavior: contain;
|
||||
overflow: hidden;
|
||||
display:block;
|
||||
}
|
||||
|
||||
iframe {
|
||||
@ -17,33 +22,92 @@ iframe {
|
||||
border:0;
|
||||
margin:0;
|
||||
padding:0;
|
||||
position:absolute;
|
||||
display:block;
|
||||
}
|
||||
|
||||
|
||||
input{
|
||||
padding:10px;
|
||||
width:80%;
|
||||
font-size:1.2em;
|
||||
padding:10px 2px;
|
||||
width:calc(100vw - 60px);
|
||||
font-size: min(4vw, 20px);
|
||||
margin:10px 0 30px 0;
|
||||
z-index: 1000;
|
||||
color:black;
|
||||
display:block;
|
||||
|
||||
}
|
||||
#clean{
|
||||
max-width:100%;
|
||||
width: 90vw;
|
||||
max-width: 100%;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
h1{
|
||||
color: white;
|
||||
font-family: verdana;
|
||||
margin: 10px;
|
||||
margin: 10px 3px 30px 3px;
|
||||
color:white;
|
||||
}
|
||||
|
||||
button {
|
||||
font-size: min(10vw, 28px);
|
||||
color:black;
|
||||
display:inline-block;
|
||||
}
|
||||
|
||||
#controlbar {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background-color: #0000;
|
||||
height: max(7vh, 50px);
|
||||
width: 100%;
|
||||
display: none;
|
||||
justify-content: center;
|
||||
}
|
||||
#container2{
|
||||
background-color:#000;
|
||||
}
|
||||
#container1{
|
||||
transition: all ease 0.4ms;
|
||||
}
|
||||
#controlbar button{
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
height: 100%;
|
||||
background-color: black;
|
||||
color: white;
|
||||
box-shadow: inset 0 0 20px 7px #FFF7;
|
||||
font-size: 1.0em;
|
||||
border-radius: 19px;
|
||||
max-width: 20%;
|
||||
margin: 0;
|
||||
padding: 0 2px;
|
||||
}
|
||||
.pressed {
|
||||
background-color: #A00!important;
|
||||
}
|
||||
|
||||
.loading {
|
||||
width: 100%!important; height:100%!important; position: absolute; top: 0; right:unset;left:0;opacity:0%; animation: fadeIn 3s;
|
||||
}
|
||||
.fullwindow {
|
||||
height: calc(100% - max(7vh, 50px)) !important; width:100%!important; position: absolute; top: max(7vh, 50px)!important; right:unset!important;left:0!important;
|
||||
}
|
||||
@keyframes fadeIn {
|
||||
0% { opacity: 0; }
|
||||
10% { opacity: 0; }
|
||||
50% { opacity: 0.2; }
|
||||
100% { opacity: 1; }
|
||||
}
|
||||
@media screen and (orientation:portrait) {
|
||||
#container2{
|
||||
width:100%;height:100%;display:none;
|
||||
}
|
||||
#container1{
|
||||
width: 50vw;height: 50vh; display:none; float:left; position: fixed; top: 0; right: 0%;
|
||||
}
|
||||
iframe{
|
||||
width:100%;
|
||||
width: 50vw;height: 50vh; display:none; top: 0; right: 0%; position: absolute;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@media screen and (orientation:landscape) {
|
||||
@ -52,27 +116,40 @@ h1{
|
||||
z-index:5;
|
||||
}
|
||||
#container1{
|
||||
width: 50vw;height: 80vh; display:none; float:left; position: fixed; top: 0; right: -10vw;
|
||||
}
|
||||
iframe{
|
||||
max-width:60vw;
|
||||
width: 50vw; height:100%; max-height: calc(100vh - 100px); display:none; position: fixed; top: 0; right: -10vw;
|
||||
}
|
||||
|
||||
}
|
||||
.hide{
|
||||
width: 1px!important;
|
||||
height: 1px!important;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
|
||||
<div id="container2"></div>
|
||||
<div id="container1" ></div>
|
||||
<div id="clean">
|
||||
<h1>Use VDO.Ninja and Twitch chat at the same time</h1>
|
||||
<input placeholder="Enter a VDON stream ID or VDON URL" id="viewlink" type="text" />
|
||||
<input placeholder="Enter the Twitch channel name" id="twitch" type="text" />
|
||||
<button onclick="loadIframes()" style="display:block;padding:10px;margin:10px;">START</button>
|
||||
</div>
|
||||
<div id="container2" ></div>
|
||||
<div id="container1" class="loading"></div>
|
||||
<div id="controlbar" >
|
||||
<button id="hidepreview">Hide Preview</button>
|
||||
<button id="mutemic">Mute Mic</button>
|
||||
<button id="mutevideo">Mute Video</button>
|
||||
<button id="togglesettings">Settings</button>
|
||||
<button id="hangup">Hangup</button>
|
||||
</div>
|
||||
<div id="clean">
|
||||
<h1>Use VDO.Ninja and Twitch chat at the same time</h1>
|
||||
VDO.Ninja Stream ID or URL:
|
||||
<input placeholder="Enter a VDON stream ID or VDON URL" id="viewlink" type="text" />
|
||||
Twitch Username or URL:
|
||||
<input placeholder="Enter the Twitch channel name" id="twitch" type="text" />
|
||||
<button onclick="loadIframes()" style="background-color: #d1fed1;; padding:10px;margin:10px;">START</button>
|
||||
<button onclick="clearInput()" style="background-color: #f4cccc;margin:10px 0px 10px 10vh;padding:10px;">CLEAR</button>
|
||||
<br /><br /><br />
|
||||
<p>
|
||||
This app lets you publish video/audio via VDO.Ninja at the same time as viewing your Twitch chat.<br /><br />If you have feature requests or suggestions, please report them at https://discord.vdo.ninja in the #feature-request channel.
|
||||
</p>
|
||||
</div>
|
||||
<script>
|
||||
|
||||
window.addEventListener("orientationchange", function() {
|
||||
@ -119,6 +196,28 @@ if (getStorage("twitchChatLink")){
|
||||
if (getStorage("vdoNinjaTwitchURL")){
|
||||
document.getElementById("viewlink").value = getStorage("vdoNinjaTwitchURL");
|
||||
}
|
||||
function clearInput(){
|
||||
var confirmit = confirm("Are you sure you want to clear the input fields and local storage?");
|
||||
if (confirmit){
|
||||
removeStorage("twitchChatLink");
|
||||
removeStorage("vdoNinjaTwitchURL");
|
||||
document.getElementById("viewlink").value = "";
|
||||
document.getElementById("twitch").value = "";
|
||||
}
|
||||
}
|
||||
|
||||
var iframe = null;
|
||||
function sendSelfCommand(action, value=null){
|
||||
iframe.contentWindow.postMessage({"target":null, "action":action, "value":value}, '*');
|
||||
}
|
||||
|
||||
var injectCSS = `
|
||||
#controlButtons{
|
||||
display:none!important;
|
||||
}
|
||||
`;
|
||||
|
||||
injectCSS = encodeURIComponent(btoa(injectCSS));
|
||||
|
||||
function loadIframes(url=false){
|
||||
|
||||
@ -129,25 +228,57 @@ function loadIframes(url=false){
|
||||
document.getElementById("container1").style.display="inline-block";
|
||||
document.getElementById("container2").style.display="inline-block";
|
||||
|
||||
|
||||
var path = window.location.host+window.location.pathname.split("/").slice(0,-1).join("/");
|
||||
path = path.replace("/examples","");
|
||||
|
||||
if (roomname.startsWith("https://")){
|
||||
var room1 = roomname;
|
||||
} else {
|
||||
var room1 = "https://"+path+"/?push="+roomname+"&webcam&autostart&vd=front&ad=1&transparent&noheader";
|
||||
var room1 = "https://"+path+"/?push="+roomname+"&webcam&autostart&vd=front&ad=1&transparent&noheader&fullscreen&cleanish&b64css="+injectCSS;
|
||||
}
|
||||
|
||||
var room2 = twitch.startsWith("https://")
|
||||
? twitch
|
||||
: `https://www.twitch.tv/embed/${twitch}/chat?parent=${location.hostname}`;
|
||||
var room2 = twitch.startsWith("https://") ? twitch : `https://www.twitch.tv/embed/${twitch}/chat?darkpopout&parent=${location.hostname}`;
|
||||
|
||||
var iframe = document.createElement("iframe");
|
||||
iframe = document.createElement("iframe");
|
||||
iframe.allow = "autoplay;camera;microphone;fullscreen;picture-in-picture;";
|
||||
iframe.src = room1;
|
||||
var iframeContainer = document.createElement("div");
|
||||
iframeContainer.appendChild(iframe);
|
||||
document.getElementById("container1").appendChild(iframeContainer);
|
||||
|
||||
document.getElementById("container1").appendChild(iframe);
|
||||
|
||||
//////////// LISTEN FOR EVENTS
|
||||
|
||||
var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
|
||||
var eventer = window[eventMethod];
|
||||
var messageEvent = eventMethod === "attachEvent" ? "onmessage" : "message";
|
||||
|
||||
|
||||
/// If you have a routing system setup, you could have just one global listener for all iframes instead.
|
||||
|
||||
eventer(messageEvent, function (e) {
|
||||
if (e.source != iframe.contentWindow){return} // reject messages send from other iframes
|
||||
console.warn(e.data);
|
||||
|
||||
if ("action" in e.data){
|
||||
if (e.data.action === "seeding-started"){
|
||||
document.getElementById("controlbar").style.display="inline-flex";
|
||||
document.getElementById("container1").classList.remove("loading");
|
||||
}
|
||||
|
||||
if (e.data.action === "settings-menu-state"){
|
||||
if (e.data.value==true){
|
||||
togglesettings.dataset.value = "true";
|
||||
togglesettings.classList.add("pressed");
|
||||
document.getElementById("container1").classList.add("fullwindow");
|
||||
} else {
|
||||
togglesettings.dataset.value = "false";
|
||||
togglesettings.classList.remove("pressed");
|
||||
document.getElementById("container1").classList.remove("fullwindow");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
setStorage("twitchChatLink", room2);
|
||||
@ -156,13 +287,69 @@ function loadIframes(url=false){
|
||||
|
||||
|
||||
setTimeout(function(){
|
||||
var iframe = document.createElement("iframe");
|
||||
iframe.allow = "autoplay;camera;microphone;fullscreen;picture-in-picture;";
|
||||
iframe.src = room2;
|
||||
var iframeContainer = document.createElement("div");
|
||||
iframeContainer.appendChild(iframe);
|
||||
document.getElementById("container2").appendChild(iframeContainer);
|
||||
},3000);
|
||||
var iframe2 = document.createElement("iframe");
|
||||
iframe2.allow = "autoplay;camera;microphone;fullscreen;picture-in-picture;";
|
||||
iframe2.src = room2;
|
||||
document.getElementById("container2").appendChild(iframe2);
|
||||
},1000);
|
||||
|
||||
}
|
||||
|
||||
var hangup = document.getElementById("hangup");
|
||||
hangup.onclick = function(){
|
||||
iframe.contentWindow.postMessage({"hangup":true}, '*');
|
||||
}
|
||||
|
||||
var togglesettings = document.getElementById("togglesettings");
|
||||
togglesettings.onclick = function(){
|
||||
iframe.contentWindow.postMessage({"toggleSettings":"toggle"}, '*');
|
||||
}
|
||||
|
||||
var mutemic = document.getElementById("mutemic");
|
||||
mutemic.onclick = function(){
|
||||
if (this.dataset.value!=="false"){
|
||||
this.dataset.value = "false";
|
||||
this.classList.add("pressed");
|
||||
this.innerText = "Un-Mute Mic";
|
||||
sendSelfCommand("mic",false);
|
||||
} else {
|
||||
this.classList.remove("pressed");
|
||||
this.innerText = "Mute Mic";
|
||||
this.dataset.value = "true";
|
||||
sendSelfCommand("mic",true);
|
||||
}
|
||||
}
|
||||
|
||||
var mutevideo = document.getElementById("mutevideo");
|
||||
mutevideo.onclick = function(){
|
||||
if (this.dataset.value!=="false"){
|
||||
this.dataset.value = "false";
|
||||
this.classList.add("pressed");
|
||||
this.innerText = "Un-Mute camera";
|
||||
sendSelfCommand("camera",false);
|
||||
} else {
|
||||
this.classList.remove("pressed");
|
||||
this.innerText = "Mute Camera";
|
||||
this.dataset.value = "true";
|
||||
sendSelfCommand("camera",true);
|
||||
}
|
||||
}
|
||||
|
||||
var hidepreview = document.getElementById("hidepreview");
|
||||
hidepreview.onclick = function(){
|
||||
if (this.dataset.value!=="false"){
|
||||
this.dataset.value = "false";
|
||||
this.classList.add("pressed");
|
||||
this.innerText = "Show Preview";
|
||||
document.getElementById("container1").classList.add("hide");
|
||||
document.getElementById("container2").classList.add("fullwindow");
|
||||
} else {
|
||||
this.classList.remove("pressed");
|
||||
this.innerText = "Hide Preview";
|
||||
this.dataset.value = "true";
|
||||
document.getElementById("container1").classList.remove("hide");
|
||||
document.getElementById("container2").classList.remove("fullwindow");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
23
index.html
23
index.html
@ -56,7 +56,7 @@
|
||||
<meta property="twitter:image" content="./media/vdoNinja_logo_full.png" />
|
||||
<meta name="msapplication-TileColor" content="#da532c" />
|
||||
<meta name="theme-color" content="#ffffff" />
|
||||
<link rel="stylesheet" href="./main.css?ver=342" />
|
||||
<link rel="stylesheet" href="./main.css?ver=344" />
|
||||
<script type="text/javascript" crossorigin="anonymous" src="./thirdparty/adapter.js"></script>
|
||||
<style id="lightbox-animations" type="text/css"></style>
|
||||
<!-- <link rel="manifest" href="manifest.json" /> -->
|
||||
@ -83,7 +83,7 @@
|
||||
|
||||
<script type="text/javascript" crossorigin="anonymous" src="./thirdparty/CodecsHandler.js?ver=47"></script>
|
||||
<script type="text/javascript" crossorigin="anonymous" src="./thirdparty/aes.js"></script>
|
||||
<script type="text/javascript" crossorigin="anonymous" src="./webrtc.js?ver=662"></script>
|
||||
<script type="text/javascript" crossorigin="anonymous" src="./webrtc.js?ver=665"></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">
|
||||
@ -269,7 +269,6 @@
|
||||
</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;">
|
||||
|
||||
<h2>
|
||||
@ -900,8 +899,13 @@
|
||||
<i style="margin-top:30px;font-size:600%;overflow:hidden;" class="largeDarkIcon las la-heartbeat"></i>
|
||||
</div>
|
||||
|
||||
<div id="container-17" class="column columnfade pointer rounded card hidden" style="overflow: hidden;" onclick="window.location = './whip';">
|
||||
<h2><span data-translate="publish-via-whip">Publish via WHIP</span></h2>
|
||||
<i style="margin-top:30px;font-size:600%;overflow:hidden;" class="largeDarkIcon las la-broadcast-tower"></i>
|
||||
</div>
|
||||
|
||||
<div id="container-16" class="column columnfade pointer rounded card hidden" style=" overflow-y: auto;">
|
||||
<h2><span data-translate="share-whepsrc">Share WHEP Media Source</span></h2>
|
||||
<h2><span data-translate="share-whepsrc">Share via WHEP</span></h2>
|
||||
<i style="margin-top:30px;font-size:560%;overflow:hidden;" class="largeDarkIcon las la-broadcast-tower"></i>
|
||||
<div class="container-inner">
|
||||
<br />
|
||||
@ -927,10 +931,15 @@
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<p></p>
|
||||
|
||||
<div id="unexpectedPushLink" class='hidden' style="width:100%;color: white;display:block;font-size:130%;">
|
||||
<h2>If this page is unexpected, double check your links.</h2>
|
||||
<b>?push=xxx</b> links are for sending video, while <b>?view=xxx</b> links are for viewing.
|
||||
</div>
|
||||
|
||||
<div id="info" class="fullcolumn columnfade">
|
||||
<center>
|
||||
<div class="infoblob" align="left">
|
||||
@ -2561,11 +2570,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=833"></script>
|
||||
<script type="text/javascript" crossorigin="anonymous" id="lib-js" src="./lib.js?ver=838"></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=650"></script>
|
||||
<script type="text/javascript" crossorigin="anonymous" id="main-js" src="./main.js?ver=655"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
205
lib.js
205
lib.js
@ -4239,8 +4239,8 @@ function updateMixerRun(e=false){ // this is the main auto-mixing code. It's a
|
||||
session.rpcs[i].mutedStateMixer = true;
|
||||
}
|
||||
if (!session.hiddenSceneViewBitrate){
|
||||
session.rpcs[i].videoElement.classList.add("nogb");
|
||||
}
|
||||
session.rpcs[i].videoElement.nogb = 2;
|
||||
}
|
||||
} else {
|
||||
if (session.groupAudio){
|
||||
session.requestRateLimit(0, i, false);
|
||||
@ -4445,7 +4445,7 @@ function updateMixerRun(e=false){ // this is the main auto-mixing code. It's a
|
||||
if (session.scene!==false){
|
||||
session.requestRateLimit(session.hiddenSceneViewBitrate, i, true); // hidden. I dont want it to be super low, for video quality reasons.
|
||||
if (!session.hiddenSceneViewBitrate){
|
||||
session.rpcs[i].videoElement.classList.add("nogb");
|
||||
session.rpcs[i].videoElement.nogb = 2;
|
||||
}
|
||||
} else {
|
||||
session.requestRateLimit(0, i, true); // w/e This is not in OBS, so we just set it as low as possible. Shoudln't exist really unless loading?
|
||||
@ -4523,6 +4523,13 @@ function updateMixerRun(e=false){ // this is the main auto-mixing code. It's a
|
||||
session.requestRateLimit(-1, i);
|
||||
}
|
||||
}
|
||||
if (session.rpcs[i].videoElement.nogb==2){
|
||||
session.rpcs[i].videoElement.nogb = 1;
|
||||
session.rpcs[i].videoElement.classList.add("nogb");
|
||||
} else if (session.rpcs[i].videoElement.nogb==1){
|
||||
session.rpcs[i].videoElement.nogb = 0;
|
||||
session.rpcs[i].videoElement.classList.remove("nogb");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -32538,7 +32545,7 @@ function updateIncomingVideoElement(UUID, video=true, audio=true){
|
||||
if (session.showControls===null){
|
||||
setTimeout(function(ele){
|
||||
if (ele){
|
||||
ele.controls=true;
|
||||
ele.controls = true;
|
||||
}
|
||||
},500, session.rpcs[UUID].videoElement);
|
||||
}
|
||||
@ -32777,6 +32784,54 @@ function addAudioPipeline(UUID, track){ // INBOUND AUDIO EFFECTS ; audio tracks
|
||||
return track;
|
||||
}
|
||||
|
||||
function processMiniInfoUpdate(miniInfo, UUID){
|
||||
if ("qlr" in miniInfo){
|
||||
session.rpcs[UUID].stats.info.quality_limitation_reason = miniInfo.qlr;
|
||||
}
|
||||
|
||||
if ("con" in miniInfo){
|
||||
session.rpcs[UUID].stats.info.conn_type = miniInfo.con;
|
||||
}
|
||||
|
||||
if ("cpu" in miniInfo){
|
||||
session.rpcs[UUID].stats.info.cpuLimited = miniInfo.cpu;
|
||||
if (session.rpcs[UUID].signalMeter){
|
||||
if (miniInfo.cpu){
|
||||
session.rpcs[UUID].signalMeter.dataset.cpu = "1";
|
||||
} else if ("cpu" in miniInfo){
|
||||
session.rpcs[UUID].signalMeter.dataset.cpu = "0";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ("hw_enc" in miniInfo){
|
||||
session.rpcs[UUID].stats.info.hardware_video_encoder = miniInfo.hw_enc;
|
||||
}
|
||||
|
||||
if ("bat" in miniInfo){
|
||||
if (typeof miniInfo.bat == "number"){
|
||||
session.rpcs[UUID].stats.info.power_level = miniInfo.bat*100;
|
||||
} else {
|
||||
session.rpcs[UUID].stats.info.power_level = null;
|
||||
}
|
||||
}
|
||||
if ("chrg" in miniInfo){
|
||||
session.rpcs[UUID].stats.info.plugged_in = miniInfo.chrg;
|
||||
}
|
||||
|
||||
if (("out" in miniInfo) && ("c" in miniInfo.out)){
|
||||
session.rpcs[UUID].stats.info.total_outbound_p2p_connections = miniInfo.out.c;
|
||||
if (session.showConnections && session.rpcs[UUID].connectionDetails){
|
||||
session.rpcs[UUID].connectionDetails.innerText = "🔗"+session.rpcs[UUID].stats.info.total_outbound_p2p_connections;
|
||||
session.rpcs[UUID].connectionDetails.dataset.value = session.rpcs[UUID].stats.info.total_outbound_p2p_connections;
|
||||
}
|
||||
}
|
||||
|
||||
if (session.rpcs[UUID].batteryMeter){
|
||||
batteryMeterInfoUpdate(UUID);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function batteryMeterInfoUpdate(UUID){
|
||||
if (session.rpcs[UUID].stats.info && (session.rpcs[UUID].stats.info.power_level!==null)){
|
||||
@ -33583,11 +33638,21 @@ function audioMeterGuest(mediaStreamSource, UUID, trackid){
|
||||
}
|
||||
|
||||
if (session.style==3 || session.meterStyle){ // overrides style
|
||||
if (session.meterStyle==4){
|
||||
if (session.rpcs[UUID].videoElement){
|
||||
session.rpcs[UUID].videoElement.dataset.loudness = total;
|
||||
if (session.rpcs[UUID].videoElement){
|
||||
if (total>40){
|
||||
session.rpcs[UUID].videoElement.dataset.speaking = "2";
|
||||
} else if (total>10){
|
||||
session.rpcs[UUID].videoElement.dataset.speaking = "1";
|
||||
} else {
|
||||
session.rpcs[UUID].videoElement.dataset.speaking = "0";
|
||||
}
|
||||
return; // this is cause we are using the data-loudness
|
||||
|
||||
if (session.meterStyle==4){
|
||||
session.rpcs[UUID].videoElement.dataset.loudness = total;
|
||||
return; // this is cause we are using the data-loudness
|
||||
}
|
||||
} else if (session.meterStyle==4){
|
||||
return;
|
||||
}
|
||||
} else if (session.scene!==false){ // if a scene, cancel
|
||||
return;
|
||||
@ -34435,6 +34500,108 @@ function resizeWindow(width, height){
|
||||
},5000);
|
||||
}
|
||||
|
||||
function configureWhipOutSDP(description){ // THIS IS FOR WHIP-OUTPUT; it has
|
||||
|
||||
var configs = false;
|
||||
|
||||
if (SafariVersion && (SafariVersion<=13) && (iOS || iPad)){
|
||||
// skip. Not going to try to tinker with older iOS SDPs
|
||||
} else if ((session.stereo==3) || (session.stereo==5) || (session.stereo==6) || (session.stereo==1)){ // stereo out
|
||||
configs = {
|
||||
'stereo': 1,
|
||||
'useinbandfec': session.noFEC ? 0 : 1,
|
||||
'maxptime': session.maxptime,
|
||||
'minptime': session.minptime,
|
||||
'ptime': session.ptime,
|
||||
'dtx': session.dtx // "usedtx", if no loud audio, stops sending audio for 400ms. default.
|
||||
};
|
||||
log("stereo enabled");
|
||||
} else if (iOS || iPad){ // iOS doesn't have multichannel, so why even bother
|
||||
configs = {
|
||||
'useinbandfec': session.noFEC ? 0 : 1,
|
||||
'maxptime': session.maxptime,
|
||||
'minptime': session.minptime,
|
||||
'ptime': session.ptime,
|
||||
'dtx': session.dtx // "usedtx", if no loud audio, stops sending audio for 400ms. default.
|
||||
};
|
||||
|
||||
} else if (session.stereo==4){
|
||||
configs = {
|
||||
'stereo': 2,
|
||||
'useinbandfec': session.noFEC ? 0 : 1,
|
||||
'maxptime': session.maxptime,
|
||||
'minptime': session.minptime,
|
||||
'ptime': session.ptime,
|
||||
'dtx': session.dtx // "usedtx", if no loud audio, stops sending audio for 400ms. default.
|
||||
};
|
||||
log("stereo enabled");
|
||||
} else {
|
||||
configs = {
|
||||
'stereo': 0,
|
||||
'useinbandfec': session.noFEC ? 0 : 1,
|
||||
'maxptime': session.maxptime,
|
||||
'minptime': session.minptime,
|
||||
'ptime': session.ptime,
|
||||
'dtx': session.dtx // "usedtx", if no loud audio, stops sending audio for 400ms. default.
|
||||
};
|
||||
}
|
||||
|
||||
if (session.whipOutAudioBitrate){
|
||||
if (!configs){
|
||||
configs = {
|
||||
'maxaveragebitrate': session.whipOutAudioBitrate * 1024,
|
||||
'cbr': session.cbr
|
||||
};
|
||||
} else{
|
||||
configs.maxaveragebitrate = session.whipOutAudioBitrate * 1024;
|
||||
configs.cbr = session.cbr;
|
||||
}
|
||||
}
|
||||
|
||||
if (configs){
|
||||
description.sdp = CodecsHandler.setOpusAttributes(description.sdp, configs);
|
||||
}
|
||||
|
||||
if (iOS || iPad){ // solves issues with iOS rotation not being correct
|
||||
if (session.removeOrientationFlag && description.sdp.includes("a=extmap:3 urn:3gpp:video-orientation\r\n")){
|
||||
description.sdp = description.sdp.replace('a=extmap:3 urn:3gpp:video-orientation\r\n', '');
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof session.whipOutCodec === "object"){
|
||||
session.whipOutCodec.reverse().forEach(codec=>{
|
||||
description.sdp = CodecsHandler.preferCodec(description.sdp, codec);
|
||||
if (session.whipOutVideoBitrate){
|
||||
description.sdp = CodecsHandler.setVideoBitrates(description.sdp , {
|
||||
min: parseInt(session.whipOutVideoBitrate/10) || 1,
|
||||
max: session.whipOutVideoBitrate || 1
|
||||
}, codec);
|
||||
}
|
||||
});
|
||||
} else if (session.whipOutCodec){
|
||||
description.sdp = CodecsHandler.preferCodec(description.sdp, session.whipOutCodec);
|
||||
if (session.whipOutVideoBitrate){
|
||||
description.sdp = CodecsHandler.setVideoBitrates(description.sdp , {
|
||||
min: parseInt(session.whipOutVideoBitrate/10) || 1,
|
||||
max: session.whipOutVideoBitrate || 1
|
||||
}, session.whipOutCodec);
|
||||
}
|
||||
} else {
|
||||
description.sdp = CodecsHandler.preferCodec(description.sdp,"h264"); // default
|
||||
description.sdp = description.sdp.replace(/42001f/gi,"42e01f"); // openh264 set as default.
|
||||
description.sdp = description.sdp.replace(/420029/gi,"42e01f");
|
||||
|
||||
if (session.whipOutVideoBitrate){
|
||||
description.sdp = CodecsHandler.setVideoBitrates(description.sdp , {
|
||||
min: parseInt(session.whipOutVideoBitrate/10) || 1,
|
||||
max: session.whipOutVideoBitrate || 1
|
||||
}, "h264");
|
||||
}
|
||||
}
|
||||
return description;
|
||||
}
|
||||
|
||||
|
||||
function whipOut(){
|
||||
log("whipOut");
|
||||
var candidates = [];
|
||||
@ -34570,15 +34737,16 @@ function whipOut(){
|
||||
warnlog("ON NEGO NEEDED");
|
||||
warnlog(event);
|
||||
try {
|
||||
session.whipOut.createOffer().then(function(offer){
|
||||
//offer.sdp = CodecsHandler.setOpusAttributes(offer.sdp, {'stereo': 1});
|
||||
offer.sdp = CodecsHandler.preferCodec(offer.sdp,"h264");
|
||||
if (!codec){
|
||||
offer.sdp = offer.sdp.replace(/42001f/gi,"42e01f");
|
||||
offer.sdp = offer.sdp.replace(/420029/gi,"42e01f");
|
||||
session.whipOut.createOffer().then(function(description){
|
||||
|
||||
try {
|
||||
description = configureWhipOutSDP(description)
|
||||
} catch(e){
|
||||
errorlog(e);
|
||||
}
|
||||
warnlog(offer);
|
||||
return session.whipOut.setLocalDescription(offer);
|
||||
|
||||
warnlog(description);
|
||||
return session.whipOut.setLocalDescription(description);
|
||||
}).then(function() {
|
||||
//log(session.whipOut.localDescription);
|
||||
var sdp = session.whipOut.localDescription.sdp;
|
||||
@ -34611,6 +34779,11 @@ function whipOut(){
|
||||
jsep.sdp = this.responseText;
|
||||
jsep.type = "answer";
|
||||
|
||||
try {
|
||||
jsep = configureWhipOutSDP(jsep)
|
||||
} catch(e){
|
||||
errorlog(e);
|
||||
}
|
||||
|
||||
warnlog("Processing answer:");
|
||||
warnlog(jsep);
|
||||
|
||||
17
main.css
17
main.css
@ -1609,7 +1609,8 @@ body.darktheme{
|
||||
height:100vh;
|
||||
}
|
||||
.mainmenuclass {
|
||||
display: inherit;
|
||||
display: inline-block;
|
||||
width:100%;
|
||||
}
|
||||
#welcomeImage{
|
||||
object-fit:cover;
|
||||
@ -2870,6 +2871,20 @@ video {
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
background-image: var(--video-background-image);
|
||||
|
||||
}
|
||||
video[data-speaking="0"] {
|
||||
transition: opacity .25s ease-in-out, background-size 0.5s ease;
|
||||
}
|
||||
video[data-speaking="1"] {
|
||||
background-image: var(--video-background-image-talking, var(--video-background-image));
|
||||
background-size: var(--video-background-image-size-talking, var(--video-background-image-size));
|
||||
transition: background-size 0.5s ease;
|
||||
}
|
||||
video[data-speaking="2"] {
|
||||
background-image: var(--video-background-image-screaming, var(--video-background-image));
|
||||
background-size: var(--video-background-image-size-screaming, var(--video-background-image-size));
|
||||
transition: background-size 0.5s ease;
|
||||
}
|
||||
|
||||
.nogb { background-image: unset !important }
|
||||
|
||||
119
main.js
119
main.js
@ -258,15 +258,72 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
|
||||
}
|
||||
|
||||
if (urlParams.has('avatarimg') || urlParams.has('bgimage') || urlParams.has('bgimg')) { // URL or data:base64 image. Becomes local to this viewer only. This is like &avatar, but slightly different. Just CSS in this case
|
||||
var avatarImg = urlParams.get('avatarimg') || urlParams.get('bgimage') || urlParams.get('bgimg') || "./media/avatar.webp";
|
||||
var avatarImg = urlParams.get('avatarimg') || urlParams.get('bgimage') || urlParams.get('bgimg') || "./media/avatar1.png";
|
||||
if (avatarImg){
|
||||
try {
|
||||
avatarImg = decodeURIComponent(avatarImg);
|
||||
} catch(e){}
|
||||
try {
|
||||
avatarImg = 'url("'+avatarImg+'")';
|
||||
document.documentElement.style.setProperty('--video-background-image', avatarImg);
|
||||
document.documentElement.style.setProperty('--video-background-image-size', "contain");
|
||||
let fallbackImage = new Image();
|
||||
fallbackImage.src = avatarImg;
|
||||
session.style = -1;
|
||||
fallbackImage.onload = function(){
|
||||
document.documentElement.style.setProperty('--video-background-image', 'url("'+avatarImg+'")');
|
||||
if (session.meterStyle!==5){
|
||||
document.documentElement.style.setProperty('--video-background-image-size', "contain");
|
||||
}
|
||||
}
|
||||
} catch(e){}
|
||||
}
|
||||
}
|
||||
if (urlParams.has('avatarimg2') || urlParams.has('bgimage2') || urlParams.has('bgimg2')) { // URL or data:base64 image. Becomes local to this viewer only. This is like &avatar, but slightly different. Just CSS in this case
|
||||
var avatarImg2 = urlParams.get('avatarimg2') || urlParams.get('bgimage2') || urlParams.get('bgimg2') || "./media/avatar2.png";
|
||||
if (avatarImg2){
|
||||
try {
|
||||
avatarImg2 = decodeURIComponent(avatarImg2);
|
||||
} catch(e){}
|
||||
try {
|
||||
let fallbackImage2 = new Image();
|
||||
fallbackImage2.src = avatarImg2;
|
||||
fallbackImage2.onload = function(){
|
||||
document.documentElement.style.setProperty('--video-background-image-talking', 'url("'+avatarImg2+'")');
|
||||
if (session.meterStyle!==5){
|
||||
document.documentElement.style.setProperty('--video-background-image-size', "contain");
|
||||
}
|
||||
}
|
||||
session.audioEffects = true;
|
||||
session.meterStyle = 4;
|
||||
session.style = -1;
|
||||
if (session.showControls===null){
|
||||
session.showControls = false;
|
||||
}
|
||||
|
||||
} catch(e){}
|
||||
}
|
||||
}
|
||||
|
||||
if (urlParams.has('avatarimg3') || urlParams.has('bgimage3') || urlParams.has('bgimg3')) { // URL or data:base64 image. Becomes local to this viewer only. This is like &avatar, but slightly different. Just CSS in this case
|
||||
var avatarImg3 = urlParams.get('avatarimg3') || urlParams.get('bgimage3') || urlParams.get('bgimg3') || "./media/avatar3.png";
|
||||
if (avatarImg3){
|
||||
try {
|
||||
avatarImg3 = decodeURIComponent(avatarImg3);
|
||||
} catch(e){}
|
||||
try {
|
||||
|
||||
let fallbackImage3 = new Image();
|
||||
fallbackImage3.src = avatarImg3;
|
||||
fallbackImage3.onload = function(){
|
||||
document.documentElement.style.setProperty('--video-background-image-screaming', 'url("'+avatarImg3+'")');
|
||||
if (session.meterStyle!==5){
|
||||
document.documentElement.style.setProperty('--video-background-image-size', "contain");
|
||||
}
|
||||
}
|
||||
session.audioEffects = true;
|
||||
session.meterStyle = 4;
|
||||
session.style = -1;
|
||||
if (session.showControls===null){
|
||||
session.showControls = false;
|
||||
}
|
||||
} catch(e){}
|
||||
}
|
||||
}
|
||||
@ -1244,7 +1301,9 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
|
||||
|
||||
if (session.scene!==false){
|
||||
session.disableWebAudio = true;
|
||||
session.audioEffects = false;
|
||||
if (session.audioEffects===null){
|
||||
session.audioEffects = false;
|
||||
}
|
||||
session.audioMeterGuest = false;
|
||||
}
|
||||
|
||||
@ -1653,6 +1712,8 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
|
||||
session.stereo = 4;
|
||||
} else if (session.stereo === "2") {
|
||||
session.stereo = 2;
|
||||
} else if (session.stereo === "6") {
|
||||
session.stereo = 6;
|
||||
} else if (session.stereo === "in") {
|
||||
session.stereo = 2;
|
||||
} else {
|
||||
@ -2217,7 +2278,9 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
|
||||
if (window.obsstudio) {
|
||||
session.disableWebAudio = true; // default true; might be useful to disable on slow or old computers?
|
||||
session.audioMeterGuest = false;
|
||||
session.audioEffects = false;
|
||||
if (session.audioEffects===null){
|
||||
session.audioEffects = false;
|
||||
}
|
||||
if (window.obsstudio.pluginVersion){
|
||||
if (macOS){ // if mac, no fix
|
||||
//session.obsfix = false;
|
||||
@ -3046,6 +3109,29 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (urlParams.has('whipoutcodec') || urlParams.has('woc')){
|
||||
session.whipOutCodec = urlParams.get('whipoutcodec') || urlParams.get('woc') || false;
|
||||
}
|
||||
if (session.whipOutCodec){
|
||||
session.whipOutCodec = session.whipOutCodec.toLowerCase();
|
||||
if (session.whipOutCodec){
|
||||
session.whipOutCodec = session.whipOutCodec.split(',');
|
||||
}
|
||||
}
|
||||
|
||||
if (urlParams.has('whipoutaudiobitrate') || urlParams.has('woab')){
|
||||
session.whipOutAudioBitrate = urlParams.get('whipoutaudiobitrate') || urlParams.get('woab') || false;
|
||||
if (session.whipOutAudioBitrate ){
|
||||
session.whipOutAudioBitrate = parseInt(session.whipOutAudioBitrate );
|
||||
}
|
||||
}
|
||||
if (urlParams.has('whipoutvideobitrate') || urlParams.has('wovb')){
|
||||
session.whipOutVideoBitrate = urlParams.get('whipoutvideobitrate') || urlParams.get('wovb') || false;
|
||||
if (session.whipOutVideoBitrate){
|
||||
session.whipOutVideoBitrate = parseInt(session.whipOutVideoBitrate);
|
||||
}
|
||||
}
|
||||
|
||||
if (urlParams.has('height') || urlParams.has('h')) {
|
||||
session.height = urlParams.get('height') || urlParams.get('h');
|
||||
@ -3519,15 +3605,23 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
|
||||
if (urlParams.has('meter') || urlParams.has('meterstyle')){ // same as also adding &style=3
|
||||
session.meterStyle = urlParams.get('meter') || urlParams.get('meterstyle') || 1;
|
||||
session.meterStyle = parseInt(session.meterStyle);
|
||||
session.style=3;
|
||||
if (session.meterStyle<4){
|
||||
session.style=3; // black canvas
|
||||
} else {
|
||||
session.style = -1; // no canvas
|
||||
}
|
||||
session.audioEffects = true;
|
||||
}
|
||||
|
||||
if (session.meterStyle==5){
|
||||
document.documentElement.style.setProperty('--video-background-image-size-talking', 'auto 35%');
|
||||
document.documentElement.style.setProperty('--video-background-image-size-screaming', 'auto 45%');
|
||||
}
|
||||
|
||||
if (urlParams.has('directorchat') || urlParams.has('dc')){
|
||||
session.directorChat = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (urlParams.has('style') || urlParams.has('st')) {
|
||||
session.style = urlParams.get('style') || urlParams.get('st');
|
||||
@ -3937,6 +4031,15 @@ async function main(){ // main asyncronous thread; mostly initializes the user s
|
||||
} else {
|
||||
getById("head1").innerHTML = '<br /><span style="color:#CCC" data-translate="please-select-which-to-share">- Please select which you wish to share</span>';
|
||||
}
|
||||
|
||||
if (!session.cleanOutput){
|
||||
try {
|
||||
if (window.obsstudio){
|
||||
getById("unexpectedPushLink").classList.remove("hidden");
|
||||
}
|
||||
} catch(e){}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BIN
media/avatar1.png
Normal file
BIN
media/avatar1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 157 KiB |
BIN
media/avatar2.png
Normal file
BIN
media/avatar2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 161 KiB |
BIN
media/avatar3.png
Normal file
BIN
media/avatar3.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 166 KiB |
208
mixer.html
208
mixer.html
@ -3,6 +3,7 @@
|
||||
<title>Mixer app</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||
<meta content="text/html;charset=utf-8" http-equiv="Content-Type" />
|
||||
<script src="./thirdparty/aes.js"></script>
|
||||
<script src="./thirdparty/jquery/jquery-3.6.0.js?asdf"></script>
|
||||
<script src="./thirdparty/jquery/jquery-ui.js"></script>
|
||||
<link rel="stylesheet" href="./thirdparty/jquery/jquery-ui.css">
|
||||
@ -14,6 +15,7 @@
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Sora:wght@200;400;700&display=swap" rel="stylesheet">
|
||||
|
||||
<style>
|
||||
:root{
|
||||
--aspect-ratio: 1.7777777777;
|
||||
@ -1015,7 +1017,10 @@
|
||||
<span >
|
||||
<input type="checkbox" id="toggleLabel" onclick="updateInviteLinks(event)" /><label>Prompt user for a display name</label><br />
|
||||
<input type="checkbox" id="toggleBroadcast" onclick="updateInviteLinks(event)"/><label>Guest can see other guests and the active layout <small>(higher CPU for guest)</small></label><br />
|
||||
<input type="checkbox" id="obfuscateInvites" onclick="updateInviteLinks(event)"/><label>Obfuscate the invite links so they cannot be easily modified by guests</label><br />
|
||||
|
||||
</span>
|
||||
<label>Append additional URL params: </label><input size='50' oninput="updateInviteLinks(event)" style='max-width:50%' type="text" id="additionalParams" placeholder='optional URL params here. eg: &showlabels&ruler' /><br />
|
||||
<i>You can manually customize the invite link further; see the documentation at <a href="https://docs.vdo.ninja" target="_blank">docs.vdo.ninja</a></i>
|
||||
<br /><br />
|
||||
<button class='close-btn'>Close</button>
|
||||
@ -1069,6 +1074,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function toggleChat(){
|
||||
document.getElementById("chatModule").classList.toggle("fadeout");
|
||||
document.getElementById("chatModuleButton").classList.toggle("hidden");
|
||||
@ -1158,7 +1164,9 @@
|
||||
var updateOnSlotChange = true;
|
||||
var assignSlotToGuest = true;
|
||||
var toggleLabel = false;
|
||||
var toggleBroadcast = true;
|
||||
var toggleBroadcast = false;
|
||||
var obfuscateInvites = false;
|
||||
var additionalParams = "";
|
||||
var messageList = [];
|
||||
var password = false;
|
||||
var syncOBS = false;
|
||||
@ -1167,9 +1175,6 @@
|
||||
|
||||
var currentOBSState = false;
|
||||
|
||||
if (urlParams.has('password') || urlParams.has('pass') || urlParams.has('pw') || urlParams.has('p')) {
|
||||
password = urlParams.get('password') || urlParams.get('pass') || urlParams.get('pw') || urlParams.get('p');
|
||||
}
|
||||
|
||||
var aspectRatio = 16/9.0;
|
||||
var pixelDensity = 720;
|
||||
@ -1205,6 +1210,14 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (urlParams.has('password') || urlParams.has('pass') || urlParams.has('pw') || urlParams.has('p')) {
|
||||
password = urlParams.get('password') || urlParams.get('pass') || urlParams.get('pw') || urlParams.get('p') || "";
|
||||
password = password.trim();
|
||||
document.getElementById("savedroompassword").classList.add("hidden");
|
||||
document.getElementById("roompassword").classList.add("hidden");
|
||||
}
|
||||
|
||||
function randomRoomName(){
|
||||
document.getElementById("roomname").value = generateString(8);
|
||||
}
|
||||
@ -1215,9 +1228,12 @@
|
||||
startRoom();
|
||||
}
|
||||
function startRoom(){
|
||||
var pid = document.getElementById("roompassword").value.trim();
|
||||
if (pid){
|
||||
password = pid;
|
||||
|
||||
if (password===false){
|
||||
var pid = document.getElementById("roompassword").value.trim();
|
||||
if (pid){
|
||||
password = pid;
|
||||
}
|
||||
}
|
||||
|
||||
var rid = document.getElementById("roomname").value.trim();
|
||||
@ -1258,6 +1274,27 @@
|
||||
sendChatMessage();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function generateHash(str, length=false){
|
||||
var buffer = new TextEncoder("utf-8").encode(str);
|
||||
return crypto.subtle.digest("SHA-256", buffer).then(
|
||||
function (hash) {
|
||||
hash = new Uint8Array(hash);
|
||||
if (length){
|
||||
hash = hash.slice(0, parseInt(parseInt(length)/2));
|
||||
}
|
||||
hash = toHexString(hash);
|
||||
return hash;
|
||||
}
|
||||
);
|
||||
};
|
||||
function toHexString(byteArray){
|
||||
return Array.prototype.map.call(byteArray, function(byte){
|
||||
return ('0' + (byte & 0xFF).toString(16)).slice(-2);
|
||||
}).join('');
|
||||
}
|
||||
|
||||
|
||||
function sendChatMessage(){ // filtered + visual
|
||||
var msg = document.getElementById('chatInput').value;
|
||||
@ -1750,6 +1787,29 @@
|
||||
getById("toggleBroadcast").checked = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (savedSession.settings && ("obfuscateInvites" in savedSession.settings)){
|
||||
obfuscateInvites = savedSession.settings.obfuscateInvites;
|
||||
if (!obfuscateInvites){
|
||||
getById("obfuscateInvites").value = "off";
|
||||
getById("obfuscateInvites").checked = false;
|
||||
getById("obfuscateInvites").removeAttribute('checked');
|
||||
} else {
|
||||
getById("obfuscateInvites").value = "on";
|
||||
getById("obfuscateInvites").checked = true;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
if (savedSession.settings && ("additionalParams" in savedSession.settings)){
|
||||
additionalParams = savedSession.settings.additionalParams;
|
||||
if (!additionalParams){
|
||||
getById("additionalParams").value = "";
|
||||
} else {
|
||||
getById("additionalParams").value = additionalParams;
|
||||
}
|
||||
}
|
||||
|
||||
if (savedSession.settings && ("syncOBS" in savedSession.settings)){
|
||||
syncOBS = savedSession.settings.syncOBS;
|
||||
if (!syncOBS){
|
||||
@ -2386,10 +2446,12 @@
|
||||
|
||||
var a = document.createElement("a");
|
||||
a.innerHTML = "Invite Guest Link 📎";
|
||||
a.href = "./?room="+roomname+"&broadcast"+additional;
|
||||
//a.href = "./?room="+roomname+"&broadcast"+additional;
|
||||
a.target = "_blank";
|
||||
a.id = "mainInviteLink";
|
||||
a.onclick = function(evt){copyFunction(this, evt);};
|
||||
document.getElementById("sources").appendChild(a);
|
||||
updateInviteLinks();
|
||||
|
||||
var a = document.createElement("a");
|
||||
a.innerHTML = "Scene View Link 📎";
|
||||
@ -2659,13 +2721,15 @@
|
||||
document.getElementById("inviteOptions").classList.remove("hidden");
|
||||
updateInviteLinks();
|
||||
}
|
||||
var toggleBroadcast = false;
|
||||
|
||||
function updateInviteLinks(event=false){
|
||||
async function updateInviteLinks(event=false){
|
||||
var additional = "";
|
||||
|
||||
if (password){
|
||||
additional += "&password="+password;
|
||||
var hash = password.trim();
|
||||
hash = encodeURIComponent(hash);
|
||||
hash = await generateHash(hash + location.hostname, 4);
|
||||
additional += "&hash="+hash;
|
||||
}
|
||||
|
||||
if (document.getElementById("toggleLabel").checked){
|
||||
@ -2675,23 +2739,50 @@
|
||||
toggleLabel= false;
|
||||
}
|
||||
|
||||
if (!document.getElementById("toggleBroadcast").checked){
|
||||
additional += "&broadcast";
|
||||
if (document.getElementById("toggleBroadcast").checked){
|
||||
additional += "&layout"; // do not use &broadcast with &layout, else you will get broken results.
|
||||
toggleBroadcast = true;
|
||||
} else {
|
||||
additional += "&layout"; // do not use &broadcast with &layout, else you will get broken results.
|
||||
additional += "&broadcast";
|
||||
toggleBroadcast = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
document.querySelectorAll(".roomname").forEach(ele=>{
|
||||
ele.innerText = roomname;
|
||||
});
|
||||
|
||||
var inviteURL = "https://"+location.hostname+"/?room="+roomname+additional;
|
||||
|
||||
if (document.getElementById("additionalParams").value.trim().length){
|
||||
additionalParams = document.getElementById("additionalParams").value.trim();
|
||||
if (!additionalParams.startsWith("&")){
|
||||
additionalParams = "&"+additionalParams;
|
||||
}
|
||||
inviteURL += additionalParams;
|
||||
} else {
|
||||
additionalParams = "";
|
||||
}
|
||||
|
||||
|
||||
if (document.getElementById("obfuscateInvites").checked){
|
||||
obfuscateInvites = true;
|
||||
inviteURL = processInvite(inviteURL);
|
||||
} else {
|
||||
obfuscateInvites = false;
|
||||
}
|
||||
|
||||
|
||||
document.getElementById("mainInviteLink").href = inviteURL
|
||||
|
||||
document.querySelectorAll(".inviteLink").forEach(ele=>{
|
||||
if (ele.tagName == "A"){
|
||||
ele.href = "./?room="+roomname+additional;
|
||||
ele.href = inviteURL
|
||||
} else if (document.getElementById("obfuscateInvites").checked){
|
||||
ele.innerHTML = inviteURL;
|
||||
} else if (ele.tagName == "I"){
|
||||
ele.innerHTML = "URL + ?room="+roomname+additional;
|
||||
ele.innerHTML = "URL + ?room="+roomname+additional+additionalParams;
|
||||
}
|
||||
ele.onclick = function(evt){copyFunction(this, evt);};
|
||||
});
|
||||
@ -2789,6 +2880,89 @@
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function processInvite(input){
|
||||
|
||||
if (input.startsWith("https://obs.ninja/")){
|
||||
input = input.replace('https://vdo.ninja/', '');
|
||||
} else if (input.startsWith("http://obs.ninja/")){
|
||||
input = input.replace('http://vdo.ninja/', '');
|
||||
} else if (input.startsWith("obs.ninja/")){
|
||||
input = input.replace('vdo.ninja/', '');
|
||||
} else if (input.startsWith("https://vdo.ninja/")){
|
||||
input = input.replace('https://vdo.ninja/', 'vdo.ninja/');
|
||||
} else if (input.startsWith("http://vdo.ninja/")){
|
||||
input = input.replace('http://vdo.ninja/', 'vdo.ninja/');
|
||||
}
|
||||
|
||||
input = input.replace('&view=', '&v=');
|
||||
input = input.replace('&view&', '&v&');
|
||||
input = input.replace('?view&', '?v&');
|
||||
input = input.replace('?view=', '?v=');
|
||||
|
||||
input = input.replace('&videobitrate=', '&vb=');
|
||||
input = input.replace('?videobitrate=', '?vb=');
|
||||
input = input.replace('&bitrate=', '&vb=');
|
||||
input = input.replace('?bitrate=', '?vb=');
|
||||
|
||||
input = input.replace('?audiodevice=', '?ad=');
|
||||
input = input.replace('&audiodevice=', '&ad=');
|
||||
|
||||
input = input.replace('?label=', '?l=');
|
||||
input = input.replace('&label=', '&l=');
|
||||
|
||||
input = input.replace('?stereo=', '?s=');
|
||||
input = input.replace('&stereo=', '&s=');
|
||||
input = input.replace('&stereo&', '&s&');
|
||||
input = input.replace('?stereo&', '?s&');
|
||||
|
||||
input = input.replace('?webcam&', '?wc&');
|
||||
input = input.replace('&webcam&', '&wc&');
|
||||
|
||||
input = input.replace('?remote=', '?rm=');
|
||||
input = input.replace('&remote=', '&rm=');
|
||||
|
||||
input = input.replace('?password=', '?p=');
|
||||
input = input.replace('&password=', '&p=');
|
||||
|
||||
input = input.replace('&maxvideobitrate=', '&mvb=');
|
||||
input = input.replace('?maxvideobitrate=', '?mvb=');
|
||||
|
||||
input = input.replace('&maxbitrate=', '&mvb=');
|
||||
input = input.replace('?maxbitrate=', '?mvb=');
|
||||
|
||||
input = input.replace('&height=', '&h=');
|
||||
input = input.replace('?height=', '?h=');
|
||||
|
||||
input = input.replace('&width=', '&w=');
|
||||
input = input.replace('?width=', '?w=');
|
||||
|
||||
input = input.replace('&quality=', '&q=');
|
||||
input = input.replace('?quality=', '?q=');
|
||||
|
||||
input = input.replace('&cleanoutput=', '&clean=');
|
||||
input = input.replace('?cleanoutput=', '?clean=');
|
||||
|
||||
input = input.replace('&maxviewers=', '&clean=');
|
||||
input = input.replace('?maxviewers=', '?clean=');
|
||||
|
||||
input = input.replace('&framerate=', '&fr=');
|
||||
input = input.replace('?framerate=', '?fr=');
|
||||
|
||||
input = input.replace('&fps=', '&fr=');
|
||||
input = input.replace('?fps=', '?fr=');
|
||||
|
||||
input = input.replace('&permaid=', '&push=');
|
||||
input = input.replace('?permaid=', '?push=');
|
||||
|
||||
input = input.replace('&roomid=', '&r=');
|
||||
input = input.replace('?roomid=', '?r=');
|
||||
|
||||
input = input.replace('&room=', '&r=');
|
||||
input = input.replace('?room=', '?r=');
|
||||
|
||||
return "https://invite.cam/"+CryptoJS.AES.encrypt(input, atob('T0JTTklOSkFGT1JMSUZF')).toString();
|
||||
}
|
||||
|
||||
function drawLayout(layoutOriginal, sceneName=false, obsSceneName = ""){
|
||||
|
||||
@ -3815,6 +3989,8 @@
|
||||
savedSession.settings.assignSlotToGuest = assignSlotToGuest;
|
||||
savedSession.settings.toggleLabel = toggleLabel;
|
||||
savedSession.settings.toggleBroadcast = toggleBroadcast;
|
||||
savedSession.settings.obfuscateInvites = obfuscateInvites;
|
||||
savedSession.settings.additionalParams = additionalParams;
|
||||
savedSession.settings.syncOBS = syncOBS;
|
||||
savedSession.settings.remoteSyncOBS = remoteSyncOBS;
|
||||
savedSession.settings.aspectRatio = aspectRatio;
|
||||
|
||||
@ -273,7 +273,8 @@
|
||||
var iframeContainer = document.createElement("span");
|
||||
|
||||
iframe.allow = "autoplay";
|
||||
var srcString = "./?view=" + streamID + "&cleanoutput&privacy&noaudio&scale=0&speedtest="+zone; // No TURN servers set on the reciever. Don't want to query for TURN servers needlessly.
|
||||
// I've removed &privacy from the view link, and left it just on the push link. This hopefully solves compatibility issues
|
||||
var srcString = "./?view=" + streamID + "&cleanoutput&noaudio&scale=0&speedtest="+zone; // No TURN servers set on the reciever. Don't want to query for TURN servers needlessly.
|
||||
|
||||
if (urlParams.has("turn")) {
|
||||
srcString = srcString + "&turn=" + urlParams.get("turn");
|
||||
|
||||
101
whip.html
101
whip.html
@ -79,7 +79,7 @@
|
||||
width:100%;
|
||||
background-color: #101520;
|
||||
}
|
||||
input.changeText {
|
||||
.changeText {
|
||||
font-size: 1em;
|
||||
align-self: center;
|
||||
width: 100%;
|
||||
@ -96,9 +96,12 @@
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
|
||||
input.changeText:focus {
|
||||
.changeText:focus {
|
||||
outline: none;
|
||||
}
|
||||
select.changetext{
|
||||
padding: .1em;
|
||||
}
|
||||
|
||||
.container{
|
||||
font-size: min(14px, 2vh);
|
||||
@ -300,6 +303,14 @@
|
||||
h3 {
|
||||
color: #b0e3ff;
|
||||
}
|
||||
.hidden{
|
||||
display:none;
|
||||
opacity:0;
|
||||
visibility:none;
|
||||
width:0;
|
||||
height:0
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
</head>
|
||||
@ -318,10 +329,57 @@
|
||||
<input type="text" id="changeText1" class="inputfield changeText" placeholder="WHIP Publishing URL" />
|
||||
<button onclick="gohere1();" class="gobutton" id="gobutton1">GO</button>
|
||||
</div>
|
||||
<div class="inputCombo" style="width:350px;margin:10 0 10 auto;">
|
||||
<input type="password" id="changeText1a" class="inputfield changeText" placeholder="Authentication Bearer Token (optional)" />
|
||||
<div class="details">⚙️</div>
|
||||
</div>
|
||||
<div >
|
||||
<div class="inputCombo" style="margin: 10px 0px 10px 10px;">
|
||||
<input type="password" id="changeText1a" class="inputfield changeText" placeholder="🗝️ Authentication Bearer Token (optional)" />
|
||||
<div class="details">⚙️</div>
|
||||
</div>
|
||||
|
||||
<div class="inputCombo" id="advanced" style="margin: 10px 0px 10px 10px;">
|
||||
<select style="border-radius:10px;margin-right:5px;width:unset!important;" class="changeText" id="audioBitrateGroupFlag" title="Which audio bitrate target would you prefer?" >
|
||||
<option value="0" selected>🎙️Default Audio Bitrate</option>
|
||||
<option value="500">🎙️32-kbps</option>
|
||||
<option value="2500">🎙️64-kbps</option>
|
||||
<option value="6000">🎙️128-kbps</option>
|
||||
<option value="20000">🎙️256-kbps</option>
|
||||
</select >
|
||||
<select style="border-radius:10px;margin-right:5px;width:unset!important;" class="changeText" id="audioBitrateGroupFlag" title="Which audio bitrate target would you prefer?" >
|
||||
<option value="cbr" selected>🎙️CBR</option>
|
||||
<option value="vbr">🎙️VBR</option>
|
||||
</select >
|
||||
<select style="border-radius:10px;margin-right:5px;width:unset!important;" class="changeText" id="audioBitrateGroupFlag" title="Which audio bitrate target would you prefer?" >
|
||||
<option value="0" selected>🎙️Denoise</option>
|
||||
<option value="500">🎙️No Denoise</option>
|
||||
</select >
|
||||
<select style="border-radius:10px;margin-right:5px;width:unset!important;" class="changeText" id="audioBitrateGroupFlag" title="Which audio bitrate target would you prefer?" >
|
||||
<option value="0" selected>🎙️Auto Gain</option>
|
||||
<option value="500">🎙️No Auto Gain</option>
|
||||
</select >
|
||||
<select style="border-radius:10px;margin-right:5px;width:unset!important;" class="changeText" id="audioBitrateGroupFlag" title="Which audio bitrate target would you prefer?" >
|
||||
<option value="0" selected>🎙️Stereo</option>
|
||||
<option value="500">🎙️Mono</option>
|
||||
</select >
|
||||
</div>
|
||||
<div class="inputCombo" id="advanced2" style="margin: 10px 0px 10px 10px;">
|
||||
<select style="border-radius:10px;margin-right:5px;width:unset!important;" class="changeText" id="bitrateGroupFlag" title="Which video bitrate target would you prefer?" >
|
||||
<option value="0" selected>🎦Default Video Bitrate</option>
|
||||
<option value="500">🎦500-kbps</option>
|
||||
<option value="2500">🎦2500-kbps</option>
|
||||
<option value="6000">🎦6000-kbps</option>
|
||||
<option value="20000">🎦20000-kbps</option>
|
||||
</select >
|
||||
<select style="border-radius:10px;margin-right:5px;width:unset!important;" class="changeText" id="codecGroupFlag" title="Which video codec would you prefer to be used if available?" >
|
||||
<option value="default" selected>🎦OpenH264</option>
|
||||
<option value="vp9">🎦VP9</option>
|
||||
<option value="h264">🎦H264</option>
|
||||
<option value="vp8">🎦VP8</option>
|
||||
<option value="av1">🎦AV1</option>
|
||||
</select >
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="urlInput1" class="urlInput" title="Put the link you want to load here">
|
||||
@ -395,22 +453,35 @@ document.querySelector("#changeText2").value = localStorage.getItem('changeText2
|
||||
document.querySelector("#changeText3").value = localStorage.getItem('changeText3') || "";
|
||||
|
||||
function gohere1(){
|
||||
if (document.getElementById('changeText1').value && document.getElementById('changeText1a').value){
|
||||
if (document.getElementById('changeText1').value){
|
||||
localStorage.setItem('changeText1', document.getElementById('changeText1').value);
|
||||
localStorage.setItem('changeText1a', document.getElementById('changeText1a').value);
|
||||
window.location = domain + "?push&whippush=" + encodeURIComponent(document.getElementById('changeText1').value) + "&whippushtoken=" + document.getElementById('changeText1a').value;
|
||||
} else if (document.getElementById('changeText1').value){
|
||||
localStorage.setItem('changeText1', document.getElementById('changeText1').value);
|
||||
localStorage.setItem('changeText1a', "");
|
||||
window.location = domain + "?push&whippush=" + encodeURIComponent(document.getElementById('changeText1').value);
|
||||
localStorage.setItem('changeText1a', document.getElementById('changeText1a').value || "");
|
||||
|
||||
|
||||
localStorage.setItem('bitrateGroupFlag', document.getElementById('bitrateGroupFlag').value);
|
||||
localStorage.setItem('codecGroupFlag', document.getElementById('codecGroupFlag').value);
|
||||
|
||||
var bitrate = "";
|
||||
if (parseInt(document.getElementById('bitrateGroupFlag').value)){
|
||||
bitrate = "&whipoutvideobitrate="+document.getElementById('bitrateGroupFlag').value;
|
||||
}
|
||||
var codec = "";
|
||||
if (document.getElementById('codecGroupFlag').value!=="default"){
|
||||
codec = "&whipoutcodec="+document.getElementById('codecGroupFlag').value;
|
||||
}
|
||||
|
||||
if (document.getElementById('changeText1a').value){
|
||||
window.location = domain + "?push&whippush=" + encodeURIComponent(document.getElementById('changeText1').value) + "&whippushtoken=" + document.getElementById('changeText1a').value + codec + bitrate;
|
||||
} else {
|
||||
window.location = domain + "?push&whippush=" + encodeURIComponent(document.getElementById('changeText1').value) + codec + bitrate;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function gohere1t(){
|
||||
if (document.getElementById('changeText1t').value){
|
||||
localStorage.setItem('changeText1t', document.getElementById('changeText1t').value);
|
||||
window.location = domain + "?push&whippush=https%3A%2F%2Fg.webrtc.live-video.net%3A4443%2Fv2%2Foffer&whippushtoken="+ document.getElementById('changeText1t').value;
|
||||
window.location = domain + "?whipoutvideobitrate=5800&stereo&push&whippush=https%3A%2F%2Fg.webrtc.live-video.net%3A4443%2Fv2%2Foffer&whippushtoken="+ document.getElementById('changeText1t').value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user