Delete uncompleted tutorials

This commit is contained in:
juancarmore 2024-09-20 16:07:37 +02:00
parent 7112f7b7e3
commit 081e45bc9e
76 changed files with 0 additions and 6674 deletions

View File

@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -1,14 +0,0 @@
[![License badge](https://img.shields.io/badge/license-Apache2-orange.svg)](http://www.apache.org/licenses/LICENSE-2.0)
[![OpenVidu Tests](https://github.com/OpenVidu/openvidu/actions/workflows/openvidu-ce-test.yml/badge.svg)](https://github.com/OpenVidu/openvidu/actions/workflows/openvidu-ce-test.yml)
[![Documentation Status](https://readthedocs.org/projects/openviduio-docs/badge/?version=stable)](https://docs.openvidu.io/en/stable/?badge=stable)
[![Docker badge](https://img.shields.io/docker/pulls/openvidu/openvidu-server-kms.svg)](https://hub.docker.com/r/openvidu/openvidu-server-kms)
[![Support badge](https://img.shields.io/badge/support-sof-yellowgreen.svg)](https://openvidu.discourse.group/)
[![][OpenViduLogo]](http://openvidu.io)
openvidu-js-screen-share
===
Visit [docs.openvidu.io/en/stable/tutorials/openvidu-js-screen-share/](http://docs.openvidu.io/en/stable/tutorials/openvidu-js-screen-share/)
[OpenViduLogo]: https://secure.gravatar.com/avatar/5daba1d43042f2e4e85849733c8e5702?s=120

View File

@ -1,17 +0,0 @@
FROM node:16-alpine3.16
# Copy openvidu-basic-node
COPY ./openvidu-basic-node /opt/openvidu-basic-node
# Install openvidu-basic-node dependencies
RUN npm --prefix /opt/openvidu-basic-node install
# Copy static files to openvidu-basic-node
RUN mkdir -p /opt/openvidu-basic-node/public
COPY ./web /opt/openvidu-basic-node/public
WORKDIR /opt/openvidu-basic-node
COPY docker/entrypoint.sh .
ENTRYPOINT [ "./entrypoint.sh" ]

View File

@ -1,15 +0,0 @@
#!/bin/bash
if [ $# -eq 0 ]; then
echo "No version argument provided. Usage: \"./create_image.sh <IMAGE_NAME>\""
exit 1
fi
pushd ../
cp -r ../openvidu-basic-node .
trap 'rm -rf ./openvidu-basic-node' ERR
docker build --pull --no-cache --rm=true -f docker/Dockerfile -t "$1" .
rm -rf ./openvidu-basic-node

View File

@ -1,14 +0,0 @@
#!/bin/sh
if [ -n "${OPENVIDU_APPLICATION_SERVER_URL}" ]; then
# Replace OPENVIDU_APPLICATION_SERVER_URL at frontend app
sed -i \
"s|var APPLICATION_SERVER_URL = \"http://localhost:5000/\";|var APPLICATION_SERVER_URL = \"${OPENVIDU_APPLICATION_SERVER_URL}/\";|" \
public/app.js
else
sed -i \
"s|var APPLICATION_SERVER_URL = \"http://localhost:5000/\";|var APPLICATION_SERVER_URL = \"\";|" \
public/app.js
fi
exec node index.js "$*"

View File

@ -1,52 +0,0 @@
events {
worker_connections 512;
}
http {
upstream openvidu-deployment {
server host.docker.internal:4443;
}
upstream server-application {
server host.docker.internal:5000;
}
upstream client-application {
server host.docker.internal:8080;
}
server {
listen 443 ssl;
ssl_certificate /etc/nginx/certs/cert.pem;
ssl_certificate_key /etc/nginx/certs/key.pem;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Proto https;
proxy_headers_hash_bucket_size 512;
proxy_redirect off;
# Websockets
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# OpenVidu deployment API
location /openvidu/api {
proxy_pass http://openvidu-deployment;
}
# OpenVidu WebSocket
location ~ /openvidu$ {
proxy_pass http://openvidu-deployment;
}
# Server application requests
location /api/ {
proxy_pass http://server-application;
}
# Client application requests
location / {
proxy_pass http://client-application;
}
}
}

View File

@ -1,263 +0,0 @@
var LivekitClient = window.LivekitClient;
var room;
var myUserName;
var myRoomName;
var isScreenShared = false;
var screenSharePublication;
/* OPENVIDU METHODS */
function joinRoom() {
myRoomName = document.getElementById('roomName').value;
myUserName = document.getElementById('userName').value;
// --- 1) Get a Room object ---
room = new LivekitClient.Room();
// --- 2) Specify the actions when events take place in the room ---
// On every new Track received...
room.on(
LivekitClient.RoomEvent.TrackSubscribed,
(track, publication, participant) => {
const element = track.attach();
element.id = track.sid;
element.className = 'removable';
document.getElementById('video-container').appendChild(element);
if (track.kind === 'video' || track.kind === 'screen') {
appendUserData(element, participant.identity);
}
}
);
// On every new Track destroyed...
room.on(
LivekitClient.RoomEvent.TrackUnsubscribed,
(track, publication, participant) => {
track.detach();
document.getElementById(track.sid)?.remove();
if (track.kind === 'video' || track.kind === 'screen') {
removeUserData(participant);
}
}
);
// --- 3) Connect to the room with a valid access token ---
// Get a token from the application backend
getToken(myRoomName, myUserName).then((token) => {
const livekitUrl = getLivekitUrlFromMetadata(token);
// First param is the LiveKit server URL. Second param is the access token
room
.connect(livekitUrl, token)
.then(() => {
// --- 4) Set page layout for active call ---
document.getElementById('room-title').innerText = myRoomName;
document.getElementById('join').style.display = 'none';
document.getElementById('room').style.display = 'block';
// --- 5) Publish your local tracks ---
room.localParticipant.setMicrophoneEnabled(true);
room.localParticipant.setCameraEnabled(true).then((publication) => {
const element = publication.track.attach();
document.getElementById('video-container').appendChild(element);
initMainVideo(element, myUserName);
appendUserData(element, myUserName);
element.className = 'removable';
});
})
.catch((error) => {
console.log(
'There was an error connecting to the room:',
error.code,
error.message
);
});
});
}
function leaveRoom() {
// --- 6) Leave the room by calling 'disconnect' method over the Room object ---
room.disconnect();
// Removing all HTML elements with user's nicknames.
// HTML videos are automatically removed when leaving a Room
removeAllUserData();
// Back to 'Join room' page
document.getElementById('join').style.display = 'block';
document.getElementById('room').style.display = 'none';
}
async function toggleScreenShare() {
console.log('Toggling screen share');
const enabled = !isScreenShared;
if (enabled) {
// Enable screen sharing
try {
screenSharePublication =
await room.localParticipant?.setScreenShareEnabled(enabled);
} catch (error) {
console.error('Error enabling screen sharing', error);
}
if (screenSharePublication) {
console.log('Screen sharing enabled', screenSharePublication);
isScreenShared = enabled;
// Attach the screen share track to the video container
const element = screenSharePublication.track.attach();
element.id = screenSharePublication.trackSid;
element.className = 'removable';
document.getElementById('video-container').appendChild(element);
// Add user data for the screen share
appendUserData(element, `${myUserName}_SCREEN`);
// Listen for the 'ended' event to handle screen sharing stop
screenSharePublication.addListener('ended', async () => {
console.debug('Clicked native stop button. Stopping screen sharing');
await stopScreenSharing();
});
}
} else {
// Disable screen sharing
await stopScreenSharing();
}
}
async function stopScreenSharing() {
try {
await room.localParticipant?.setScreenShareEnabled(false);
isScreenShared = false;
const trackSid = screenSharePublication?.trackSid;
if (trackSid) {
document.getElementById(trackSid)?.remove();
removeUserData({ identity: `${myUserName}_SCREEN` });
}
screenSharePublication = undefined;
} catch (error) {
console.error('Error stopping screen sharing', error);
}
}
window.onbeforeunload = function () {
if (room) room.disconnect();
};
/* APPLICATION SPECIFIC METHODS */
window.addEventListener('load', function () {
generateParticipantInfo();
});
function generateParticipantInfo() {
document.getElementById('roomName').value = 'RoomA';
document.getElementById('userName').value =
'Participant' + Math.floor(Math.random() * 100);
}
function appendUserData(videoElement, participantIdentity) {
var dataNode = document.createElement('div');
dataNode.className = 'removable';
dataNode.id = 'data-' + participantIdentity;
dataNode.innerHTML = '<p>' + participantIdentity + '</p>';
videoElement.parentNode.insertBefore(dataNode, videoElement.nextSibling);
addClickListener(videoElement, participantIdentity);
}
function removeUserData(participant) {
var dataNode = document.getElementById('data-' + participant.identity);
dataNode?.parentNode.removeChild(dataNode);
}
function removeAllUserData() {
var elementsToRemove = document.getElementsByClassName('removable');
while (elementsToRemove[0]) {
elementsToRemove[0].parentNode.removeChild(elementsToRemove[0]);
}
}
function addClickListener(videoElement, userData) {
videoElement.addEventListener('click', function () {
var mainVideo = $('#main-video video').get(0);
if (mainVideo.srcObject !== videoElement.srcObject) {
$('#main-video').fadeOut('fast', () => {
$('#main-video p').html(userData);
mainVideo.srcObject = videoElement.srcObject;
$('#main-video').fadeIn('fast');
});
}
});
}
function initMainVideo(videoElement, userData) {
document.querySelector('#main-video video').srcObject =
videoElement.srcObject;
document.querySelector('#main-video p').innerHTML = userData;
document.querySelector('#main-video video')['muted'] = true;
}
function getLivekitUrlFromMetadata(token) {
if (!token) throw new Error('Trying to get metadata from an empty token');
try {
const base64Url = token.split('.')[1];
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
const jsonPayload = decodeURIComponent(
window
.atob(base64)
.split('')
.map((c) => {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
})
.join('')
);
const payload = JSON.parse(jsonPayload);
if (!payload?.metadata) throw new Error('Token does not contain metadata');
const metadata = JSON.parse(payload.metadata);
return metadata.livekitUrl;
} catch (error) {
throw new Error('Error decoding and parsing token: ' + error);
}
}
/**
* --------------------------------------------
* GETTING A TOKEN FROM YOUR APPLICATION SERVER
* --------------------------------------------
* The methods below request the creation of a Token to
* your application server. This keeps your OpenVidu deployment secure.
*
* In this sample code, there is no user control at all. Anybody could
* access your application server endpoints! In a real production
* environment, your application server must identify the user to allow
* access to the endpoints.
*
*/
var APPLICATION_SERVER_URL = 'http://localhost:5000/';
function getToken(roomName, participantName) {
return new Promise((resolve, reject) => {
$.ajax({
type: 'POST',
url: APPLICATION_SERVER_URL + 'token',
data: JSON.stringify({
roomName,
participantName,
permissions: {},
}),
headers: { 'Content-Type': 'application/json' },
success: (token) => resolve(token),
error: (error) => reject(error),
});
});
}

View File

@ -1,75 +0,0 @@
<html>
<head>
<title>openvidu-js-screen-share</title>
<meta name="viewport" content="width=device-width, initial-scale=1" charset="utf-8">
<link rel="shortcut icon" href="resources/images/favicon.ico" type="image/x-icon">
<!-- Bootstrap -->
<script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<!-- Bootstrap -->
<link rel="stylesheet" href="style.css" type="text/css" media="screen">
<script src="https://cdn.jsdelivr.net/npm/livekit-client/dist/livekit-client.umd.min.js"></script>
<script src="app.js"></script>
</head>
<body>
<nav class="navbar navbar-default">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="/"><img class="demo-logo" src="resources/images/openvidu_vert_white_bg_trans_cropped.png"/> JS Screen Share</a>
<a class="navbar-brand nav-icon" href="https://github.com/OpenVidu/openvidu-tutorials/tree/master/openvidu-js-screen-share" title="GitHub Repository" target="_blank"><i class="fa fa-github" aria-hidden="true"></i></a>
<a class="navbar-brand nav-icon" href="https://docs.openvidu.io/en/stable/tutorials/openvidu-js-screen-share/" title="Documentation" target="_blank"><i class="fa fa-book" aria-hidden="true"></i></a>
</div>
</div>
</nav>
<div id="main-container" class="container">
<div id="join">
<div id="img-div"><img src="resources/images/openvidu_grey_bg_transp_cropped.png" /></div>
<div id="join-dialog" class="jumbotron vertical-center">
<h1>Join a video room</h1>
<form class="form-group" onsubmit="joinRoom(); return false">
<p>
<label>Participant</label>
<input class="form-control" type="text" id="userName" required>
</p>
<p>
<label>Room</label>
<input class="form-control" type="text" id="roomName" required>
</p>
<p class="text-center">
<input class="btn btn-lg btn-success" type="submit" name="commit" value="Join!">
</p>
</form>
</div>
</div>
<div id="room" style="display: none;">
<div id="room-header">
<h1 id="room-title"></h1>
<input class="btn btn-large" type="button" id="buttonScreenShare" onmouseup="toggleScreenShare()" value="Screen share">
<input class="btn btn-large btn-danger" type="button" id="buttonLeaveRoom" onmouseup="leaveRoom()" value="Leave room">
</div>
<div id="main-video" class="col-md-6"><p></p><video autoplay playsinline="true"></video></div>
<div id="video-container" class="col-md-6"></div>
</div>
</div>
<footer class="footer">
<div class="container">
<div class="text-muted">OpenVidu © 2022</div>
<a href="http://www.openvidu.io/" target="_blank"><img class="openvidu-logo" src="resources/images/openvidu_globe_bg_transp_cropped.png"/></a>
</div>
</footer>
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

View File

@ -1,296 +0,0 @@
html {
position: relative;
min-height: 100%;
}
nav {
height: 50px;
width: 100%;
z-index: 1;
background-color: #4d4d4d !important;
border-color: #4d4d4d !important;
border-top-right-radius: 0 !important;
border-top-left-radius: 0 !important;
}
.navbar-header {
width: 100%;
}
.nav-icon {
padding: 5px 15px 5px 15px;
float: right;
}
nav a {
color: #ccc !important;
}
nav i.fa {
font-size: 40px;
color: #ccc;
}
nav a:hover {
color: #a9a9a9 !important;
}
nav i.fa:hover {
color: #a9a9a9;
}
#main-container {
padding-bottom: 80px;
}
/*vertical-center {
position: relative;
top: 30%;
left: 50%;
transform: translate(-50%, -50%);
}*/
.horizontal-center {
margin: 0 auto;
}
.form-control {
color: #0088aa;
font-weight: bold;
}
.form-control:focus {
border-color: #0088aa;
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(0, 136, 170, 0.6);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(0, 136, 170, 0.6);
}
input.btn {
font-weight: bold;
}
.btn {
font-weight: bold !important;
}
.btn-success {
background-color: #06d362 !important;
border-color: #06d362;
}
.btn-success:hover {
background-color: #1abd61 !important;
border-color: #1abd61;
}
.footer {
position: absolute;
bottom: 0;
width: 100%;
height: 60px;
background-color: #4d4d4d;
}
.footer .text-muted {
margin: 20px 0;
float: left;
color: #ccc;
}
.openvidu-logo {
height: 35px;
float: right;
margin: 12px 0;
-webkit-transition: all 0.1s ease-in-out;
-moz-transition: all 0.1s ease-in-out;
-o-transition: all 0.1s ease-in-out;
transition: all 0.1s ease-in-out;
}
.openvidu-logo:hover {
-webkit-filter: grayscale(0.5);
filter: grayscale(0.5);
}
.demo-logo {
margin: 0;
height: 22px;
float: left;
padding-right: 8px;
}
a:hover .demo-logo {
-webkit-filter: brightness(0.7);
filter: brightness(0.7);
}
#join-dialog {
margin-left: auto;
margin-right: auto;
max-width: 70%;
}
#join-dialog h1 {
color: #4d4d4d;
font-weight: bold;
text-align: center;
}
#img-div {
text-align: center;
margin-top: 3em;
margin-bottom: 3em;
/*position: relative;
top: 20%;
left: 50%;
transform: translate(-50%, -50%);*/
}
#img-div img {
height: 15%;
}
#join-dialog label {
color: #0088aa;
}
#join-dialog input.btn {
margin-top: 15px;
}
#room-header {
margin-bottom: 20px;
}
#room-title {
display: inline-block;
}
#buttonLeaveRoom {
float: right;
margin-top: 20px;
}
#buttonScreenShare {
float: right;
margin-top: 20px;
margin-left: 15px;
}
#container-cameras video {
position: relative;
float: left;
width: 50%;
cursor: pointer;
}
#container-cameras div {
float: left;
width: 50%;
position: relative;
margin-left: -50%;
}
#container-cameras p {
display: inline-block;
background: #f8f8f8;
padding-left: 5px;
padding-right: 5px;
color: #777777;
font-weight: bold;
border-bottom-right-radius: 4px;
}
#container-screens video {
position: relative;
float: left;
width: 50%;
cursor: pointer;
}
#container-screens div {
float: left;
width: 50%;
position: relative;
margin-left: -50%;
}
#container-screens p {
display: inline-block;
background: #f8f8f8;
padding-left: 5px;
padding-right: 5px;
color: #777777;
font-weight: bold;
border-bottom-right-radius: 4px;
}
video {
width: 100%;
height: auto;
}
#main-video p {
position: absolute;
display: inline-block;
background: #f8f8f8;
padding-left: 5px;
padding-right: 5px;
font-size: 22px;
color: #777777;
font-weight: bold;
border-bottom-right-radius: 4px;
}
#room img {
width: 100%;
height: auto;
display: inline-block;
object-fit: contain;
vertical-align: baseline;
}
#room #container-cameras img {
position: relative;
float: left;
width: 50%;
cursor: pointer;
object-fit: cover;
height: 180px;
}
/* xs ans md screen resolutions*/
@media screen and (max-width: 991px) and (orientation: portrait) {
#join-dialog {
max-width: inherit;
}
#img-div img {
height: 10%;
}
#img-div {
margin-top: 2em;
margin-bottom: 2em;
}
.container-fluid>.navbar-collapse, .container-fluid>.navbar-header, .container>.navbar-collapse, .container>.navbar-header {
margin-right: 0;
margin-left: 0;
}
.navbar-header i.fa {
font-size: 30px;
}
.navbar-header a.nav-icon {
padding: 7px 3px 7px 3px;
}
}
@media only screen and (max-height: 767px) and (orientation: landscape) {
#img-div {
margin-top: 1em;
margin-bottom: 1em;
}
#join-dialog {
max-width: inherit;
}
}

