+ diff --git a/basic/frontend/javascript/README.md b/basic/frontend/javascript/README.md new file mode 100644 index 00000000..270d66eb --- /dev/null +++ b/basic/frontend/javascript/README.md @@ -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 +``` diff --git a/basic/frontend/javascript/web/app.js b/basic/frontend/javascript/web/app.js new file mode 100644 index 00000000..9e34733c --- /dev/null +++ b/basic/frontend/javascript/web/app.js @@ -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 = "
" + participantIdentity + "
"; + 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), + }); + }); +} diff --git a/basic/frontend/javascript/web/index.html b/basic/frontend/javascript/web/index.html new file mode 100644 index 00000000..36a557f1 --- /dev/null +++ b/basic/frontend/javascript/web/index.html @@ -0,0 +1,114 @@ + + +
+