2021-09-07 01:22:01 -04:00

401 lines
12 KiB
HTML

<html>
<head>
<script type="text/javascript" src="./thirdparty/obs-websocket.min.js"></script>
<link rel="stylesheet" href="https://vdo.ninja/main.css" />
<title>OBS Controller Demo using VDO.Ninja</title>
<style>
.container {
max-width: 80%;
width: fit-content;
margin: 0 auto;
}
h1 {
margin-top: 1em;
margin-bottom: 1em;
padding: 10px;
}
.card {
margin: 10px;
box-shadow: 0 4px 8px 0 rgb(0 0 0 / 10%);
background-color: #ddd;
color: black;
margin-bottom: 1.5em;
}
.card>div {
padding: 10px;
}
.card h2 {
font-size: 1.5em;
padding: 10px;
background-color: #457b9d;
color: white;
border-bottom: 2px solid #3b6a87;
}
small {
font-style: italic;
display: block;
margin-left: 1em;
}
span.warning {
color: rgb(212, 191, 0);
}
input {
padding: 10px;
display: inline-block;
flex-flow: unset;
margin: 10px;
}
video {
max-width: 640px;
max-height: 360px;
padding: 20px;
}
audio {
max-width: 640px;
max-height: 360px;
padding: 20px;
}
div#processing {
display: none;
justify-content: center;
place-items: center;
position: absolute;
inset: 0;
font-size: 1.5em;
font-weight: bold;
background: #141926;
flex-direction: column;
}
button {
margin:5px;
border:solid black 2px;
}
body {
color:white;
display: inline-block;
flex-flow: unset;
}
a {
color: #CEF!important;
}
#info{
margin: 20px;
max-height: 50%;
overflow: auto;
}
#client {
margin:10px;
display:block;
}
label {
color:white;
}
</style>
</head>
<body>
<div class="container">
<h1>OBS remote (server)</h1>
<span id='setup'>
<label for="address">Websocket Address</label>
<input name="address" id="address" placeholder="address (optional)" value="localhost:4444" />
<label for="address">Websocket Password</label>
<input name="password" id="password" placeholder="password here (optional)" />
<br />
<label for="vdoroomname">Room name to use</label>
<input name="vdoroomname" id="vdoroomname" placeholder="vdo room name to use (optional)" />
<label for="vdopassword">Room password to use</label>
<input name="vdopassword" id="vdopassword" placeholder="vdo password to use (optional)" />
<br />
<button id="address_button">Connect</button>
<button id="address_button_2">Connect and share OBS Output</button>
</span>
<a id="client" target="_blank"></a>
<div id="info"></div>
<small>Code available on GitHub: <a target="_blank" href='https://github.com/steveseguin/remote_ninja'>https://github.com/steveseguin/remote_ninja</a></small>
</div>
<script>
var hostname = "vdo.ninja"; // all that's supported as of this moment.
const obs = new OBSWebSocket();
var scenesData = {};
scenesData.scenes = [];
function sendToOBS(action, data={}){
document.getElementById("info").innerHTML = action + "<br />"+document.getElementById("info").innerHTML;
obs.sendCallback(action, data, sendCallback)
}
(function(w) {
w.URLSearchParams = w.URLSearchParams || function(searchString) {
var self = this;
searchString = searchString.replace("??", "?");
self.searchString = searchString;
self.get = function(name) {
var results = new RegExp('[\?&]' + name + '=([^&#]*)').exec(self.searchString);
if (results == null) {
return null;
} else {
return decodeURI(results[1]) || 0;
}
};
};
})(window);
var urlEdited = window.location.search.replace(/\?\?/g, "?");
urlEdited = urlEdited.replace(/\?/g, "&");
urlEdited = urlEdited.replace(/\&/, "?");
if (urlEdited !== window.location.search){
warnlog(window.location.search + " changed to " + urlEdited);
window.history.pushState({path: urlEdited.toString()}, '', urlEdited.toString());
}
var urlParams = new URLSearchParams(urlEdited);
var roomname = Math.floor(Math.random() * 1000000);
var pwurl = Math.floor(Math.random() * 1000000);
if (urlParams.get("password")){
pwurl = urlParams.get("password");
localStorage.setItem('password',pwurl)
} else if (localStorage.getItem('password')){
pwurl = localStorage.getItem('password');
} else {
localStorage.setItem('password',pwurl)
}
if (urlParams.get("room")){
roomname = urlParams.get("room")
localStorage.setItem('roomname',roomname)
} else if (localStorage.getItem('roomname')){
roomname = localStorage.getItem('roomname');
} else {
localStorage.setItem('roomname',roomname)
}
document.getElementById('vdoroomname').value = roomname;
document.getElementById('vdopassword').value = pwurl;
if (localStorage.getItem('address')){
document.getElementById('address').value = localStorage.getItem('address');
}
if (localStorage.getItem('wspass')){
document.getElementById('password').value = localStorage.getItem('wspass');
}
var iframe = null;
function createIFrame(visible=true){
iframe = document.createElement("iframe");
if (visible){
iframe.src = "https://"+hostname+"/?room="+roomname+"&push=mainOBSOutput&od=0&transparent&webcam&vd=obs&view&password="+pwurl+"&label=OBS_"+Math.floor(Math.random() * 1000000);
iframe.style.minWidth = "720px";
iframe.style.minHeight = "480px";
iframe.style.maxWidth = "50%";
iframe.style.maxHeight = "50%";
iframe.style.display = "block";
iframe.style.margin = "auto";
iframe.allow = "autoplay;camera;microphone;fullscreen;picture-in-picture;";
} else {
iframe.src = "https://"+hostname+"/?room="+roomname+"&push&autostart&vd=0&view&ad=0&transparent&cleanoutput&password="+pwurl+"&label=OBS_"+Math.floor(Math.random() * 1000000);
iframe.style.opacity = 0;
iframe.style.width = 0;
iframe.style.height = 0;
iframe.style.position = "absolulte";
iframe.style.top = "-100px";
iframe.style.left = "-100px";
}
document.getElementById("client").parentNode.insertBefore(iframe, document.getElementById("client").nextSibling);
var eventMethod = window.addEventListener ? "addEventListener" : "attachEvent";
var eventer = window[eventMethod];
var messageEvent = eventMethod === "attachEvent" ? "onmessage" : "message";
eventer(messageEvent, function (e) {
if (e.source != iframe.contentWindow){return} // reject messages send from other iframes
console.log(e);
if ("dataReceived" in e.data){
if ("sendToOBS" in e.data.dataReceived){
if ("action" in e.data.dataReceived.sendToOBS){
if ("data" in e.data.dataReceived.sendToOBS){
sendToOBS(e.data.dataReceived.sendToOBS.action, e.data.dataReceived.sendToOBS.data);
}
}
}
} else if ("action" in e.data){
if (e.data.action === "new-push-connection"){
console.log(e.data);
updateSceneList();
}
}
});
}
function sendCallback(err, data){
console.log("CALLBACK TRIGGERED");
var msg = {};
msg.sentFromOBS = {};
msg.sentFromOBS.callbackData = data;
msg.sentFromOBS.callbackError = err;
try {
iframe.contentWindow.postMessage({"sendData":msg}, '*');
} catch(e){}
}
function sendRawData(data){
var msg = {};
msg.sentFromOBS = {};
msg.sentFromOBS.rawData = data;
try {
iframe.contentWindow.postMessage({"sendData":msg}, '*');
} catch(e){}
}
function heartBeat(){
obs.send("GetStats");
}
function updateSceneList(){
var msg = {};
msg.sentFromOBS = {};
msg.sentFromOBS.scenes = scenesData;
try {
iframe.contentWindow.postMessage({"sendData":msg}, '*');
} catch(e){}
console.log(msg);
obs.send("GetSourcesList");
obs.send('GetCurrentScene');
obs.send("GetVideoInfo");
obs.send("ListOutputs");
}
document.getElementById('address_button').addEventListener('click', e => {
connect(e, false);
});
document.getElementById('address_button_2').addEventListener('click', e => {
connect(e, true);
});
var heartBeatInterval = null;
function connect(e, camera){
const address = document.getElementById('address').value || "localhost:4444";
const password = document.getElementById('password').value;
roomname = document.getElementById('vdoroomname').value || Math.floor(Math.random() * 1000000);
pwurl = document.getElementById('vdopassword').value || Math.floor(Math.random() * 1000000);
localStorage.setItem('roomname',roomname)
localStorage.setItem('password',pwurl)
createIFrame(camera); // connects to VDO.Ninja's IFRAME API
localStorage.setItem('address',address);
if (password){
localStorage.setItem('wspass',password);
var ret = obs.connect({
address: address,
password: password
});
} else {
var ret = obs.connect({
address: address
});
}
ret.then(() => {
console.log(`Success!`);
return obs.send('GetSceneList');
}).then(data => {
document.getElementById("setup").style.display = "none";
scenesData = data;
updateSceneList();
var pathname = window.location.pathname.split("/");
pathname.pop();
pathname = pathname.join("/");
var clientLink = window.location.protocol + "//" + window.location.host + pathname + "/interface.html?room="+roomname+"&password="+pwurl;
document.getElementById("client").href = clientLink;
document.getElementById("client").innerHTML = "<b><font style='color:#70c4ff;'>client link:</font></b> "+clientLink;
document.getElementById("info").innerHTML = "<br /><p style='color:#bdffbd;'>Connection to OBS websockets opened.</p>" + document.getElementById("info").innerHTML;
try {
obs._socket.onmessage2 = obs._socket.onmessage; // hijacking the obs-websocket.js framework
obs._socket.onmessage = function(data){
console.log(data);
obs._socket.onmessage2(data);
if (data.type && data.data){
if (data.type == "message"){
sendRawData(data.data);
}
}
}
} catch(e){console.error(e);}
clearInterval(heartBeatInterval);
heartBeatInterval = setInterval(function(){heartBeat();},3000);
}).catch(err => { // Promise convention dicates you have a catch on every chain.
console.log(err);
document.getElementById("info").innerHTML = "<br />Error trying to connect. Is SSL enabled on your domain?" + document.getElementById("info").innerHTML;
if ("error" in err){
document.getElementById("info").innerHTML = "<br />"+err.error + document.getElementById("info").innerHTML;
}
});
};
// We use the source visibility one and filter visibility web socket commands quite often in shows.
obs.on('SwitchScenes', data => {
console.log(`New Active Scene: ${data.sceneName}`);
scenesData.currentScene = data.sceneName
updateSceneList();
});
obs.on('ConnectionOpened', (data) => function(){
document.getElementById("setup").style.display = "none";
document.getElementById("info").innerHTML = "<br /><p style='color:#bdffbd;'>Connection to OBS websockets opened.</p>" + document.getElementById("info").innerHTML;
});
obs.on('ConnectionClosed', (data) => function(){
document.getElementById("setup").style.display = "unset";
document.getElementById("info").innerHTML = "<br />Connection to OBS websockets closed" + document.getElementById("info").innerHTML;
});
obs.on('AuthenticationSuccess', (data) => function(){
document.getElementById("setup").style.display = "none";
document.getElementById("info").innerHTML = "<br />OBS websockets authenticated" + document.getElementById("info").innerHTML;
});
obs.on('AuthenticationFailure', (data) => function(){
document.getElementById("setup").style.display = "unset";
document.getElementById("info").innerHTML = "<br />Authentication to OBS websockets failed" + document.getElementById("info").innerHTML;
});
obs.on('ScenesChanged', (data) => function(){
scenesData = data;
updateSceneList()
});
// You must add this handler to avoid uncaught exceptions.
obs.on('error', err => {
document.getElementById("info").innerHTML = "<br />OBS websockets error" + document.getElementById("info").innerHTML;
console.error('socket error:', err);
if ("error" in err){
document.getElementById("info").innerHTML = "<br />"+err.error + document.getElementById("info").innerHTML;
}
});
</script>
</body>
</html>