View File

@ -1,25 +0,0 @@
# Compiled class file
*.class
# Log file
*.log
# BlueJ files
*.ctxt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
target/
.vscode/*

View File

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<beansProjectDescription>
<version>1</version>
<pluginVersion><![CDATA[3.9.2.201712210913-RELEASE]]></pluginVersion>
<configSuffixes>
<configSuffix><![CDATA[xml]]></configSuffix>
</configSuffixes>
<enableImports><![CDATA[false]]></enableImports>
<configs>
<config>java:io.openvidu.recording.java.App</config>
</configs>
<autoconfigs>
</autoconfigs>
<configSets>
</configSets>
</beansProjectDescription>

View File

@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -1,14 +0,0 @@
[![License badge](https://img.shields.io/badge/license-Apache2-orange.svg)](http://www.apache.org/licenses/LICENSE-2.0)
[![OpenVidu Tests](https://github.com/OpenVidu/openvidu/actions/workflows/openvidu-ce-test.yml/badge.svg)](https://github.com/OpenVidu/openvidu/actions/workflows/openvidu-ce-test.yml)
[![Documentation Status](https://readthedocs.org/projects/openvidu/badge/?version=stable)](https://docs.openvidu.io/en/stable/?badge=stable)
[![Docker badge](https://img.shields.io/docker/pulls/openvidu/openvidu-server-kms.svg)](https://hub.docker.com/r/openvidu/openvidu-server-kms)
[![Support badge](https://img.shields.io/badge/support-sof-yellowgreen.svg)](https://openvidu.discourse.group/)
[![][OpenViduLogo]](http://openvidu.io)
openvidu-recording-java
===
Visit [docs.openvidu.io/en/stable/tutorials/openvidu-recording-java/](http://docs.openvidu.io/en/stable/tutorials/openvidu-recording-java/)
[OpenViduLogo]: https://secure.gravatar.com/avatar/5daba1d43042f2e4e85849733c8e5702?s=120

View File

@ -1,26 +0,0 @@
FROM maven:3.6.3 as build
WORKDIR /openvidu-recording-java
COPY ./pom.xml pom.xml
COPY ./src/main src/main
RUN mvn clean install
RUN mvn -o package
FROM alpine:3.11
RUN apk update && \
apk add openjdk11-jre && \
rm -rf /var/cache/apk/*
# Install basic-webinar
RUN mkdir -p /opt/openvidu-recording-java
COPY --from=build /openvidu-recording-java/target/openvidu-recording-java-*.jar /opt/openvidu-recording-java/openvidu-recording-java.jar
# Entrypoint
COPY ./docker/entrypoint.sh /usr/local/bin
RUN chmod +x /usr/local/bin/entrypoint.sh
CMD /usr/local/bin/entrypoint.sh

View File

@ -1,8 +0,0 @@
#!/bin/bash
if [ $# -eq 0 ]; then
echo "No version argument provided. Usage: \"./create_image.sh <IMAGE_NAME>\""
exit 1
fi
pushd ../
docker build -f docker/Dockerfile -t "$1" .

View File

@ -1,13 +0,0 @@
#!/bin/sh
[ ! -z "${OPENVIDU_URL}" ] && echo "OPENVIDU_URL: ${OPENVIDU_URL}" || echo "OPENVIDU_URL: default"
[ ! -z "${OPENVIDU_SECRET}" ] && echo "OPENVIDU_SECRET: ${OPENVIDU_SECRET}" || echo "OPENVIDU_SECRET: default"
[ ! -z "${SERVER_PORT}" ] && echo "SERVER_PORT: ${SERVER_PORT}" || echo "SERVER_PORT: default"
# Run Application
JAVA_PROPERTIES="-Djava.security.egd=file:/dev/./urandom"
[ ! -z "${OPENVIDU_URL}" ] && JAVA_PROPERTIES=" ${JAVA_PROPERTIES} -Dopenvidu.url=${OPENVIDU_URL}"
[ ! -z "${OPENVIDU_SECRET}" ] && JAVA_PROPERTIES=" ${JAVA_PROPERTIES} -Dopenvidu.secret=${OPENVIDU_SECRET}"
[ ! -z "${SERVER_PORT}" ] && JAVA_PROPERTIES=" ${JAVA_PROPERTIES} -Dserver.port=${SERVER_PORT}"
java ${JAVA_PROPERTIES} -jar /opt/openvidu-recording-java/openvidu-recording-java.jar

View File

@ -1,44 +0,0 @@
events {
worker_connections 512;
}
http {
upstream openvidu-deployment {
server host.docker.internal:4443;
}
upstream client-application {
server host.docker.internal:5000;
}
server {
listen 443 ssl;
ssl_certificate /etc/nginx/certs/cert.pem;
ssl_certificate_key /etc/nginx/certs/key.pem;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Proto https;
proxy_headers_hash_bucket_size 512;
proxy_redirect off;
# Websockets
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# OpenVidu deployment API
location /openvidu/api {
proxy_pass http://openvidu-deployment;
}
# OpenVidu WebSocket
location ~ /openvidu$ {
proxy_pass http://openvidu-deployment;
}
# Client application requests
location / {
proxy_pass https://client-application;
}
}
}

View File

@ -1,68 +0,0 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>io.openvidu</groupId>
<artifactId>openvidu-recording-java</artifactId>
<version>2.27.0</version>
<packaging>jar</packaging>
<name>openvidu-recording-java</name>
<url>https://github.com/OpenVidu/openvidu-tutorials/tree/master/openvidu-recording-java</url>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>11</java.version>
<start-class>io.openvidu.recording.java.App</start-class>
<docker.image.prefix>openvidu</docker.image.prefix>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>1.5.21</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<dependency>
<groupId>io.livekit</groupId>
<artifactId>livekit-server</artifactId>
<version>0.5.7</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.12.0</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20231013</version>
</dependency>
</dependencies>
</project>

View File

@ -1,13 +0,0 @@
package io.openvidu.recording.java;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class App
{
public static void main( String[] args )
{
SpringApplication.run(App.class, args);
}
}

View File

@ -1,251 +0,0 @@
package io.openvidu.recording.java;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.ResourceUtils;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.json.JSONObject;
import io.livekit.server.*;
import livekit.LivekitEgress;
import livekit.LivekitEgress.EgressInfo;
import livekit.LivekitEgress.EncodedFileOutput;
import livekit.LivekitEgress.EncodedFileType;
import livekit.LivekitEgress.EncodedFileOutput.Builder;
@CrossOrigin(origins = "*")
@RestController
public class MyRestController {
@Value("${LIVEKIT_URL}")
private String LIVEKIT_URL;
@Value("${LIVEKIT_API_KEY}")
private String LIVEKIT_API_KEY;
@Value("${LIVEKIT_API_SECRET}")
private String LIVEKIT_API_SECRET;
@Value("${RECORDINGS_PATH}")
private String RECORDINGS_PATH;
// OpenVidu object as entrypoint of the SDK
private EgressServiceClient egressClient;
@PostConstruct()
public void initialize() {
String livekitUrlHostname = LIVEKIT_URL.replaceFirst("^ws:", "http:").replaceFirst("^wss:", "https:");
this.egressClient = EgressServiceClient.create(livekitUrlHostname, LIVEKIT_API_KEY, LIVEKIT_API_SECRET, true);
}
/*******************/
/*** Session API ***/
/*******************/
/**
* @param params The JSON object with roomName and participantName
* @return The JWT token
*/
@PostMapping("/token")
public ResponseEntity<?> getToken(@RequestBody(required = true) Map<String, String> params) {
String roomName = params.get("roomName");
String participantName = params.get("participantName");
JSONObject response = new JSONObject();
if (roomName == null || participantName == null) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
// By default, tokens expire 6 hours after generation.
// You may override this by using token.setTtl(long millis).
AccessToken token = new AccessToken(LIVEKIT_API_KEY, LIVEKIT_API_SECRET);
token.setName(participantName);
token.setIdentity(participantName);
JSONObject metadata = new JSONObject();
metadata.put("livekitUrl", LIVEKIT_URL);
// add metadata to the token, which will be available in the participant's
// metadata
token.setMetadata(metadata.toString());
token.addGrants(new RoomJoin(true), new RoomName(roomName));
response.put("token", token.toJwt());
return new ResponseEntity<>(response.toMap(), HttpStatus.OK);
}
/*******************/
/** Recording API **/
/*******************/
@RequestMapping(value = "/recordings/start", method = RequestMethod.POST)
public ResponseEntity<?> startRecording(@RequestBody Map<String, Object> params) {
try {
String roomName = (String) params.get("roomName");
String outputMode = (String) params.get("outputMode");
Boolean videoOnly = (Boolean) params.get("videoOnly");
Boolean audioOnly = (Boolean) params.get("audioOnly");
String audioTrackId = (String) params.get("audioTrackId");
String videoTrackId = (String) params.get("videoTrackId");
Builder outputBuilder = LivekitEgress.EncodedFileOutput.newBuilder()
.setFileType(EncodedFileType.DEFAULT_FILETYPE)
.setFilepath("/recordings/" + roomName + "-" + new Date().getTime())
.setDisableManifest(true);
EncodedFileOutput output = outputBuilder.build();
System.out.println("Starting recording " + roomName);
LivekitEgress.EgressInfo egressInfo;
if ("COMPOSED".equals(outputMode)) {
System.out.println("Starting COMPOSED recording " + roomName);
egressInfo = this.egressClient
.startRoomCompositeEgress(roomName, output, "grid", null, null, audioOnly, videoOnly)
.execute().body();
} else if ("INDIVIDUAL".equals(outputMode)) {
System.out.println("Starting INDIVIDUAL recording " + roomName);
egressInfo = this.egressClient.startTrackCompositeEgress(roomName, output, audioTrackId, videoTrackId)
.execute().body();
} else {
return ResponseEntity.badRequest().body("outputMode is required");
}
return ResponseEntity.ok().body(generateEgressInfoResponse(egressInfo));
} catch (Exception e) {
System.out.println("Error starting recording " + e.getMessage());
return ResponseEntity.badRequest().body("Error starting recording");
}
}
@RequestMapping(value = "/recordings/stop", method = RequestMethod.POST)
public ResponseEntity<?> stopRecording(@RequestBody Map<String, Object> params) {
String recordingId = (String) params.get("recordingId");
if (recordingId == null) {
return ResponseEntity.badRequest().body("recordingId is required");
}
System.out.println("Stoping recording | {recordingId}=" + recordingId);
try {
LivekitEgress.EgressInfo egressInfo = this.egressClient.stopEgress(recordingId).execute().body();
return ResponseEntity.ok().body(generateEgressInfoResponse(egressInfo));
} catch (Exception e) {
System.out.println("Error stoping recording " + e.getMessage());
return ResponseEntity.badRequest().body("Error stoping recording");
}
}
@RequestMapping(value = "/recordings", method = RequestMethod.DELETE)
public ResponseEntity<?> deleteRecordings() {
try {
File recordingsDir = ResourceUtils.getFile("classpath:static");
deleteFiles(new File(RECORDINGS_PATH));
deleteFiles(new File(recordingsDir.getAbsolutePath()));
JSONObject response = new JSONObject();
response.put("message", "All recordings deleted");
return ResponseEntity.ok().body(response.toMap());
} catch (IOException e) {
e.printStackTrace();
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Error deleting recordings");
}
}
@RequestMapping(value = "/recordings/list", method = RequestMethod.GET)
public ResponseEntity<?> listRecordings() {
System.out.println("Listing recordings");
List<JSONObject> recordings = new ArrayList<>();
try {
File recordingsDir = ResourceUtils.getFile("classpath:static");
Files.walk(Path.of(RECORDINGS_PATH)).forEach(filePath -> {
JSONObject recordingsMap = new JSONObject();
if (Files.isRegularFile(filePath)) {
String fileName = filePath.getFileName().toString();
String destinationPath = recordingsDir.getAbsolutePath() + File.separator + fileName;
try {
Files.copy(filePath, Path.of(destinationPath), StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
e.printStackTrace();
}
recordingsMap.put("name", fileName);
recordingsMap.put("path", "/" + fileName);
recordings.add(recordingsMap);
}
});
} catch (IOException e) {
e.printStackTrace();
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
JSONObject response = new JSONObject();
response.put("recordings", recordings);
return new ResponseEntity<>(response.toMap(), HttpStatus.OK);
}
private void deleteFiles(File directory) {
if (directory.exists() && directory.isDirectory()) {
File[] files = directory.listFiles();
if (files != null) {
for (File file : files) {
if (file.isDirectory()) {
deleteFiles(file);
} else {
if (file.delete()) {
System.out.println("Deleted file: " + file.getAbsolutePath());
} else {
System.out.println("Failed to delete file: " + file.getAbsolutePath());
}
}
}
}
}
}
private Map<String, Object> generateEgressInfoResponse(EgressInfo egressInfo) {
JSONObject info = new JSONObject();
JSONObject response = new JSONObject();
info.put("egressId", egressInfo.getEgressId());
info.put("roomName", egressInfo.getRoomName());
info.put("status", egressInfo.getStatus().toString());
response.put("info", info);
return response.toMap();
}
}

View File

@ -1,9 +0,0 @@
spring.profiles.active=container
server.port: 3000
server.ssl.enabled: false
LIVEKIT_URL: ws://localhost:7880/
LIVEKIT_API_KEY: http://localhost:4443/
LIVEKIT_API_SECRET: MY_SECRET

View File

@ -1,12 +0,0 @@
server.port: 5000
server.ssl.enabled: true
server.ssl.key-store: classpath:openvidu-selfsigned.jks
server.ssl.key-store-password: openvidu
server.ssl.key-store-type: JKS
server.ssl.key-alias: openvidu-selfsigned
spring.http.converters.preferred-json-mapper=gson
LIVEKIT_URL: ws://localhost:7880/
LIVEKIT_API_KEY: key1
LIVEKIT_API_SECRET: abcdefghijklmnopqrstuvwxyz123456
RECORDINGS_PATH: ../../openvidu-lk/local-deployment/cluster/recordings

View File

@ -1,348 +0,0 @@
var LivekitClient = window.LivekitClient;
var room;
var myRoomName;
var token;
var nickname;
var numVideos = 0;
var localVideoPublication;
var localAudioPublication;
/* OPENVIDU METHODS */
function joinRoom() {
// --- 0) Change the button ---
document.getElementById('join-btn').disabled = true;
document.getElementById('join-btn').innerHTML = 'Joining...';
const myParticipantName = `Participant${Math.floor(Math.random() * 100)}`;
const myRoomName = $('#roomName').val();
room = new LivekitClient.Room();
room.on(
LivekitClient.RoomEvent.TrackSubscribed,
(track, publication, participant) => {
const element = track.attach();
element.id = track.sid;
document.getElementById('video-container').appendChild(element);
if (track.kind === 'video') {
var audioTrackId;
var videoTrackId;
participant.getTracks().forEach((track) => {
if (track.kind === 'audio') {
audioTrackId = track.trackInfo.sid;
} else if (track.kind === 'video') {
videoTrackId = track.trackInfo.sid;
}
});
addIndividualRecordingButton(element.id, videoTrackId, audioTrackId);
updateNumVideos(1);
}
}
);
// On every new Track destroyed...
room.on(
LivekitClient.RoomEvent.TrackUnsubscribed,
(track, publication, participant) => {
track.detach();
document.getElementById(track.sid)?.remove();
if (track.kind === 'video') {
// removeUserData(participant);
updateNumVideos(-1);
}
}
);
room.on(LivekitClient.RoomEvent.RecordingStatusChanged, (isRecording) => {
console.log('Recording status changed: ' + status);
if (!isRecording) {
listRecordings();
}
});
getToken(myRoomName, myParticipantName).then(async (token) => {
const livekitUrl = getLivekitUrlFromMetadata(token);
try {
await room.connect(livekitUrl, token);
var participantName = $('#user').val();
$('#room-title').text(myRoomName);
$('#join').hide();
$('#room').show();
const [audioPublication, videoPublication] = await Promise.all([
room.localParticipant.setMicrophoneEnabled(true),
room.localParticipant.setCameraEnabled(true),
]);
localVideoPublication = videoPublication;
localAudioPublication = audioPublication;
console.log('Connected to room ' + myRoomName);
const element = videoPublication.track.attach();
element.id = videoPublication.track.sid;
document.getElementById('video-container').appendChild(element);
addIndividualRecordingButton(
element.id,
videoPublication.track.sid,
audioPublication.track.sid
);
updateNumVideos(1);
} catch (error) {
console.warn(
'There was an error connecting to the room:',
error.code,
error.message
);
enableBtn();
}
return false;
});
}
function leaveRoom() {
room.disconnect();
room = null;
$('#video-container').empty();
numVideos = 0;
$('#join').show();
$('#room').hide();
enableBtn();
}
/* OPENVIDU METHODS */
function enableBtn() {
document.getElementById('join-btn').disabled = false;
document.getElementById('join-btn').innerHTML = 'Join!';
}
/* APPLICATION REST METHODS */
function getToken(roomName, participantName) {
return new Promise((resolve, reject) => {
// Video-call chosen by the user
httpRequest(
'POST',
'token',
{ roomName, participantName },
'Error generating token',
(response) => resolve(response.token)
);
});
}
async function httpRequest(method, url, body, errorMsg, successCallback) {
try {
const response = await fetch(url, {
method,
headers: {
'Content-Type': 'application/json',
},
body: method === 'GET' ? undefined : JSON.stringify(body),
});
if (response.ok) {
const data = await response.json();
successCallback(data);
} else {
console.warn(errorMsg);
console.warn('Error: ' + response.statusText);
}
} catch (error) {
console.error(error);
}
}
function startComposedRecording() {
var hasAudio = $('#has-audio-checkbox').prop('checked');
var hasVideo = $('#has-video-checkbox').prop('checked');
httpRequest(
'POST',
'recordings/start',
{
roomName: room.roomInfo.name,
outputMode: 'COMPOSED',
videoOnly: hasVideo && !hasAudio,
audioOnly: hasAudio && !hasVideo,
},
'Start recording WRONG',
(res) => {
console.log(res);
document.getElementById('forceRecordingId').value = res.id;
checkBtnsRecordings();
$('#textarea-http').text(JSON.stringify(res, null, '\t'));
}
);
}
function startIndividualRecording(videoTrackId, audioTrackId) {
return new Promise((resolve, reject) => {
httpRequest(
'POST',
'recordings/start',
{
roomName: room.roomInfo.name,
outputMode: 'INDIVIDUAL',
audioTrackId,
videoTrackId,
},
'Start recording WRONG',
(res) => {
console.log(res);
$('#textarea-http').text(JSON.stringify(res.info, null, '\t'));
resolve(res);
}
);
});
}
function stopRecording(id) {
var forceRecordingId = id ? id : $('#forceRecordingId').val();
httpRequest(
'POST',
'recordings/stop',
{
recordingId: forceRecordingId,
},
'Stop recording WRONG',
(res) => {
console.log(res);
$('#forceRecordingId').val('');
$('#textarea-http').text(JSON.stringify(res.info, null, '\t'));
}
);
}
function listRecordings() {
httpRequest('GET', 'recordings/list', {}, 'List recordings WRONG', (res) => {
console.log(res);
$('#recording-list').empty();
if (res.recordings && res.recordings.length > 0) {
res.recordings.forEach((recording) => {
var li = document.createElement('li');
var a = document.createElement('a');
a.href = recording.path;
a.target = '_blank';
a.appendChild(document.createTextNode(recording.name));
li.appendChild(a);
$('#recording-list').append(li);
});
$('#delete-recordings-btn').prop('disabled', res.recordings.length === 0);
}
});
}
function deleteRecordings() {
httpRequest('DELETE', 'recordings', {}, 'Delete recordings WRONG', (res) => {
console.log(res);
$('#recording-list').empty();
$('#delete-recordings-btn').prop('disabled', true);
$('#textarea-http').text(JSON.stringify(res, null, '\t'));
});
}
/* APPLICATION REST METHODS */
/* APPLICATION BROWSER METHODS */
events = '';
window.onbeforeunload = function () {
// Gracefully leave room
if (room) {
removeUser();
leaveRoom();
}
};
function getLivekitUrlFromMetadata(token) {
if (!token) throw new Error('Trying to get metadata from an empty token');
try {
const base64Url = token.split('.')[1];
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
const jsonPayload = decodeURIComponent(
window
.atob(base64)
.split('')
.map((c) => {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
})
.join('')
);
const payload = JSON.parse(jsonPayload);
if (!payload?.metadata) throw new Error('Token does not contain metadata');
const metadata = JSON.parse(payload.metadata);
return metadata.livekitUrl;
} catch (error) {
throw new Error('Error decoding and parsing token: ' + error);
}
}
function updateNumVideos(i) {
numVideos += i;
$('video').removeClass();
switch (numVideos) {
case 1:
$('video').addClass('two');
break;
case 2:
$('video').addClass('two');
break;
case 3:
$('video').addClass('three');
break;
case 4:
$('video').addClass('four');
break;
}
}
function checkBtnsRecordings() {
if (document.getElementById('forceRecordingId').value === '') {
document.getElementById('buttonStopRecording').disabled = true;
} else {
document.getElementById('buttonStopRecording').disabled = false;
}
}
function addIndividualRecordingButton(elementId, videoTrackId, audioTrackId) {
const div = document.createElement('div');
var button = document.createElement('button');
// button.id = elementId + '-button';
button.className = 'recording-track-button btn btn-sm';
button.innerHTML = 'Record Track';
button.style = 'position: absolute; left: 0; z-index: 1000;';
button.onclick = async () => {
if (button.innerHTML === 'Record Track') {
button.innerHTML = 'Stop Recording';
button.className = 'recording-track-button btn btn-sm btn-danger';
var res = await startIndividualRecording(videoTrackId, audioTrackId);
button.id = res.info.egressId;
} else {
button.innerHTML = 'Record Track';
button.className = 'recording-track-button btn btn-sm';
stopRecording(button.id);
}
};
div.appendChild(button);
var element = document.getElementById(elementId);
element.parentNode.insertBefore(div, element.nextSibling);
}
function clearHttpTextarea() {
$('#textarea-http').text('');
}
/* APPLICATION BROWSER METHODS */

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

View File

@ -1,121 +0,0 @@
<html>
<head>
<title>openvidu-recording-node</title>
<meta name="viewport" content="width=device-width, initial-scale=1" charset="utf-8">
<link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon">
<!-- Bootstrap -->
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha256-k2WSCIexGzOj3Euiig+TlR8gA0EmPjuc79OEeY5L45g="
crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u"
crossorigin="anonymous">
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"
crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<!-- Bootstrap -->
<link rel="styleSheet" href="style.css" type="text/css" media="screen">
<script src="https://cdn.jsdelivr.net/npm/livekit-client/dist/livekit-client.umd.min.js"></script>
<script src="app.js"></script>
<script>
$(document).ready(function () {
$('[data-toggle="tooltip"]').tooltip({
html: true
});
});
</script>
</head>
<body>
<nav class="navbar navbar-default">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="/">
<img class="demo-logo" src="images/openvidu_vert_white_bg_trans_cropped.png" /> Recording Java</a>
<a class="navbar-brand nav-icon" href="https://github.com/OpenVidu/openvidu-livekit-tutorials/tree/master/openvidu-recording-node"
title="GitHub Repository" target="_blank">
<i class="fa fa-github" aria-hidden="true"></i>
</a>
<a class="navbar-brand nav-icon" href="#" title="Documentation"
target="_blank">
<i class="fa fa-book" aria-hidden="true"></i>
</a>
</div>
</div>
</nav>
<div id="main-container" class="container">
<div id="join" class="vertical-center">
<div id="img-div">
<img src="images/openvidu_grey_bg_transp_cropped.png" />
</div>
<div id="join-dialog" class="jumbotron">
<h1>Join a video room</h1>
<form class="form-group" onsubmit="return false">
<p>
<label>Room</label>
<input class="form-control" type="text" id="roomName" value="RoomA" required>
</p>
<p class="text-center">
<button class="btn btn-lg btn-success" id="join-btn" onclick="joinRoom()">Join!</button>
</p>
</form>
<hr>
</div>
</div>
<div id="room" style="display: none">
<div id="room-header">
<h1 id="room-title"></h1>
<input class="btn btn-sm btn-danger" type="button" id="buttonLeaveRoom" onmouseup="leaveRoom()"
value="Leave room">
</div>
<div id="video-container" class="col-md-12"></div>
<div id="recording-btns">
<div class="btns">
<input class="btn btn-md" type="button" id="buttonStartRecording" onmouseup="startComposedRecording()" value="Start Composed recording">
<form>
<label class="checkbox-inline">
<input type="checkbox" id="has-audio-checkbox" checked>Has audio
</label>
<label class="checkbox-inline">
<input type="checkbox" id="has-video-checkbox" checked>Has video
</label>
</form>
</div>
<div class="btns">
<input class="btn btn-md" type="button" id="buttonListRecording" onmouseup="listRecordings()" value="List recordings">
<div class="vertical-separator-bottom"></div>
<input class="btn btn-md btn-danger" type="button" id="buttonStopRecording" onmouseup="stopRecording()" value="Stop recording"
disabled>
<input class="form-control" id="forceRecordingId" type="text" onkeyup="checkBtnsRecordings()">
</div>
<div class="textarea-container" id="textarea-http-container">
<button type="button" class="btn btn-outline-secondary" id="clear-http-btn" onclick="clearHttpTextarea()">Clear</button>
<span>HTTP responses</span>
<textarea id="textarea-http" readonly="true" class="form-control" name="textarea-http"></textarea>
</div>
<div class="textarea-container" id="recordings-list-container">
<button type="button" class="btn btn-md btn-danger" id="delete-recordings-btn" onclick="deleteRecordings()" disabled>Delete All</button>
<span>Recordings list</span>
<ul id="recording-list"></ul>
</div>
</div>
</div>
</div>
<footer class="footer">
<div class="container">
<div class="text-muted">OpenVidu © 2023</div>
<a href="http://www.openvidu.io/" target="_blank">
<img class="openvidu-logo" src="images/openvidu_globe_bg_transp_cropped.png" />
</a>
</div>
</footer>
</body>
</html>

View File

@ -1,474 +0,0 @@
html {
position: relative;
min-height: 100%;
}
body {
min-height: 100%;
}
nav {
height: 50px;
width: 100%;
z-index: 1;
background-color: #4d4d4d !important;
border-color: #4d4d4d !important;
border-top-right-radius: 0 !important;
border-top-left-radius: 0 !important;
}
.navbar-header {
width: 100%;
}
.nav-icon {
padding: 5px 15px 5px 15px;
float: right;
}
nav a {
color: #ccc !important;
}
nav i.fa {
font-size: 40px;
color: #ccc;
}
nav a:hover {
color: #a9a9a9 !important;
}
nav i.fa:hover {
color: #a9a9a9;
}
#main-container {
padding-bottom: 80px;
height: 100%;
margin-top: -70px;
}
.vertical-center {
width: -webkit-fit-content;
width: fit-content;
margin: auto;
}
.vertical-center#not-logged form {
width: -moz-fit-content;
margin: auto;
}
.vertical-center#not-logged table {
width: -moz-fit-content;
margin: auto;
}
.vertical-center table {
margin-top: 3em !important;
}
.horizontal-center {
margin: 0 auto;
}
.form-control {
color: #0088aa;
font-weight: bold;
}
.form-control:focus {
border-color: #0088aa;
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(0, 136, 170, 0.6);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(0, 136, 170, 0.6);
}
input.btn {
font-weight: bold;
}
.btn {
font-weight: bold !important;
}
.btn-success {
background-color: #06d362 !important;
border-color: #06d362;
}
.btn-success:hover {
background-color: #1abd61 !important;
border-color: #1abd61;
}
.btn-info {
background-color: #0088aa !important;
border-color: #0088aa;
}
.btn-info:hover {
background-color: #00708c !important;
border-color: #00708c;
}
.btn-warning {
background-color: #ffcc00 !important;
border-color: #ffcc00;
color: #4d4d4d;
}
.btn-warning:hover {
background-color: #eabb3a !important;
border-color: #eabb3a;
color: #4d4d4d;
}
.btn-warning:active {
color: #4d4d4d;
}
.btn-warning:focus {
color: #4d4d4d;
}
.btn-warning:active:focus {
color: #4d4d4d;
}
.footer {
position: absolute;
bottom: 0;
width: 100%;
height: 60px;
background-color: #4d4d4d;
}
.footer .text-muted {
margin: 20px 0;
float: left;
color: #ccc;
}
.openvidu-logo {
height: 35px;
float: right;
margin: 12px 0;
-webkit-transition: all 0.1s ease-in-out;
-moz-transition: all 0.1s ease-in-out;
-o-transition: all 0.1s ease-in-out;
transition: all 0.1s ease-in-out;
}
.openvidu-logo:hover {
-webkit-filter: grayscale(0.5);
filter: grayscale(0.5);
}
.demo-logo {
margin: 0;
height: 22px;
float: left;
padding-right: 8px;
}
a:hover .demo-logo {
-webkit-filter: brightness(0.7);
filter: brightness(0.7);
}
#join {
padding-top: 40px;
}
#not-logged {
padding-top: 40px;
}
#join-dialog h1 {
color: #4d4d4d;
font-weight: bold;
text-align: center;
}
#join-dialog label {
color: #0088aa;
}
#join-dialog input.btn {
margin-top: 15px;
}
#join-dialog hr {
background: #4d4d4d;
}
#img-div {
text-align: center;
padding-bottom: 3em;
}
#img-div img {
height: 15%;
}
#logged {
width: 100%;
}
#join {
max-width: 700px;
margin: auto;
margin-top: 100px;
}
#room-header {
margin-bottom: 20px;
height: 8%;
margin-top: 70px;
}
#room-header form {
display: inline-block;
}
#room-header input.btn {
float: right;
margin-top: 20px;
margin-left: 5px;
}
#room-title {
display: inline-block;
}
#room-header .form-control {
width: initial;
float: right;
margin: 18px 0px 0px 5px;
}
#video-container {
width: 100%;
max-height: 42%;
display: block;
overflow: hidden;
}
#video-container video.two {
max-width: 50%;
}
#video-container video.three {
max-width: 33.33%;
}
#video-container video.four {
max-width: 25%;
}
#video-container div {
position: absolute;
display: inline-flex;
margin-left: calc(-50% + 15px);
}
#video-container p {
display: inline-block;
background: #f8f8f8;
padding-left: 5px;
padding-right: 5px;
color: #777777;
font-weight: bold;
border-bottom-right-radius: 4px;
}
#video-container p.userName {
float: right;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 0px;
font-weight: lighter;
font-size: 12px;
background: #777777;
color: #f8f8f8;
}
video {
width: auto;
height: auto;
max-height: 100%;
object-fit: scale-down;
}
#room {
height: 100%;
padding-bottom: 80px;
}
#room img {
width: 100%;
height: auto;
display: inline-block;
object-fit: contain;
vertical-align: baseline;
}
#room #video-container img {
position: relative;
float: left;
width: 50%;
cursor: pointer;
object-fit: cover;
height: 180px;
}
table i {
cursor: pointer;
margin-left: 1em;
}
#tooltip-div {
text-align: left;
}
#tooltip-div hr {
margin: 5px 0;
}
#login-info {
text-align: right;
}
#login-info form {
display: inline;
}
#login-info div {
display: inline;
margin-right: 1em;
}
#name-user {
font-weight: bold;
}
#recording-btns {
display: inline-block;
padding-left: 15px;
padding-top: 20px;
width: 100%;
height: 40%;
}
#recording-btns .btns {
margin-top: 10px;
}
#recording-btns .btns .form-control {
width: initial;
display: inline;
}
#recording-btns .btns form {
display: inline;
margin-left: 5px;
}
#recording-btns textarea {
height: 100%;
}
.textarea-container {
position: relative;
display: inline-block;
height: 74%;
margin-top: 20px;
resize: none;
}
#textarea-http-container {
width: 59%;
}
#recordings-list-container {
width: 39%;
overflow: auto;
}
.textarea-container button {
position: absolute;
top: 1px;
right: 1px;
z-index: 1;
}
.textarea-container span {
position: absolute;
bottom: 1px;
right: 1px;
padding: 3px;
border-bottom-right-radius: 4px;
z-index: 1;
color: #a5a5a5;
background-color: #ededee;
font-weight: 600;
}
.textarea-container textarea {
height: 100%;
resize: none;
}
.vertical-separator-bottom {
width: 2px;
height: 34px;
display: inline-block;
background-color: #cbcbcb;
margin: 0 8px 0 8px;
margin-bottom: -12px;
}
.vertical-separator-top {
width: 2px;
height: 30px;
background-color: #cbcbcb;
margin: 20px 8px 0 15px;
float: right;
}
/* xs ans md screen resolutions*/
@media screen and (max-width: 991px) {
#join {
padding-top: inherit;
}
#not-logged {
padding-top: inherit;
}
.container .navbar-header {
margin-right: 0 !important;
margin-left: 0 !important;
}
.nav-icon {
padding: 9px 8px 9px 8px;
}
nav i.fa {
font-size: 32px;
}
.vertical-center {
padding-top: 10px;
}
#img-div {
padding-bottom: 1em;
}
#img-div img {
height: 10%;
}
}

