520 lines
23 KiB
HTML
520 lines
23 KiB
HTML
<html lang="en">
|
|
|
|
<head>
|
|
|
|
<link rel="apple-touch-icon" sizes="180x180" href="/static/favicons/apple-touch-icon.png">
|
|
<link rel="icon" type="image/png" sizes="32x32" href="/static/favicons/favicon-32x32.png">
|
|
<link rel="icon" type="image/png" sizes="16x16" href="/static/favicons/favicon-16x16.png">
|
|
<link rel="manifest" href="/static/favicons/site.webmanifest">
|
|
<link rel="mask-icon" href="/static/favicons/safari-pinned-tab.svg" color="#5bbad5">
|
|
<meta name="msapplication-TileColor" content="#b91d47">
|
|
<meta name="theme-color" content="#ffffff">
|
|
|
|
<title>VEL Connect</title>
|
|
|
|
<link rel="stylesheet" type="text/css" href="/static/css/spectre.min.css">
|
|
<link rel="stylesheet" type="text/css" href="/static/css/spectre-exp.min.css">
|
|
<link rel="stylesheet" type="text/css" href="/static/css/spectre-icons.min.css">
|
|
<link rel="stylesheet" type="text/css" href="/static/css/coloris.min.css">
|
|
<script src="/static/js/util.js"></script>
|
|
<style>
|
|
.container {
|
|
max-width: 30em;
|
|
}
|
|
|
|
.card {
|
|
margin: 1em;
|
|
box-shadow: 0 0 2em #0003;
|
|
}
|
|
|
|
input.btn {
|
|
cursor: auto;
|
|
user-select: auto;
|
|
}
|
|
|
|
.centered {
|
|
margin: auto;
|
|
}
|
|
|
|
hr {
|
|
color: #0004;
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
<body>
|
|
<div class="container">
|
|
|
|
<div id="loading"><br><br>
|
|
<div class="loading loading-lg"></div>
|
|
</div>
|
|
<div id="failure" style="display: none;"><br><br><br>☹️</div>
|
|
|
|
|
|
<div id="headset_details" style="display: none;">
|
|
<div class="panel card">
|
|
<div class="card-image">
|
|
<img id="cover_image" class="img-responsive" src="/static/img/cover_default.png">
|
|
</div>
|
|
<div class="panel-header text-center">
|
|
<figure class="avatar avatar-lg" style="background: none;"><img
|
|
src="/static/img/velconnect_logo_1_square.webp" alt="Avatar"></figure>
|
|
<div class="panel-title h5 mt-10">Headset ID:</div>
|
|
<code class="panel-subtitle hw_id">---</code>
|
|
<br>
|
|
<br>
|
|
<div class="container">
|
|
<div class="columns">
|
|
<div class="col-6">
|
|
<div class="panel-title h5 mt-10">Pairing Code:</div>
|
|
<code class="panel-subtitle pairing_code">---</code>
|
|
</div>
|
|
<div class="col-6">
|
|
<a href="/pair">
|
|
<button class="btn btn-primary btn-lg tooltip tooltip-right" id="pair_new"
|
|
data-tooltip="Clear this headset and pair a new headset">
|
|
Pair New
|
|
</button>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<hr>
|
|
</div>
|
|
|
|
|
|
<div class="panel-body">
|
|
<div class="container">
|
|
<div class="columns">
|
|
<div class="tile tile-centered col-6">
|
|
<div class="tile-content">
|
|
<div class="tile-title text-bold">First Seen</div>
|
|
<div class="tile-subtitle date_created">---</div>
|
|
</div>
|
|
</div>
|
|
<div class="tile tile-centered col-6">
|
|
<div class="tile-content">
|
|
<div class="tile-title text-bold">Last Modified</div>
|
|
<div class="tile-subtitle last_modified">---</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<br>
|
|
|
|
<div class="divider text-center" data-content="User Settings"></div>
|
|
|
|
<div class="tile tile-centered">
|
|
<div class="tile-content">
|
|
<div class="tile-title text-bold">User Name</div>
|
|
<input class="btn user_name" type="text" id="user_name" placeholder="----">
|
|
<button class="btn btn-primary btn-lg tooltip tooltip-right" id="set_user_name"
|
|
data-tooltip="Set Username">Set
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<!-- <br>-->
|
|
<!-- <div class="tile tile-centered">-->
|
|
<!-- <div class="tile-content">-->
|
|
<!-- <div class="tile-title text-bold">TV URL</div>-->
|
|
<!-- <input class="btn tv_url" type="text" id="tv_url" placeholder="----">-->
|
|
<!-- <button class="btn btn-primary btn-lg tooltip tooltip-right" id="set_tv_url"-->
|
|
<!-- data-tooltip="">Set</button>-->
|
|
<!-- </div>-->
|
|
<!-- <div class="tile-action">-->
|
|
<!-- </div>-->
|
|
<!-- </div>-->
|
|
<!-- <br>-->
|
|
<!-- <div class="tile tile-centered">-->
|
|
<!-- <div class="tile-content">-->
|
|
<!-- <div class="tile-title text-bold">User Color</div>-->
|
|
<!-- <input class="btn user_color coloris" type="text" id="user_color" placeholder="#ffffff">-->
|
|
<!-- <button class="btn btn-primary btn-lg tooltip tooltip-right" id="set_user_color"-->
|
|
<!-- data-tooltip="Set User Color">Set</button>-->
|
|
<!-- </div>-->
|
|
<!-- <div class="tile-action">-->
|
|
<!-- </div>-->
|
|
<!-- </div>-->
|
|
|
|
|
|
<br>
|
|
<div class="tile tile-centered">
|
|
<div class="tile-content">
|
|
<div class="tile-title text-bold">Avatar URL</div>
|
|
<div class="tile-subtitle">
|
|
<a href="https://convrged.readyplayer.me" target="blank">
|
|
Create New Avatar
|
|
<svg style="width:1em;height:1em" viewBox="0 0 24 24">
|
|
<path fill="currentColor"
|
|
d="M14,3V5H17.59L7.76,14.83L9.17,16.24L19,6.41V10H21V3M19,19H5V5H12V3H5C3.89,3 3,3.9 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V12H19V19Z"/>
|
|
</svg>
|
|
</a>
|
|
</div>
|
|
<input class="btn avatar_url" type="text" id="avatar_url" placeholder="----">
|
|
<button class="btn btn-primary btn-lg tooltip tooltip-right" id="set_avatar_url"
|
|
data-tooltip="Set Avatar URL">Set
|
|
</button>
|
|
</div>
|
|
<div class="tile-action">
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<br>
|
|
<div class="tile tile-centered">
|
|
<div class="tile-content">
|
|
<div class="tile-title text-bold">Upload File</div>
|
|
<input class="btn upload_file" type="file" id="upload_file">
|
|
<button class="btn btn-primary btn-lg" id="upload_file_button">Upload
|
|
</button>
|
|
</div>
|
|
<div class="tile-action">
|
|
</div>
|
|
</div>
|
|
<br>
|
|
|
|
|
|
<br>
|
|
|
|
<div class="divider text-center" data-content="Room Settings"></div>
|
|
<div class="tile tile-centered">
|
|
<div class="tile-content">
|
|
<div class="tile-title text-bold">Current Room</div>
|
|
<div class="tile-subtitle">
|
|
<a id="shareable_link" href="" target="blank">
|
|
Shareable Link
|
|
<svg style="width:1em;height:1em" viewBox="0 0 24 24">
|
|
<path fill="currentColor"
|
|
d="M14,3V5H17.59L7.76,14.83L9.17,16.24L19,6.41V10H21V3M19,19H5V5H12V3H5C3.89,3 3,3.9 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V12H19V19Z"/>
|
|
</svg>
|
|
</a>
|
|
</div>
|
|
<input class="btn current_room" type="text" id="current_room" placeholder="----">
|
|
<input style="display: none;" class="btn current_app" type="text" id="current_app"
|
|
placeholder="----">
|
|
<button class="btn btn-primary btn-lg tooltip tooltip-right" id="set_room_id"
|
|
data-tooltip="Set Room ID">Set
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<br>
|
|
<!-- <div class="tile tile-centered">-->
|
|
<!-- <div class="tile-content">-->
|
|
<!-- <div class="tile-title text-bold">Carpet Color</div>-->
|
|
<!-- <input class="btn carpet_color coloris" type="text" id="carpet_color" placeholder="#ffffff">-->
|
|
<!-- <button class="btn btn-primary btn-lg tooltip tooltip-right" id="set_carpet_color"-->
|
|
<!-- data-tooltip="Set Carpet Color">Set-->
|
|
<!-- </button>-->
|
|
<!-- </div>-->
|
|
<!-- <div class="tile-action">-->
|
|
<!-- </div>-->
|
|
<!-- </div>-->
|
|
<!-- <br>-->
|
|
|
|
<div class="divider text-center" data-content="Screen Sharing"></div>
|
|
|
|
<div>
|
|
<div>
|
|
<div class="flex flex-col h-screen relative">
|
|
<header class="flex h-16 justify-center items-center text-xl bg-black text-white">
|
|
<div class="columns">
|
|
<button id="bnt_pubcam" class="btn btn-secondary btn-lg col-6"
|
|
onclick="start(true)">Publish Camera
|
|
</button>
|
|
<button id="bnt_pubscreen" class="btn btn-secondary btn-lg col-6"
|
|
onclick="start(false)">Publish Screen
|
|
</button>
|
|
</div>
|
|
</header>
|
|
<video style="width: 100%;" id="pub_video" class="bg-black" controls></video>
|
|
</div>
|
|
<script src="https://unpkg.com/ion-sdk-js@1.5.5/dist/ion-sdk.min.js"></script>
|
|
<script src="https://unpkg.com/ion-sdk-js@1.5.5/dist/json-rpc.min.js"></script>
|
|
</div>
|
|
<p>For more screen-sharing options and remote control, download <a
|
|
href="https://github.com/velaboratory/VEL-Share/releases/latest" target="_blank">VEL-Share
|
|
<svg style="width:1em;height:1em" viewBox="0 0 24 24">
|
|
<path fill="currentColor"
|
|
d="M14,3V5H17.59L7.76,14.83L9.17,16.24L19,6.41V10H21V3M19,19H5V5H12V3H5C3.89,3 3,3.9 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V12H19V19Z"/>
|
|
</svg>
|
|
</a></p>
|
|
</div>
|
|
|
|
<br>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script type="text/javascript" src="/static/js/coloris.min.js"></script>
|
|
<script>
|
|
|
|
let submit_button = document.getElementById('submit_pairing_code');
|
|
let pair_code_input = document.getElementById('pair_code');
|
|
let loading = document.getElementById('loading');
|
|
let enter_pairing_id = document.getElementById('enter_pairing_id');
|
|
let headset_details = document.getElementById('headset_details');
|
|
let hw_id_field = document.getElementById('hw_id');
|
|
let failure = document.getElementById('failure');
|
|
let current_app = document.getElementById('current_app');
|
|
let current_room = document.getElementById('current_room');
|
|
let set_room_id = document.getElementById('set_room_id');
|
|
let set_user_color = document.getElementById('set_user_color');
|
|
let user_color = document.getElementById('user_color');
|
|
let carpet_color = document.getElementById('carpet_color');
|
|
let set_user_name = document.getElementById('set_user_name');
|
|
let set_tv_url = document.getElementById('set_tv_url');
|
|
let set_carpet_color = document.getElementById('set_carpet_color');
|
|
let set_avatar_url = document.getElementById('set_avatar_url');
|
|
let upload_file_button = document.getElementById('upload_file_button');
|
|
|
|
|
|
// check cookie
|
|
let hw_id = getCookie('hw_id');
|
|
|
|
if (hw_id !== "" && hw_id !== undefined && hw_id !== "undefined") {
|
|
|
|
httpGetAsync('/api/device/get_data/' + hw_id, (resp) => {
|
|
console.log(resp);
|
|
let respData = JSON.parse(resp);
|
|
|
|
if ("error" in respData) {
|
|
window.location.href = "/pair";
|
|
}
|
|
|
|
writeClass('hw_id', respData['device']['hw_id']);
|
|
writeClass('pairing_code', respData['device']['pairing_code']);
|
|
writeValue('current_app', respData['device']['current_app']);
|
|
writeValue('current_room', respData['device']['current_room']);
|
|
writeClass('date_created', respData['device']['date_created'] + "<br>" + timeSinceString(respData['device']['date_created']) + " ago");
|
|
writeClass('last_modified', respData['device']['last_modified'] + "<br>" + timeSinceString(respData['device']['last_modified']) + " ago");
|
|
writeValue('user_name', respData['device']['friendly_name']);
|
|
writeValue('avatar_url', respData['device']['data']?.['avatar_url']);
|
|
writeValue('tv_url', respData['room']?.['data']?.['tv_url']);
|
|
writeValue('carpet_color', respData['room']?.['data']?.['carpet_color']);
|
|
if (carpet_color) carpet_color.parentElement.style.color = "" + respData['room']?.['data']?.['carpet_color'];
|
|
|
|
|
|
Coloris({
|
|
el: '.coloris',
|
|
swatches: [
|
|
'#264653',
|
|
'#2a9d8f',
|
|
'#e9c46a',
|
|
'#f4a261',
|
|
'#e76f51',
|
|
'#d62828',
|
|
'#023e8a',
|
|
'#0077b6',
|
|
'#0096c7',
|
|
'#00b4d8',
|
|
'#48cae4',
|
|
]
|
|
});
|
|
|
|
document.getElementById('cover_image').src = `/static/img/cover_${respData['device']['current_app']}.png`
|
|
document.getElementById('shareable_link').href = `/join/${respData['device']['current_app']}/${respData['device']['current_room']}`
|
|
|
|
loading.style.display = "none";
|
|
headset_details.style.display = "block";
|
|
}, (status) => {
|
|
loading.style.display = "none";
|
|
failure.style.display = "block";
|
|
});
|
|
|
|
|
|
function setDeviceField(data) {
|
|
fetch('/api/device/set_data/' + hw_id, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Accept': 'application/json',
|
|
'Content-Type': 'application/json'
|
|
},
|
|
|
|
body: JSON.stringify(data)
|
|
})
|
|
.then(_ => console.log('success'))
|
|
.catch(_ => console.log('fail'));
|
|
}
|
|
|
|
function setDeviceData(data) {
|
|
fetch('/api/device/set_data/' + hw_id, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Accept': 'application/json',
|
|
'Content-Type': 'application/json'
|
|
},
|
|
|
|
body: JSON.stringify({"data": data})
|
|
})
|
|
.then(_ => console.log('success'))
|
|
.catch(_ => console.log('fail'));
|
|
}
|
|
|
|
function setRoomData(data) {
|
|
fetch('/api/set_data/' + current_app.value + "_" + current_room.value, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Accept': 'application/json',
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify(data)
|
|
})
|
|
.then(_ => console.log('success'))
|
|
.catch(_ => console.log('fail'));
|
|
}
|
|
|
|
|
|
function uploadFile(file) {
|
|
let formData = new FormData();
|
|
formData.append("file", file);
|
|
fetch('/api/upload_file', {
|
|
method: 'POST',
|
|
body: formData
|
|
})
|
|
.then(_ => console.log('success'))
|
|
.catch(_ => console.log('fail'));
|
|
}
|
|
|
|
if (set_room_id) {
|
|
set_room_id.addEventListener('click', () => {
|
|
setDeviceField({"current_room": current_room.value});
|
|
});
|
|
}
|
|
if (set_user_color) {
|
|
set_user_color.addEventListener('click', () => {
|
|
setDeviceData({"user_color": document.getElementById('user_color').value});
|
|
});
|
|
}
|
|
if (set_user_name) {
|
|
set_user_name.addEventListener('click', () => {
|
|
setDeviceField({"friendly_name": document.getElementById('user_name').value});
|
|
});
|
|
}
|
|
if (set_tv_url) {
|
|
set_tv_url.addEventListener('click', () => {
|
|
setRoomData({"tv_url": document.getElementById('tv_url').value});
|
|
});
|
|
}
|
|
if (set_carpet_color) {
|
|
set_carpet_color.addEventListener('click', () => {
|
|
setRoomData({"carpet_color": document.getElementById('carpet_color').value});
|
|
});
|
|
}
|
|
if (set_avatar_url) {
|
|
set_avatar_url.addEventListener('click', () => {
|
|
setDeviceData({"avatar_url": document.getElementById('avatar_url').value});
|
|
});
|
|
}
|
|
if (upload_file_button) {
|
|
upload_file_button.addEventListener('click', () => {
|
|
uploadFile(document.getElementById('upload_file').files[0]);
|
|
});
|
|
}
|
|
|
|
} else {
|
|
window.location.href = "/pair";
|
|
}
|
|
|
|
|
|
Coloris({
|
|
el: '.coloris',
|
|
swatches: [
|
|
'#264653',
|
|
'#2a9d8f',
|
|
'#e9c46a',
|
|
'#f4a261',
|
|
'#e76f51',
|
|
'#d62828',
|
|
'#023e8a',
|
|
'#0077b6',
|
|
'#0096c7',
|
|
'#00b4d8',
|
|
'#48cae4',
|
|
]
|
|
});
|
|
|
|
|
|
const roomName = (Math.random() + 1).toString(36).substring(7);
|
|
const pubVideo = document.getElementById("pub_video");
|
|
const subVideo = document.getElementById("sub_video");
|
|
const bntPubCam = document.getElementById("bnt_pubcam");
|
|
const bntPubScreen = document.getElementById("bnt_pubscreen");
|
|
|
|
setDeviceData({"streamer_stream_id": roomName});
|
|
|
|
const serverURL = "wss://velnet.ugavel.com/ws";
|
|
|
|
const config = {
|
|
iceServers: [
|
|
{
|
|
urls: "stun:stun.l.google.com:19302",
|
|
},
|
|
],
|
|
};
|
|
|
|
const signalLocal = new Signal.IonSFUJSONRPCSignal(serverURL);
|
|
const clientLocal = new IonSDK.Client(signalLocal, config);
|
|
|
|
signalLocal.onopen = () => clientLocal.join(roomName);
|
|
|
|
const start = (type) => {
|
|
if (type) {
|
|
IonSDK.LocalStream.getUserMedia({
|
|
resolution: "vga",
|
|
audio: true,
|
|
video: true,
|
|
codec: "vp8",
|
|
}).then((media) => {
|
|
pubVideo.srcObject = media;
|
|
pubVideo.autoplay = true;
|
|
pubVideo.controls = true;
|
|
pubVideo.muted = true;
|
|
bntPubCam.disabled = true;
|
|
bntPubScreen.disabled = true;
|
|
clientLocal.publish(media);
|
|
}).catch(console.error);
|
|
} else {
|
|
IonSDK.LocalStream.getDisplayMedia({
|
|
audio: true,
|
|
video: true,
|
|
codec: "vp8",
|
|
}).then((media) => {
|
|
pubVideo.srcObject = media;
|
|
pubVideo.autoplay = true;
|
|
pubVideo.controls = true;
|
|
pubVideo.muted = true;
|
|
bntPubCam.disabled = true;
|
|
bntPubScreen.disabled = true;
|
|
clientLocal.publish(media);
|
|
}).catch(console.error);
|
|
}
|
|
}
|
|
|
|
clientLocal.ontrack = (track, stream) => {
|
|
console.log("got track: ", track.id, "for stream: ", stream.id);
|
|
stream.mute();
|
|
stream.unmute();
|
|
if (track.kind === "video") {
|
|
subVideo.srcObject = stream;
|
|
subVideo.play();
|
|
}
|
|
//track.onunmute = () => {
|
|
//subVideo.srcObject = stream;
|
|
//subVideo.autoplay = true;
|
|
//subVideo.muted = true;
|
|
|
|
//subVideo.play();
|
|
|
|
//stream.onremovetrack = () => {
|
|
//subVideo.srcObject = null;
|
|
//}
|
|
//}
|
|
}
|
|
|
|
|
|
</script>
|
|
</div>
|
|
</body>
|
|
|
|
</html> |