Add basic client tutorial for JavaScript

This commit is contained in:
juancarmore 2024-04-30 01:28:52 +02:00
parent 47118ed13c
commit a55858f7e4
8 changed files with 595 additions and 0 deletions

View File

@ -0,0 +1,32 @@
# Basic Frontend JavaScript
Basic client application built with plain HTML, CSS and JavaScript. It internally uses [livekit-client-sdk-js](https://docs.livekit.io/client-sdk-js/).
For further information, check the [tutorial documentation](https://livekit-tutorials.openvidu.io/basic/frontend/javascript).
## Prerequisites
- [Node](https://nodejs.org/en/download)
## Run
1. Download repository
```bash
git clone https://github.com/OpenVidu/openvidu-livekit-tutorials.git
cd openvidu-livekit-tutorials/basic/frontend/javascript
```
2. Run the application
To run this tutorial, you will need a HTTP web server installed on your development computer. If you have Node.js installed, you can easily set up [http-server](https://github.com/indexzero/http-server):
```bash
npm install --location=global http-server
```
After installing http-server, serve the tutorial:
```bash
http-server -p 5080 web
```

View File

@ -0,0 +1,180 @@
// For local development, leave these variables empty
// For production, configure them with correct URLs depending on your deployment
var APPLICATION_SERVER_URL = "";
var LIVEKIT_URL = "";
configureUrls();
var LivekitClient = window.LivekitClient;
var room;
function configureUrls() {
// If APPLICATION_SERVER_URL is not configured, use default value from local development
if (!APPLICATION_SERVER_URL) {
if (window.location.hostname === "localhost") {
APPLICATION_SERVER_URL = "http://localhost:6080/";
} else {
APPLICATION_SERVER_URL = "https://" + window.location.hostname + ":6443/";
}
}
// If LIVEKIT_URL is not configured, use default value from local development
if (!LIVEKIT_URL) {
if (window.location.hostname === "localhost") {
LIVEKIT_URL = "ws://localhost:7880/";
} else {
LIVEKIT_URL = "wss://" + window.location.hostname + ":7443/";
}
}
}
function joinRoom() {
var myRoomName = document.getElementById("roomName").value;
var 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") {
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") {
removeUserData(participant);
}
});
// --- 3) Connect to the room with a valid access token ---
// Get a token from the application backend
getToken(myRoomName, myUserName).then((token) => {
// First param is the LiveKit server URL. Second param is the access token
room.connect(LIVEKIT_URL, 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";
}
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;
}
/**
* --------------------------------------------
* 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.
*/
function getToken(roomName, participantName) {
return new Promise((resolve, reject) => {
$.ajax({
type: "POST",
url: APPLICATION_SERVER_URL + "token",
data: JSON.stringify({
roomName,
participantName,
}),
headers: { "Content-Type": "application/json" },
success: (token) => resolve(token),
error: (error) => reject(error),
});
});
}

View File

@ -0,0 +1,114 @@
<html>
<head>
<title>openvidu-js</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@2.1.1/dist/livekit-client.umd.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
</a>
<a
class="navbar-brand nav-icon"
href="https://github.com/OpenVidu/openvidu-livekit-tutorials/tree/master/basic/frontend/javascript"
title="GitHub Repository"
target="_blank"
>
<i class="fa fa-github" aria-hidden="true"></i>
</a>
<a
class="navbar-brand nav-icon"
href="https://livekit-tutorials.openvidu.io/basic/frontend/javascript"
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 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">Made with love by OpenVidu Team</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.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -0,0 +1,269 @@
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, 0.075),
0 0 8px rgba(0, 136, 170, 0.6);
box-shadow: inset 0 1px 1px rgba(0, 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;
}
#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 {
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 #video-container 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;
}
}