View File

@ -1,24 +0,0 @@
# Compiled class file
*.class
# Log file
*.log
# BlueJ files
*.ctxt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
target/

View File

@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -1,14 +0,0 @@
[![License badge](https://img.shields.io/badge/license-Apache2-orange.svg)](http://www.apache.org/licenses/LICENSE-2.0)
[![OpenVidu Tests](https://github.com/OpenVidu/openvidu/actions/workflows/openvidu-ce-test.yml/badge.svg)](https://github.com/OpenVidu/openvidu/actions/workflows/openvidu-ce-test.yml)
[![Documentation Status](https://readthedocs.org/projects/openvidu/badge/?version=stable)](https://docs.openvidu.io/en/stable/?badge=stable)
[![Docker badge](https://img.shields.io/docker/pulls/openvidu/openvidu-server-kms.svg)](https://hub.docker.com/r/openvidu/openvidu-server-kms)
[![Support badge](https://img.shields.io/badge/support-sof-yellowgreen.svg)](https://openvidu.discourse.group/)
[![][OpenViduLogo]](http://openvidu.io)
openvidu-roles-java
===
Visit [docs.openvidu.io/en/stable/tutorials/openvidu-roles-java/](http://docs.openvidu.io/en/stable/tutorials/openvidu-roles-java/)
[OpenViduLogo]: https://secure.gravatar.com/avatar/5daba1d43042f2e4e85849733c8e5702?s=120

View File

@ -1,26 +0,0 @@
FROM maven:3.6.3 as build
WORKDIR /basic-webinar
COPY ./pom.xml pom.xml
COPY ./src/main src/main
RUN mvn clean install
RUN mvn -o package
FROM alpine:3.11
RUN apk update && \
apk add openjdk11-jre && \
rm -rf /var/cache/apk/*
# Install basic-webinar
RUN mkdir -p /opt/openvidu-basic-webinar
COPY --from=build /basic-webinar/target/openvidu-roles-java-*.jar /opt/openvidu-basic-webinar/openvidu-basic-webinar.jar
# Entrypoint
COPY ./docker/entrypoint.sh /usr/local/bin
RUN chmod +x /usr/local/bin/entrypoint.sh
CMD /usr/local/bin/entrypoint.sh

View File

@ -1,8 +0,0 @@
#!/bin/bash
if [ $# -eq 0 ]; then
echo "No version argument provided. Usage: \"./create_image.sh <IMAGE_NAME>\""
exit 1
fi
pushd ../
docker build --pull --no-cache --rm=true -f docker/Dockerfile -t "$1" .

View File

@ -1,13 +0,0 @@
#!/bin/sh
[ ! -z "${OPENVIDU_URL}" ] && echo "OPENVIDU_URL: ${OPENVIDU_URL}" || echo "OPENVIDU_URL: default"
[ ! -z "${OPENVIDU_SECRET}" ] && echo "OPENVIDU_SECRET: ${OPENVIDU_SECRET}" || echo "OPENVIDU_SECRET: default"
[ ! -z "${SERVER_PORT}" ] && echo "SERVER_PORT: ${SERVER_PORT}" || echo "SERVER_PORT: default"
# Run Application
JAVA_PROPERTIES="-Djava.security.egd=file:/dev/./urandom"
[ ! -z "${OPENVIDU_URL}" ] && JAVA_PROPERTIES=" ${JAVA_PROPERTIES} -Dopenvidu.url=${OPENVIDU_URL}"
[ ! -z "${OPENVIDU_SECRET}" ] && JAVA_PROPERTIES=" ${JAVA_PROPERTIES} -Dopenvidu.secret=${OPENVIDU_SECRET}"
[ ! -z "${SERVER_PORT}" ] && JAVA_PROPERTIES=" ${JAVA_PROPERTIES} -Dserver.port=${SERVER_PORT}"
java ${JAVA_PROPERTIES} -jar /opt/openvidu-basic-webinar/openvidu-basic-webinar.jar

View File

@ -1,44 +0,0 @@
events {
worker_connections 512;
}
http {
upstream openvidu-deployment {
server host.docker.internal:4443;
}
upstream client-application {
server host.docker.internal:5000;
}
server {
listen 443 ssl;
ssl_certificate /etc/nginx/certs/cert.pem;
ssl_certificate_key /etc/nginx/certs/key.pem;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Proto https;
proxy_headers_hash_bucket_size 512;
proxy_redirect off;
# Websockets
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# OpenVidu deployment API
location /openvidu/api {
proxy_pass http://openvidu-deployment;
}
# OpenVidu WebSocket
location ~ /openvidu$ {
proxy_pass http://openvidu-deployment;
}
# Client application requests
location / {
proxy_pass https://client-application;
}
}
}

View File

@ -1,62 +0,0 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>io.openvidu</groupId>
<artifactId>openvidu-roles-java</artifactId>
<version>2.27.0</version>
<packaging>jar</packaging>
<name>openvidu-roles-java</name>
<url>https://github.com/OpenVidu/openvidu-tutorials/tree/master/openvidu-roles-java</url>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
<start-class>io.openvidu.js.java.App</start-class>
<docker.image.prefix>openvidu</docker.image.prefix>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20231013</version>
</dependency>
<dependency>
<groupId>io.livekit</groupId>
<artifactId>livekit-server</artifactId>
<version>0.5.7</version>
</dependency>
</dependencies>
</project>

View File

@ -1,13 +0,0 @@
package io.openvidu.js.java;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class App
{
public static void main( String[] args )
{
SpringApplication.run(App.class, args);
}
}

View File

@ -1,76 +0,0 @@
package io.openvidu.js.java;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.servlet.http.HttpSession;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@CrossOrigin(origins = "*")
@RestController
public class LoginController {
public class MyUser {
String name;
String pass;
String role;
public MyUser(String name, String pass, String role) {
this.name = name;
this.pass = pass;
this.role = role;
}
}
public static Map<String, MyUser> users = new ConcurrentHashMap<>();
public LoginController() {
users.put("publisher1", new MyUser("publisher1", "pass", "PUBLISHER"));
users.put("publisher2", new MyUser("publisher2", "pass", "PUBLISHER"));
users.put("subscriber", new MyUser("subscriber", "pass", "SUBSCRIBER"));
}
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody(required = true) Map<String, String> params, HttpSession httpSession) {
String user = params.get("user");
String pass = params.get("pass");
Map<String, String> response = new HashMap<String, String>();
if (login(user, pass)) {
// Successful login
// Validate session and return OK
// Value stored in req.session allows us to identify the user in future requests
httpSession.setAttribute("loggedUser", user);
return new ResponseEntity<>(new HashMap<String, String>(), HttpStatus.OK);
} else {
// Credentials are NOT valid
// Invalidate session and return error
httpSession.invalidate();
response.put("message", "Invalid credentials");
return new ResponseEntity<>(response, HttpStatus.UNAUTHORIZED);
}
}
@PostMapping("/logout")
public ResponseEntity<Void> logout(HttpSession session) {
System.out.println("'" + session.getAttribute("loggedUser") + "' has logged out");
session.invalidate();
return new ResponseEntity<>(HttpStatus.OK);
}
private boolean login(String user, String pass) {
if(user.isEmpty() || pass.isEmpty()) return false;
return (users.containsKey(user) && users.get(user).pass.equals(pass));
}
}

View File

@ -1,85 +0,0 @@
package io.openvidu.js.java;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpSession;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import io.livekit.server.AccessToken;
import io.livekit.server.CanPublish;
import io.livekit.server.CanSubscribe;
import io.livekit.server.RoomJoin;
import io.livekit.server.RoomName;
import io.openvidu.js.java.LoginController.MyUser;
@CrossOrigin(origins = "*")
@RestController
public class RoomController {
@Value("${LIVEKIT_URL}")
private String LIVEKIT_URL;
@Value("${LIVEKIT_API_KEY}")
private String LIVEKIT_API_KEY;
@Value("${LIVEKIT_API_SECRET}")
private String LIVEKIT_API_SECRET;
/**
* @param params The JSON object with roomName and participantName
* @return The JWT token
*/
@PostMapping("/token")
public ResponseEntity<Map<String, String>> getToken(@RequestBody(required = true) Map<String, String> params, HttpSession httpSession) {
String roomName = params.get("roomName");
String participantName = params.get("participantName");
Map<String, String> response = new HashMap<String, String>();
if(!isUserLogged(httpSession)) {
response.put("message", "User is not logged");
return new ResponseEntity<>(response, HttpStatus.UNAUTHORIZED);
}
if(roomName == null || participantName == null) {
response.put("message", "roomName and participantName are required");
return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
}
MyUser user = LoginController.users.get(httpSession.getAttribute("loggedUser"));
Boolean canPublish = user.role.equals("PUBLISHER");
// By default, tokens expire 6 hours after generation.
// You may override this by using token.setTtl(long millis).
AccessToken token = new AccessToken(LIVEKIT_API_KEY, LIVEKIT_API_SECRET);
token.setName(participantName);
token.setIdentity(participantName);
JSONObject metadata = new JSONObject();
metadata.put("livekitUrl", LIVEKIT_URL);
metadata.put("user", user.name);
metadata.put("role", user.role);
// add metadata to the token, which will be available in the participant's metadata
token.setMetadata(metadata.toString());
token.addGrants(new RoomJoin(true), new RoomName(roomName), new CanPublish(canPublish), new CanSubscribe(true));
response.put("token", token.toJwt());
return new ResponseEntity<>(response, HttpStatus.OK);
}
private boolean isUserLogged(HttpSession httpSession) {
return httpSession != null && httpSession.getAttribute("loggedUser") != null;
}
}

View File

@ -1,11 +0,0 @@
server.port: 5000
server.ssl.enabled: true
server.ssl.key-store: classpath:openvidu-selfsigned.jks
server.ssl.key-store-password: openvidu
server.ssl.key-store-type: JKS
server.ssl.key-alias: openvidu-selfsigned
LIVEKIT_URL: ws://localhost:7880/
LIVEKIT_API_KEY: devkey
LIVEKIT_API_SECRET: secret

View File

@ -1,274 +0,0 @@
var LivekitClient = window.LivekitClient;
var room;
var myRoomName;
var token;
var nickname;
/* OPENVIDU METHODS */
function joinRoom() {
document.getElementById('join-btn').disabled = true;
document.getElementById('join-btn').innerHTML = 'Joining...';
const myParticipantName = $('#myParticipantName').val();
const myRoomName = $('#myRoomName').val();
room = new LivekitClient.Room();
room.on(
LivekitClient.RoomEvent.TrackSubscribed,
(track, publication, participant) => {
const element = track.attach();
element.id = track.sid;
element.className = 'removable';
document.getElementById('video-container').appendChild(element);
if (track.kind === 'video') {
var participantNickname;
try {
participantNickname = JSON.parse(participant.metadata).nickname;
} catch (error) {
console.warn('Error parsing participant metadata: ' + error);
}
appendUserData(element, participant.identity, participantNickname);
}
}
);
// On every new Track destroyed...
room.on(
LivekitClient.RoomEvent.TrackUnsubscribed,
(track, publication, participant) => {
track.detach();
document.getElementById(track.sid)?.remove();
if (track.kind === 'video') {
removeUserData(participant);
}
}
);
getToken(myRoomName, myParticipantName).then((token) => {
const livekitUrl = getLivekitUrlFromMetadata(token);
room
.connect(livekitUrl, token)
.then(async () => {
var participantName = $('#user').val();
$('#room-title').text(myRoomName);
$('#join').hide();
$('#room').show();
const canPublish = room.localParticipant.permissions.canPublish;
if (canPublish) {
const [microphonePublication, cameraPublication] = await Promise.all([
room.localParticipant.setMicrophoneEnabled(true),
room.localParticipant.setCameraEnabled(true),
]);
const element = cameraPublication.track.attach();
element.className = 'removable';
document.getElementById('video-container').appendChild(element);
initMainVideo(element, myParticipantName, nickname);
appendUserData(element, myParticipantName, nickname);
} else {
initMainVideoThumbnail();
}
})
.catch((error) => {
console.warn(
'There was an error connecting to the room:',
error.code,
error.message
);
enableBtn();
});
});
return false;
}
function leaveRoom() {
room.disconnect();
room = null;
// Removing all HTML elements with the user's nicknames
cleanRoomView();
$('#join').show();
$('#room').hide();
enableBtn();
}
/* OPENVIDU METHODS */
function enableBtn() {
document.getElementById('join-btn').disabled = false;
document.getElementById('join-btn').innerHTML = 'Join!';
}
/* APPLICATION REST METHODS */
function logIn() {
nickname = $('#user').val();
var pass = $('#pass').val();
httpPostRequest(
'login',
{ user: nickname, pass },
'Login WRONG',
(response) => {
$('#name-user').text(nickname);
$('#not-logged').hide();
$('#logged').show();
// Random myParticipantName and room
$('#myRoomName').val('Room ' + Math.floor(Math.random() * 10));
$('#myParticipantName').val(
'Participant ' + Math.floor(Math.random() * 100)
);
}
);
}
function logOut() {
httpPostRequest('logout', {}, 'Logout WRONG', (response) => {
$('#not-logged').show();
$('#logged').hide();
});
enableBtn();
}
function getToken(roomName, participantName) {
return new Promise((resolve, reject) => {
// Video-call chosen by the user
httpPostRequest(
'token',
{ roomName, participantName },
'Error generating token',
(response) => resolve(response.token)
);
});
}
async function httpPostRequest(url, body, errorMsg, successCallback) {
try {
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
});
if (response.ok) {
const data = await response.json();
successCallback(data);
} else {
console.warn(errorMsg);
console.warn('Error: ', response);
}
} catch (error) {
console.error(error);
}
}
/* APPLICATION REST METHODS */
/* APPLICATION BROWSER METHODS */
window.onbeforeunload = () => {
if (room) {
leaveRoom();
}
logOut();
};
function getLivekitUrlFromMetadata(token) {
if (!token) throw new Error('Trying to get metadata from an empty token');
try {
const base64Url = token.split('.')[1];
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
const jsonPayload = decodeURIComponent(
window
.atob(base64)
.split('')
.map((c) => {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
})
.join('')
);
const payload = JSON.parse(jsonPayload);
if (!payload?.metadata) throw new Error('Token does not contain metadata');
const metadata = JSON.parse(payload.metadata);
return metadata.livekitUrl;
} catch (error) {
throw new Error('Error decoding and parsing token: ' + error);
}
}
function appendUserData(videoElement, participantName, nickname) {
var dataNode = document.createElement('div');
dataNode.className = 'removable';
dataNode.id = 'data-' + participantName;
dataNode.innerHTML = `
<p class='nickname'>${nickname}</p>
<p class='participantName'>${participantName}</p>
`;
videoElement.parentNode.insertBefore(dataNode, videoElement.nextSibling);
addClickListener(videoElement, participantName);
}
function removeUserData(participant) {
var dataNode = document.getElementById('data-' + participant.identity);
dataNode?.parentNode.removeChild(dataNode);
}
function removeAllUserData() {
var elementsToRemove = document.getElementsByClassName('removable');
while (elementsToRemove[0]) {
elementsToRemove[0].parentNode.removeChild(elementsToRemove[0]);
}
}
function cleanMainVideo() {
$('#main-video video').get(0).srcObject = null;
$('#main-video p').each(function () {
$(this).html('');
});
}
function addClickListener(videoElement, clientData, serverData) {
videoElement.addEventListener('click', function () {
var mainVideo = $('#main-video video').get(0);
if (mainVideo.srcObject !== videoElement.srcObject) {
$('#main-video').fadeOut('fast', () => {
$('#main-video p.nickname').html(clientData);
$('#main-video p.participantName').html(serverData);
mainVideo.srcObject = videoElement.srcObject;
$('#main-video').fadeIn('fast');
});
}
});
}
function initMainVideo(videoElement, participantName, nickname) {
$('#main-video video').get(0).srcObject = videoElement.srcObject;
$('#main-video p.nickname').html(nickname);
$('#main-video p.participantName').html(participantName);
}
function initMainVideoThumbnail() {
$('#main-video video').css(
'background',
"url('images/subscriber-msg.jpg') round"
);
}
function cleanRoomView() {
removeAllUserData();
cleanMainVideo();
$('#main-video video').css('background', '');
}
/* APPLICATION BROWSER METHODS */

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

View File

@ -1,210 +0,0 @@
<html>
<head>
<title>openvidu-roles-node</title>
<meta
name="viewport"
content="width=device-width, initial-scale=1"
charset="utf-8"
/>
<link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon" />
<!-- Bootstrap -->
<script
src="https://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
crossorigin="anonymous"
></script>
<link
rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u"
crossorigin="anonymous"
/>
<script
src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"
integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"
crossorigin="anonymous"
></script>
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"
/>
<!-- Bootstrap -->
<link rel="styleSheet" href="style.css" type="text/css" media="screen" />
<script src="https://cdn.jsdelivr.net/npm/livekit-client/dist/livekit-client.umd.min.js"></script>
<script src="app.js"></script>
<script>
$(document).ready(function () {
$('[data-toggle="tooltip"]').tooltip({
html: true,
});
});
</script>
</head>
<body>
<nav class="navbar navbar-default">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="/"
><img
class="demo-logo"
src="images/openvidu_vert_white_bg_trans_cropped.png"
/>
Roles Node</a
>
<a
class="navbar-brand nav-icon"
href="https://github.com/OpenVidu/openvidu-tutorials/tree/master/openvidu-roles-node"
title="GitHub Repository"
target="_blank"
><i class="fa fa-github" aria-hidden="true"></i
></a>
<a
class="navbar-brand nav-icon"
href="http://www.docs.openvidu.io/en/stable/tutorials/openvidu-roles-node/"
title="Documentation"
target="_blank"
><i class="fa fa-book" aria-hidden="true"></i
></a>
</div>
</div>
</nav>
<div id="main-container" class="container">
<div id="not-logged" class="vertical-center">
<div id="img-div">
<img src="images/openvidu_grey_bg_transp_cropped.png" />
</div>
<form class="form-group jumbotron" onsubmit="return false">
<p>
<label>User</label
><input class="form-control" type="text" id="user" required />
</p>
<p>
<label>Password</label
><input class="form-control" type="password" id="pass" required />
</p>
<p class="text-center">
<button class="btn btn-lg btn-info" onclick="logIn()">
Log in
</button>
</p>
</form>
<table class="table">
<tr>
<th>User</th>
<th>Password</th>
<th>
Role<i
data-toggle="tooltip"
data-placement="bottom"
title=""
data-original-title="<div id='tooltip-div'>PUBLISHER<div>Send and receive media<hr></div>SUBSCRIBER<div>Receive media</div></div>"
class="glyphicon glyphicon-info-sign"
></i>
</th>
</tr>
<tr>
<td>publisher1</td>
<td>pass</td>
<td>PUBLISHER</td>
</tr>
<tr>
<td>publisher2</td>
<td>pass</td>
<td>PUBLISHER</td>
</tr>
<tr>
<td>subscriber</td>
<td>pass</td>
<td>SUBSCRIBER</td>
</tr>
</table>
</div>
<div id="logged" hidden>
<div id="join" class="vertical-center">
<div id="img-div">
<img src="images/openvidu_grey_bg_transp_cropped.png" />
</div>
<div id="join-dialog" class="jumbotron">
<h1>Join a video room</h1>
<form class="form-group" onsubmit="return false">
<p>
<label>Participant</label>
<input
class="form-control"
type="text"
id="myParticipantName"
required
/>
</p>
<p>
<label>Room</label>
<input
class="form-control"
type="text"
id="myRoomName"
required
/>
</p>
<p class="text-center">
<button
class="btn btn-lg btn-success"
id="join-btn"
onclick="joinRoom()"
>
Join!
</button>
</p>
</form>
<hr />
<div id="login-info">
<div>Logged as <span id="name-user"></span></div>
<button
id="logout-btn"
class="btn btn-warning"
onclick="logOut()"
>
Log out
</button>
</div>
</div>
</div>
<div id="room" style="display: none">
<div id="room-header">
<h1 id="room-title"></h1>
<input
class="btn btn-large btn-danger"
type="button"
id="buttonLeaveRoom"
onmouseup="leaveRoom()"
value="Leave room"
/>
</div>
<div id="main-video" class="col-md-6">
<p class="nickname"></p>
<p class="participantName"></p>
<video autoplay playsinline="true"></video>
</div>
<div id="video-container" class="col-md-6"></div>
</div>
</div>
</div>
<footer class="footer">
<div class="container">
<div class="text-muted">OpenVidu © 2023</div>
<a href="http://www.openvidu.io/" target="_blank"
><img
class="openvidu-logo"
src="images/openvidu_globe_bg_transp_cropped.png"
/></a>
</div>
</footer>
</body>
</html>

View File

@ -1,382 +0,0 @@
html {
position: relative;
min-height: 100%;
}
nav {
height: 50px;
width: 100%;
z-index: 1;
background-color: #4d4d4d !important;
border-color: #4d4d4d !important;
border-top-right-radius: 0 !important;
border-top-left-radius: 0 !important;
}
.navbar {
margin-bottom: 0px !important;
}
.navbar-header {
width: 100%;
}
.nav-icon {
padding: 5px 15px 5px 15px;
float: right;
}
nav a {
color: #ccc !important;
}
nav i.fa {
font-size: 40px;
color: #ccc;
}
nav a:hover {
color: #a9a9a9 !important;
}
nav i.fa:hover {
color: #a9a9a9;
}
#main-container {
padding-bottom: 80px;
display: contents;
}
.vertical-center {
width: -webkit-fit-content;
width: fit-content;
margin: auto;
}
.vertical-center#not-logged form {
width: -moz-fit-content;
margin: auto;
}
.vertical-center#not-logged table {
width: -moz-fit-content;
margin: auto;
}
.vertical-center table {
margin-top: 3em !important;
}
.horizontal-center {
margin: 0 auto;
}
.form-control {
color: #0088aa;
font-weight: bold;
}
.form-control:focus {
border-color: #0088aa;
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(0, 136, 170, 0.6);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(0, 136, 170, 0.6);
}
input.btn {
font-weight: bold;
}
.btn {
font-weight: bold !important;
}
.btn-success {
background-color: #06d362 !important;
border-color: #06d362;
}
.btn-success:hover {
background-color: #1abd61 !important;
border-color: #1abd61;
}
.btn-info {
background-color: #0088aa !important;
border-color: #0088aa;
}
.btn-info:hover {
background-color: #00708c !important;
border-color: #00708c;
}
.btn-warning {
background-color: #ffcc00 !important;
border-color: #ffcc00;
color: #4d4d4d;
}
.btn-warning:hover {
background-color: #eabb3a !important;
border-color: #eabb3a;
color: #4d4d4d;
}
.btn-warning:active {
color: #4d4d4d;
}
.btn-warning:focus {
color: #4d4d4d;
}
.btn-warning:active:focus {
color: #4d4d4d;
}
.footer {
position: absolute;
bottom: 0;
width: 100%;
height: 60px;
background-color: #4d4d4d;
}
.footer .text-muted {
margin: 20px 0;
float: left;
color: #ccc;
}
.openvidu-logo {
height: 35px;
float: right;
margin: 12px 0;
-webkit-transition: all 0.1s ease-in-out;
-moz-transition: all 0.1s ease-in-out;
-o-transition: all 0.1s ease-in-out;
transition: all 0.1s ease-in-out;
}
.openvidu-logo:hover {
-webkit-filter: grayscale(0.5);
filter: grayscale(0.5);
}
.demo-logo {
margin: 0;
height: 22px;
float: left;
padding-right: 8px;
}
a:hover .demo-logo {
-webkit-filter: brightness(0.7);
filter: brightness(0.7);
}
#join {
padding-top: 40px;
}
#not-logged {
padding-top: 40px;
}
#join-dialog h1 {
color: #4d4d4d;
font-weight: bold;
text-align: center;
}
#join-dialog label {
color: #0088aa;
}
#join-dialog input.btn {
margin-top: 15px;
}
#join-dialog hr {
background: #4d4d4d;
}
#img-div {
text-align: center;
padding-bottom: 3em;
}
#img-div img {
height: 15%;
}
#logged {
width: 100%;
}
#join {
max-width: 700px;
margin: auto;
}
#room-header {
margin-bottom: 20px;
}
#room-header form {
display: inline;
}
#room-title {
display: inline-block;
}
#buttonLeaveRoom {
float: right;
margin-top: 20px;
}
#video-container video {
position: relative;
float: left;
width: 50%;
cursor: pointer;
}
#video-container div {
float: left;
width: 50%;
position: relative;
margin-left: -50%;
}
#video-container p {
display: inline-block;
background: #f8f8f8;
padding-left: 5px;
padding-right: 5px;
color: #777777;
font-weight: bold;
border-bottom-right-radius: 4px;
}
#video-container p.nickname {
float: right;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 0px;
font-weight: lighter;
font-size: 12px;
background: #777777;
color: #f8f8f8;
}
video {
width: 100%;
height: auto;
}
#main-video p {
position: absolute;
display: inline-block;
background: #f8f8f8;
padding-left: 5px;
padding-right: 5px;
font-size: 22px;
color: #777777;
font-weight: bold;
border-bottom-right-radius: 4px;
}
#main-video p.nickname {
position: absolute;
right: 0;
font-size: 16px !important;
margin-right: 15px;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 0px;
font-weight: lighter;
font-size: 12px;
background: #777777;
color: #f8f8f8;
}
#room img {
width: 100%;
height: auto;
display: inline-block;
object-fit: contain;
vertical-align: baseline;
}
#room #video-container img {
position: relative;
float: left;
width: 50%;
cursor: pointer;
object-fit: cover;
height: 180px;
}
table i {
cursor: pointer;
margin-left: 1em;
}
#tooltip-div {
text-align: left;
}
#tooltip-div hr {
margin: 5px 0;
}
#login-info {
text-align: right;
}
#login-info form {
display: inline;
}
#login-info div {
display: inline;
margin-right: 1em;
}
#name-user {
font-weight: bold;
}
/* xs ans md screen resolutions*/
@media screen and (max-width: 991px) {
#join {
padding-top: inherit;
}
#not-logged {
padding-top: inherit;
}
.container .navbar-header {
margin-right: 0 !important;
margin-left: 0 !important;
}
.nav-icon {
padding: 9px 8px 9px 8px;
}
nav i.fa {
font-size: 32px;
}
.vertical-center {
padding-top: 10px;
}
#img-div {
padding-bottom: 1em;
}
#img-div img {
height: 10%;
}
}

View File

@ -1,38 +0,0 @@
package io.openvidu.js.java.test;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
/**
* Unit test for simple App.
*/
public class AppTest
extends TestCase
{
/**
* Create the test case
*
* @param testName name of the test case
*/
public AppTest( String testName )
{
super( testName );
}
/**
* @return the suite of tests being tested
*/
public static Test suite()
{
return new TestSuite( AppTest.class );
}
/**
* Rigourous Test :-)
*/
public void testApp()
{
assertTrue( true );
}
}

View File

@ -1,60 +0,0 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Typescript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.vscode/

View File

@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -1,14 +0,0 @@
[![License badge](https://img.shields.io/badge/license-Apache2-orange.svg)](http://www.apache.org/licenses/LICENSE-2.0)
[![OpenVidu Tests](https://github.com/OpenVidu/openvidu/actions/workflows/openvidu-ce-test.yml/badge.svg)](https://github.com/OpenVidu/openvidu/actions/workflows/openvidu-ce-test.yml)
[![Documentation Status](https://readthedocs.org/projects/openvidu/badge/?version=stable)](https://docs.openvidu.io/en/stable/?badge=stable)
[![Docker badge](https://img.shields.io/docker/pulls/openvidu/openvidu-server-kms.svg)](https://hub.docker.com/r/openvidu/openvidu-server-kms)
[![Support badge](https://img.shields.io/badge/support-sof-yellowgreen.svg)](https://openvidu.discourse.group/)
[![][OpenViduLogo]](http://openvidu.io)
openvidu-roles-node
===
Visit [docs.openvidu.io/en/stable/tutorials/openvidu-roles-node/](http://docs.openvidu.io/en/stable/tutorials/openvidu-roles-node/)
[OpenViduLogo]: https://secure.gravatar.com/avatar/5daba1d43042f2e4e85849733c8e5702?s=120

View File

@ -1,13 +0,0 @@
FROM node:16-alpine3.16
# Copy openvidu-roles-node
COPY . /opt/openvidu-roles-node
# Install openvidu-roles-node dependencies
RUN npm --prefix /opt/openvidu-roles-node install
WORKDIR /opt/openvidu-roles-node
COPY docker/entrypoint.sh .
ENTRYPOINT [ "./entrypoint.sh" ]

View File

@ -1,8 +0,0 @@
#!/bin/bash
if [ $# -eq 0 ]; then
echo "No version argument provided. Usage: \"./create_image.sh <IMAGE_NAME>\""
exit 1
fi
pushd ../
docker build --pull --no-cache --rm=true -f docker/Dockerfile -t "$1" .

View File

@ -1,3 +0,0 @@
#!/bin/sh
exec node server.js "$*"

View File

@ -1,44 +0,0 @@
events {
worker_connections 512;
}
http {
upstream openvidu-deployment {
server host.docker.internal:4443;
}
upstream client-application {
server host.docker.internal:5000;
}
server {
listen 443 ssl;
ssl_certificate /etc/nginx/certs/cert.pem;
ssl_certificate_key /etc/nginx/certs/key.pem;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Proto https;
proxy_headers_hash_bucket_size 512;
proxy_redirect off;
# Websockets
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# OpenVidu deployment API
location /openvidu/api {
proxy_pass http://openvidu-deployment;
}
# OpenVidu WebSocket
location ~ /openvidu$ {
proxy_pass http://openvidu-deployment;
}
# Client application requests
location / {
proxy_pass https://client-application;
}
}
}

View File

@ -1,21 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIDgzCCAmugAwIBAgIJALDBPXTrlAhlMA0GCSqGSIb3DQEBCwUAMFgxCzAJBgNV
BAYTAkVTMRMwEQYDVQQIDApTb21lLVN0YXRlMREwDwYDVQQKDAhPcGVuVmlkdTEh
MB8GCSqGSIb3DQEJARYSb3BlbnZpZHVAZ21haWwuY29tMB4XDTE3MDUzMDE1MjQx
N1oXDTI3MDUyODE1MjQxN1owWDELMAkGA1UEBhMCRVMxEzARBgNVBAgMClNvbWUt
U3RhdGUxETAPBgNVBAoMCE9wZW5WaWR1MSEwHwYJKoZIhvcNAQkBFhJvcGVudmlk
dUBnbWFpbC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDDbK/i
LN80IBnGUbzA4AFl4KQEX6RCreythnfOSnIcQSTP+KjesZMFgsGV0LteDxeCJ+kq
4YoS+CW7ojvOEz3xjCgo4tFdevz8ZoeO0RBQIbARbPako4OXC6vWs6LHwCR0aDWo
9HfS1Uusb8g77csRPRlNA3DGR8dcRTiEBdfHS6Jh/7V7XiDlaxPXj+iIY8PyCqOf
gv4clDt17R+dendDsgYxbmZaodrppNocMQIyUaDIwI4DZOa8nQYk9uuUhkiAFAQB
O642bx6NI0qDu5KgtMmbaS6s+rMnrL8eeZOicqff6XoC6tk6GE8Fo4iYkxp2gAiT
sigaSwpNRWhupISVAgMBAAGjUDBOMB0GA1UdDgQWBBTi3sXqpf42vUNehFU6y4Pn
PTJFRDAfBgNVHSMEGDAWgBTi3sXqpf42vUNehFU6y4PnPTJFRDAMBgNVHRMEBTAD
AQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBfl0C7XAbPdJzCjCsboMTzfC2B4uwspbST
MbUGnmNkTwzeOOsrLQmlCznKGprYV9vGR0MwBzYw2swWCzg/2MLN8swW7NF9gMkK
K61AANxT0qZV/aZhdM7W//pHJoQYsBsQT7cuTrL/VB/niD81uMA/mSWyXIn8KDIy
CPY6jiZ4qiIJnQWhWm1Cazv6O7wjDisvB1cCJhDvBv42KkFwtigt5MQnBEGOI2LD
iKCkXfj33E5B6n0sEel68WgYi6rx2tsR9lzAjCRF+jgNd7FfeUi999m7ykgEACR5
OYRqkVcIXz30r9RxQyFqZLNzyO9oaVZpex8ZbWwEzNXG2ccddnMJ
-----END CERTIFICATE-----

View File

@ -1,27 +0,0 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAw2yv4izfNCAZxlG8wOABZeCkBF+kQq3srYZ3zkpyHEEkz/io
3rGTBYLBldC7Xg8XgifpKuGKEvglu6I7zhM98YwoKOLRXXr8/GaHjtEQUCGwEWz2
pKODlwur1rOix8AkdGg1qPR30tVLrG/IO+3LET0ZTQNwxkfHXEU4hAXXx0uiYf+1
e14g5WsT14/oiGPD8gqjn4L+HJQ7de0fnXp3Q7IGMW5mWqHa6aTaHDECMlGgyMCO
A2TmvJ0GJPbrlIZIgBQEATuuNm8ejSNKg7uSoLTJm2kurPqzJ6y/HnmTonKn3+l6
AurZOhhPBaOImJMadoAIk7IoGksKTUVobqSElQIDAQABAoIBAGr/2HFjFjbpGJOw
b0O/oqRQUh2e7EYiCoOcK37E3iPAO1KvmG6OFayfwjSwG9bNNpbqGU2EPeBTA/3v
PwV/HZxinB5+yhl/3IKp9LDqoR7uwwNXgNf2O3d5SXX91zO9bXhbEn5WlEDYzl00
uxKtCVF//ZlgN+AoruxDbkVDGbkhKHGzvqOW+BWwbYHPOttQ9TQx2ss5+DekpDFV
/FuvXGOcSSV/N+WbDwsqUiM8ovkcflEgQjZYlAY4Ro2U2buf3fKEmy1jxSznNp30
QA4WyYypyS8Hz6S/F4nsjtS3ufCurrWBv4vt2qB+8tH//07NUacjQWZdvEAtaJ+G
IyJgsOUCgYEA38b2SQiw74fwoK6so2XdpCzOObLQBOT1aeZQjt3v8XZ0MPzwWJZm
VkMBmolcEw24xA3jHhOQYafAGjRdaKxlRbJyGJqwENfAs7hO5JSLLWWXsorJQcb7
1OVTFPt7NZopx2Kw7kGGuj9884w57cSl9lsuMptqk8W7pbTo5opOfl8CgYEA35CR
x23kaFDh8+zKUuSdIrkUJ66cazgOfF0FYKiDewd1sGEdZT+gq7Wo8Hi8cp9q+Kuq
MkGrTzu21yMi2fo7RKoSOtV4RQViu6PuoYR/KvRq4F4abHEAivAG4xhPQife84IB
NkDQXaE9EXTP7DXPwj4HV8CnC9LC2qPFU7GVeYsCgYBWPK6c5qSJKrIouif9sDwC
EOJIighwWmvZK9DPvefB/gw49MEK4qr9g0US8Oxyy07w/wkPhiqV97eoYZW9yPIe
Me6WXMaNNxgkKlr86+HW1NfpDmMQ3kYefWHPLDsHJSoElJvqtYXeMKlOkjOg1a+/
iNP83Lftyr3N1jIK5jHpsQKBgE1+clm7qOnT547C7JrxLdrEZs0ehI/R3YuUPvHz
V6gEvPHHqAXZmVsL3CSG5WOiCNVrw9Ip2zTa0RUf08vVJkg1353PMyJRrJi4SVZp
dB8ym/1sASLHxNVkQC7l1UtsQKcN0Fe6/b8GzgFICW6qdHqzP55WZFD/3JUnIZZS
PyrjAoGBAMfuE0Nrw9Fq7f8+U/SsqiW9djJp4R34EF5n8qHkotfRoAbGktCGxLoF
QMbB9X0ibRMpxEKPHj+V0FXu9zUKFuqoriRc3alQYLNzJEdKeQQ/AN5xjG3ilM6D
/FunImbvSdJLjR9Ue6vlFdOquHevBCFDiHOJPQJ/y4qyZR5Avluw
-----END RSA PRIVATE KEY-----

File diff suppressed because it is too large Load Diff

View File

@ -1,26 +0,0 @@
{
"name": "openvidu-roles-node",
"version": "2.27.0",
"description": "",
"main": "server.js",
"scripts": {
"test": "ng test"
},
"repository": {
"type": "git",
"url": "git+https://github.com/OpenVidu/openvidu-tutorials.git"
},
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/OpenVidu/openvidu-tutorials/issues"
},
"homepage": "https://github.com/OpenVidu/openvidu-tutorials#readme",
"dependencies": {
"body-parser": "1.20.2",
"cors": "2.8.5",
"dotenv": "^16.3.1",
"express": "4.18.2",
"express-session": "1.17.3",
"livekit-server-sdk": "1.2.7"
}
}

View File

@ -1,274 +0,0 @@
var LivekitClient = window.LivekitClient;
var room;
var myRoomName;
var token;
var nickname;
/* OPENVIDU METHODS */
function joinRoom() {
document.getElementById('join-btn').disabled = true;
document.getElementById('join-btn').innerHTML = 'Joining...';
const myParticipantName = $('#myParticipantName').val();
const myRoomName = $('#myRoomName').val();
room = new LivekitClient.Room();
room.on(
LivekitClient.RoomEvent.TrackSubscribed,
(track, publication, participant) => {
const element = track.attach();
element.id = track.sid;
element.className = 'removable';
document.getElementById('video-container').appendChild(element);
if (track.kind === 'video') {
var participantNickname;
try {
participantNickname = JSON.parse(participant.metadata).nickname;
} catch (error) {
console.warn('Error parsing participant metadata: ' + error);
}
appendUserData(element, participant.identity, participantNickname);
}
}
);
// On every new Track destroyed...
room.on(
LivekitClient.RoomEvent.TrackUnsubscribed,
(track, publication, participant) => {
track.detach();
document.getElementById(track.sid)?.remove();
if (track.kind === 'video') {
removeUserData(participant);
}
}
);
getToken(myRoomName, myParticipantName).then((token) => {
const livekitUrl = getLivekitUrlFromMetadata(token);
room
.connect(livekitUrl, token)
.then(async () => {
var participantName = $('#user').val();
$('#room-title').text(myRoomName);
$('#join').hide();
$('#room').show();
const canPublish = room.localParticipant.permissions.canPublish;
if (canPublish) {
const [microphonePublication, cameraPublication] = await Promise.all([
room.localParticipant.setMicrophoneEnabled(true),
room.localParticipant.setCameraEnabled(true),
]);
const element = cameraPublication.track.attach();
element.className = 'removable';
document.getElementById('video-container').appendChild(element);
initMainVideo(element, myParticipantName, nickname);
appendUserData(element, myParticipantName, nickname);
} else {
initMainVideoThumbnail();
}
})
.catch((error) => {
console.warn(
'There was an error connecting to the room:',
error.code,
error.message
);
enableBtn();
});
});
return false;
}
function leaveRoom() {
room.disconnect();
room = null;
// Removing all HTML elements with the user's nicknames
cleanRoomView();
$('#join').show();
$('#room').hide();
enableBtn();
}
/* OPENVIDU METHODS */
function enableBtn() {
document.getElementById('join-btn').disabled = false;
document.getElementById('join-btn').innerHTML = 'Join!';
}
/* APPLICATION REST METHODS */
function logIn() {
nickname = $('#user').val();
var pass = $('#pass').val();
httpPostRequest(
'login',
{ user: nickname, pass },
'Login WRONG',
(response) => {
$('#name-user').text(nickname);
$('#not-logged').hide();
$('#logged').show();
// Random myParticipantName and room
$('#myRoomName').val('Room ' + Math.floor(Math.random() * 10));
$('#myParticipantName').val(
'Participant ' + Math.floor(Math.random() * 100)
);
}
);
}
function logOut() {
httpPostRequest('logout', {}, 'Logout WRONG', (response) => {
$('#not-logged').show();
$('#logged').hide();
});
enableBtn();
}
function getToken(roomName, participantName) {
return new Promise((resolve, reject) => {
// Video-call chosen by the user
httpPostRequest(
'token',
{ roomName, participantName },
'Error generating token',
(response) => resolve(response.token)
);
});
}
async function httpPostRequest(url, body, errorMsg, successCallback) {
try {
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
});
if (response.ok) {
const data = await response.json();
successCallback(data);
} else {
console.warn(errorMsg);
console.warn('Error: ' + response.statusText);
}
} catch (error) {
console.error(error);
}
}
/* APPLICATION REST METHODS */
/* APPLICATION BROWSER METHODS */
window.onbeforeunload = () => {
if (room) {
leaveRoom();
}
logOut();
};
function getLivekitUrlFromMetadata(token) {
if (!token) throw new Error('Trying to get metadata from an empty token');
try {
const base64Url = token.split('.')[1];
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
const jsonPayload = decodeURIComponent(
window
.atob(base64)
.split('')
.map((c) => {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
})
.join('')
);
const payload = JSON.parse(jsonPayload);
if (!payload?.metadata) throw new Error('Token does not contain metadata');
const metadata = JSON.parse(payload.metadata);
return metadata.livekitUrl;
} catch (error) {
throw new Error('Error decoding and parsing token: ' + error);
}
}
function appendUserData(videoElement, participantName, nickname) {
var dataNode = document.createElement('div');
dataNode.className = 'removable';
dataNode.id = 'data-' + participantName;
dataNode.innerHTML = `
<p class='nickname'>${nickname}</p>
<p class='participantName'>${participantName}</p>
`;
videoElement.parentNode.insertBefore(dataNode, videoElement.nextSibling);
addClickListener(videoElement, participantName);
}
function removeUserData(participant) {
var dataNode = document.getElementById('data-' + participant.identity);
dataNode?.parentNode.removeChild(dataNode);
}
function removeAllUserData() {
var elementsToRemove = document.getElementsByClassName('removable');
while (elementsToRemove[0]) {
elementsToRemove[0].parentNode.removeChild(elementsToRemove[0]);
}
}
function cleanMainVideo() {
$('#main-video video').get(0).srcObject = null;
$('#main-video p').each(function () {
$(this).html('');
});
}
function addClickListener(videoElement, clientData, serverData) {
videoElement.addEventListener('click', function () {
var mainVideo = $('#main-video video').get(0);
if (mainVideo.srcObject !== videoElement.srcObject) {
$('#main-video').fadeOut('fast', () => {
$('#main-video p.nickname').html(clientData);
$('#main-video p.participantName').html(serverData);
mainVideo.srcObject = videoElement.srcObject;
$('#main-video').fadeIn('fast');
});
}
});
}
function initMainVideo(videoElement, participantName, nickname) {
$('#main-video video').get(0).srcObject = videoElement.srcObject;
$('#main-video p.nickname').html(nickname);
$('#main-video p.participantName').html(participantName);
}
function initMainVideoThumbnail() {
$('#main-video video').css(
'background',
"url('images/subscriber-msg.jpg') round"
);
}
function cleanRoomView() {
removeAllUserData();
cleanMainVideo();
$('#main-video video').css('background', '');
}
/* APPLICATION BROWSER METHODS */

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

View File

@ -1,210 +0,0 @@
<html>
<head>
<title>openvidu-roles-node</title>
<meta
name="viewport"
content="width=device-width, initial-scale=1"
charset="utf-8"
/>
<link rel="shortcut icon" href="images/favicon.ico" type="image/x-icon" />
<!-- Bootstrap -->
<script
src="https://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
crossorigin="anonymous"
></script>
<link
rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u"
crossorigin="anonymous"
/>
<script
src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"
integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"
crossorigin="anonymous"
></script>
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"
/>
<!-- Bootstrap -->
<link rel="styleSheet" href="style.css" type="text/css" media="screen" />
<script src="https://cdn.jsdelivr.net/npm/livekit-client/dist/livekit-client.umd.min.js"></script>
<script src="app.js"></script>
<script>
$(document).ready(function () {
$('[data-toggle="tooltip"]').tooltip({
html: true,
});
});
</script>
</head>
<body>
<nav class="navbar navbar-default">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="/"
><img
class="demo-logo"
src="images/openvidu_vert_white_bg_trans_cropped.png"
/>
Roles Node</a
>
<a
class="navbar-brand nav-icon"
href="https://github.com/OpenVidu/openvidu-tutorials/tree/master/openvidu-roles-node"
title="GitHub Repository"
target="_blank"
><i class="fa fa-github" aria-hidden="true"></i
></a>
<a
class="navbar-brand nav-icon"
href="http://www.docs.openvidu.io/en/stable/tutorials/openvidu-roles-node/"
title="Documentation"
target="_blank"
><i class="fa fa-book" aria-hidden="true"></i
></a>
</div>
</div>
</nav>
<div id="main-container" class="container">
<div id="not-logged" class="vertical-center">
<div id="img-div">
<img src="images/openvidu_grey_bg_transp_cropped.png" />
</div>
<form class="form-group jumbotron" onsubmit="return false">
<p>
<label>User</label
><input class="form-control" type="text" id="user" required />
</p>
<p>
<label>Password</label
><input class="form-control" type="password" id="pass" required />
</p>
<p class="text-center">
<button class="btn btn-lg btn-info" onclick="logIn()">
Log in
</button>
</p>
</form>
<table class="table">
<tr>
<th>User</th>
<th>Password</th>
<th>
Role<i
data-toggle="tooltip"
data-placement="bottom"
title=""
data-original-title="<div id='tooltip-div'>PUBLISHER<div>Send and receive media<hr></div>SUBSCRIBER<div>Receive media</div></div>"
class="glyphicon glyphicon-info-sign"
></i>
</th>
</tr>
<tr>
<td>publisher1</td>
<td>pass</td>
<td>PUBLISHER</td>
</tr>
<tr>
<td>publisher2</td>
<td>pass</td>
<td>PUBLISHER</td>
</tr>
<tr>
<td>subscriber</td>
<td>pass</td>
<td>SUBSCRIBER</td>
</tr>
</table>
</div>
<div id="logged" hidden>
<div id="join" class="vertical-center">
<div id="img-div">
<img src="images/openvidu_grey_bg_transp_cropped.png" />
</div>
<div id="join-dialog" class="jumbotron">
<h1>Join a video room</h1>
<form class="form-group" onsubmit="return false">
<p>
<label>Participant</label>
<input
class="form-control"
type="text"
id="myParticipantName"
required
/>
</p>
<p>
<label>Room</label>
<input
class="form-control"
type="text"
id="myRoomName"
required
/>
</p>
<p class="text-center">
<button
class="btn btn-lg btn-success"
id="join-btn"
onclick="joinRoom()"
>
Join!
</button>
</p>
</form>
<hr />
<div id="login-info">
<div>Logged as <span id="name-user"></span></div>
<button
id="logout-btn"
class="btn btn-warning"
onclick="logOut()"
>
Log out
</button>
</div>
</div>
</div>
<div id="room" style="display: none">
<div id="room-header">
<h1 id="room-title"></h1>
<input
class="btn btn-large btn-danger"
type="button"
id="buttonLeaveRoom"
onmouseup="leaveRoom()"
value="Leave room"
/>
</div>
<div id="main-video" class="col-md-6">
<p class="nickname"></p>
<p class="participantName"></p>
<video autoplay playsinline="true"></video>
</div>
<div id="video-container" class="col-md-6"></div>
</div>
</div>
</div>
<footer class="footer">
<div class="container">
<div class="text-muted">OpenVidu © 2023</div>
<a href="http://www.openvidu.io/" target="_blank"
><img
class="openvidu-logo"
src="images/openvidu_globe_bg_transp_cropped.png"
/></a>
</div>
</footer>
</body>
</html>

View File

@ -1,382 +0,0 @@
html {
position: relative;
min-height: 100%;
}
nav {
height: 50px;
width: 100%;
z-index: 1;
background-color: #4d4d4d !important;
border-color: #4d4d4d !important;
border-top-right-radius: 0 !important;
border-top-left-radius: 0 !important;
}
.navbar {
margin-bottom: 0px !important;
}
.navbar-header {
width: 100%;
}
.nav-icon {
padding: 5px 15px 5px 15px;
float: right;
}
nav a {
color: #ccc !important;
}
nav i.fa {
font-size: 40px;
color: #ccc;
}
nav a:hover {
color: #a9a9a9 !important;
}
nav i.fa:hover {
color: #a9a9a9;
}
#main-container {
padding-bottom: 80px;
display: contents;
}
.vertical-center {
width: -webkit-fit-content;
width: fit-content;
margin: auto;
}
.vertical-center#not-logged form {
width: -moz-fit-content;
margin: auto;
}
.vertical-center#not-logged table {
width: -moz-fit-content;
margin: auto;
}
.vertical-center table {
margin-top: 3em !important;
}
.horizontal-center {
margin: 0 auto;
}
.form-control {
color: #0088aa;
font-weight: bold;
}
.form-control:focus {
border-color: #0088aa;
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(0, 136, 170, 0.6);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(0, 136, 170, 0.6);
}
input.btn {
font-weight: bold;
}
.btn {
font-weight: bold !important;
}
.btn-success {
background-color: #06d362 !important;
border-color: #06d362;
}
.btn-success:hover {
background-color: #1abd61 !important;
border-color: #1abd61;
}
.btn-info {
background-color: #0088aa !important;
border-color: #0088aa;
}
.btn-info:hover {
background-color: #00708c !important;
border-color: #00708c;
}
.btn-warning {
background-color: #ffcc00 !important;
border-color: #ffcc00;
color: #4d4d4d;
}
.btn-warning:hover {
background-color: #eabb3a !important;
border-color: #eabb3a;
color: #4d4d4d;
}
.btn-warning:active {
color: #4d4d4d;
}
.btn-warning:focus {
color: #4d4d4d;
}
.btn-warning:active:focus {
color: #4d4d4d;
}
.footer {
position: absolute;
bottom: 0;
width: 100%;
height: 60px;
background-color: #4d4d4d;
}
.footer .text-muted {
margin: 20px 0;
float: left;
color: #ccc;
}
.openvidu-logo {
height: 35px;
float: right;
margin: 12px 0;
-webkit-transition: all 0.1s ease-in-out;
-moz-transition: all 0.1s ease-in-out;
-o-transition: all 0.1s ease-in-out;
transition: all 0.1s ease-in-out;
}
.openvidu-logo:hover {
-webkit-filter: grayscale(0.5);
filter: grayscale(0.5);
}
.demo-logo {
margin: 0;
height: 22px;
float: left;
padding-right: 8px;
}
a:hover .demo-logo {
-webkit-filter: brightness(0.7);
filter: brightness(0.7);
}
#join {
padding-top: 40px;
}
#not-logged {
padding-top: 40px;
}
#join-dialog h1 {
color: #4d4d4d;
font-weight: bold;
text-align: center;
}
#join-dialog label {
color: #0088aa;
}
#join-dialog input.btn {
margin-top: 15px;
}
#join-dialog hr {
background: #4d4d4d;
}
#img-div {
text-align: center;
padding-bottom: 3em;
}
#img-div img {
height: 15%;
}
#logged {
width: 100%;
}
#join {
max-width: 700px;
margin: auto;
}
#room-header {
margin-bottom: 20px;
}
#room-header form {
display: inline;
}
#room-title {
display: inline-block;
}
#buttonLeaveRoom {
float: right;
margin-top: 20px;
}
#video-container video {
position: relative;
float: left;
width: 50%;
cursor: pointer;
}
#video-container div {
float: left;
width: 50%;
position: relative;
margin-left: -50%;
}
#video-container p {
display: inline-block;
background: #f8f8f8;
padding-left: 5px;
padding-right: 5px;
color: #777777;
font-weight: bold;
border-bottom-right-radius: 4px;
}
#video-container p.nickname {
float: right;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 0px;
font-weight: lighter;
font-size: 12px;
background: #777777;
color: #f8f8f8;
}
video {
width: 100%;
height: auto;
}
#main-video p {
position: absolute;
display: inline-block;
background: #f8f8f8;
padding-left: 5px;
padding-right: 5px;
font-size: 22px;
color: #777777;
font-weight: bold;
border-bottom-right-radius: 4px;
}
#main-video p.nickname {
position: absolute;
right: 0;
font-size: 16px !important;
margin-right: 15px;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 0px;
font-weight: lighter;
font-size: 12px;
background: #777777;
color: #f8f8f8;
}
#room img {
width: 100%;
height: auto;
display: inline-block;
object-fit: contain;
vertical-align: baseline;
}
#room #video-container img {
position: relative;
float: left;
width: 50%;
cursor: pointer;
object-fit: cover;
height: 180px;
}
table i {
cursor: pointer;
margin-left: 1em;
}
#tooltip-div {
text-align: left;
}
#tooltip-div hr {
margin: 5px 0;
}
#login-info {
text-align: right;
}
#login-info form {
display: inline;
}
#login-info div {
display: inline;
margin-right: 1em;
}
#name-user {
font-weight: bold;
}
/* xs ans md screen resolutions*/
@media screen and (max-width: 991px) {
#join {
padding-top: inherit;
}
#not-logged {
padding-top: inherit;
}
.container .navbar-header {
margin-right: 0 !important;
margin-left: 0 !important;
}
.nav-icon {
padding: 9px 8px 9px 8px;
}
nav i.fa {
font-size: 32px;
}
.vertical-center {
padding-top: 10px;
}
#img-div {
padding-bottom: 1em;
}
#img-div img {
height: 10%;
}
}

View File

@ -1,175 +0,0 @@
/* CONFIGURATION */
require('dotenv').config(
!!process.env.CONFIG ? { path: process.env.CONFIG } : {}
);
// For demo purposes we ignore self-signed certificate
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
// Node imports
const express = require('express');
const fs = require('fs');
const session = require('express-session');
const https = require('https');
const bodyParser = require('body-parser');
const AccessToken = require('livekit-server-sdk').AccessToken;
const cors = require('cors');
const app = express();
// Environment variable: PORT where the node server is listening
const SERVER_PORT = process.env.SERVER_PORT || 5000;
// Environment variable: api key shared with our LiveKit deployment
const LIVEKIT_API_KEY = process.env.LIVEKIT_API_KEY || 'devkey';
// Environment variable: api secret shared with our LiveKit deployment
const LIVEKIT_API_SECRET = process.env.LIVEKIT_API_SECRET || 'secret';
// Environment variable: url of our LiveKit deployment
const LIVEKIT_URL = process.env.LIVEKIT_URL || 'ws://localhost:7880';
// Listen (start app with node server.js)
const options = {
key: fs.readFileSync('openvidukey.pem'),
cert: fs.readFileSync('openviducert.pem'),
};
// The users of our application
// They should be stored in a database
const users = [
{
user: 'publisher1',
pass: 'pass',
role: 'PUBLISHER',
},
{
user: 'publisher2',
pass: 'pass',
role: 'PUBLISHER',
},
{
user: 'subscriber',
pass: 'pass',
role: 'SUBSCRIBER',
},
];
// Enable CORS support
app.use(
cors({
origin: '*',
})
);
// Server configuration
app.use(
session({
saveUninitialized: true,
resave: false,
secret: 'MY_SECRET',
})
);
// Set the static files location
app.use(express.static(__dirname + '/public'));
// Parse application/x-www-form-urlencoded
app.use(
bodyParser.urlencoded({
extended: 'true',
})
);
// Parse application/json
app.use(bodyParser.json());
// Parse application/vnd.api+json as json
app.use(
bodyParser.json({
type: 'application/vnd.api+json',
})
);
https.createServer(options, app).listen(SERVER_PORT, () => {
console.log(`App listening on port ${SERVER_PORT}`);
console.log(`LIVEKIT API KEY: ${LIVEKIT_API_KEY}`);
console.log(`LIVEKIT API SECRET: ${LIVEKIT_API_SECRET}`);
console.log(`LIVEKIT URL: ${LIVEKIT_URL}`);
console.log();
console.log('Access the app at https://localhost:' + SERVER_PORT);
});
/* CONFIGURATION */
/* REST API */
app.post('/login', (req, res) => {
// Retrieve params from body
const { user, pass } = req.body;
if (login(user, pass)) {
// Successful login
// Validate session and return OK
// Value stored in req.session allows us to identify the user in future requests
console.log(`Successful login for user '${user}'`);
req.session.loggedUser = user;
res.status(200).json({});
} else {
// Credentials are NOT valid
// Invalidate session and return error
console.log(`Invalid credentials for user '${user}'`);
req.session.destroy();
res.status(401).json({ message: 'Invalid credentials' });
}
});
app.post('/logout', function (req, res) {
console.log(`'${req.session.loggedUser}' has logged out`);
req.session.destroy();
res.status(200).json({});
});
app.post('/token', (req, res) => {
const {roomName, participantName} = req.body;
if (!isLogged(req.session)) {
req.session.destroy();
res.status(401).json({ message: 'User not logged' });
return;
}
console.log(
`Getting a token for room '${roomName}' and participant '${participantName}'`
);
if (!roomName || !participantName) {
res
.status(400)
.json({ message: 'roomName and participantName are required' });
return;
}
const user = users.find((u) => u.user === req.session.loggedUser);
const {role, user: nickname} = user;
const canPublish = role === 'PUBLISHER';
const at = new AccessToken(LIVEKIT_API_KEY, LIVEKIT_API_SECRET, {
identity: participantName,
// add metadata to the token, which will be available in the participant's metadata
metadata: JSON.stringify({ livekitUrl: LIVEKIT_URL, nickname, role }),
});
at.addGrant({
roomJoin: true,
room: roomName,
canPublish,
canSubscribe: true,
});
res.status(200).json({ token: at.toJwt() });
});
/* REST API */
/* AUXILIARY METHODS */
function login(user, pass) {
return users.find((u) => u.user === user && u.pass === pass);
}
function isLogged(session) {
return session.loggedUser != null;
}
/* AUXILIARY METHODS */