-
+
-
+
-
-
-
\ No newline at end of file
+
+
+
diff --git a/application-client/openvidu-electron/src/index.js b/application-client/openvidu-electron/src/index.js
index 4dea6e82..23985b00 100644
--- a/application-client/openvidu-electron/src/index.js
+++ b/application-client/openvidu-electron/src/index.js
@@ -1,196 +1,50 @@
-const axios = require("axios");
-const { Room, RoomEvent } = require("livekit-client");
+const { app, BrowserWindow } = require("electron");
+const path = require("node:path");
+const livekit = require("livekit-client");
-const ipcRenderer = require("electron").ipcRenderer;
-const { BrowserWindow } = require("@electron/remote");
-var room;
-var myParticipantName;
-var myRoomName;
-var isScreenShared = false;
-var screenSharePublication;
+// Handle creating/removing shortcuts on Windows when installing/uninstalling.
+if (require("electron-squirrel-startup")) {
+ app.quit();
+}
-// Configure this constants with correct URLs depending on your deployment
-const APPLICATION_SERVER_URL = "http://localhost:6080/";
-const LIVEKIT_URL = "ws://localhost:7880/";
+const createWindow = () => {
+ // Create the browser window.
+ const mainWindow = new BrowserWindow({
+ width: 1280,
+ height: 720,
+ webPreferences: {
+ nodeIntegration: true,
+ contextIsolation: false,
+ }
+ });
-ipcRenderer.on("screen-share-ready", async (event, sourceId) => {
- if (sourceId) {
- try {
- const stream = await navigator.mediaDevices.getUserMedia({
- audio: false,
- video: {
- mandatory: {
- chromeMediaSource: "desktop",
- chromeMediaSourceId: sourceId,
- },
- },
- });
+ // and load the index.html of the app.
+ mainWindow.loadFile(path.join(__dirname, "index.html"));
- const track = stream.getVideoTracks()[0];
- const screenPublication = await room.localParticipant.publishTrack(track);
- isScreenShared = true;
- screenSharePublication = screenPublication;
- const element = screenPublication.track.attach();
- element.id = screenPublication.trackSid;
- element.className = "removable";
- document.getElementById("local-participant").appendChild(element);
- } catch (error) {
- console.error("Error enabling screen sharing", error);
- }
- }
+ // Open the DevTools.
+ // mainWindow.webContents.openDevTools();
+};
+
+// This method will be called when Electron has finished
+// initialization and is ready to create browser windows.
+// Some APIs can only be used after this event occurs.
+app.whenReady().then(() => {
+ createWindow();
+
+ // On OS X it's common to re-create a window in the app when the
+ // dock icon is clicked and there are no other windows open.
+ app.on("activate", () => {
+ if (BrowserWindow.getAllWindows().length === 0) {
+ createWindow();
+ }
+ });
});
-async function joinRoom() {
- myRoomName = document.getElementById("roomName").value;
- myParticipantName = document.getElementById("participantName").value;
-
- // --- 1) Get a Room object ---
- room = new Room();
-
- // --- 2) Specify the actions when events take place in the room ---
-
- // On every new Track received...
- room.on(RoomEvent.TrackSubscribed, (track, publication, participant) => {
- console.log("TrackSubscribed", track, publication, participant);
- const element = track.attach();
- element.id = track.sid;
- element.className = "removable";
- document.getElementById("remote-participants").appendChild(element);
- });
-
- // On every new Track destroyed...
- room.on(RoomEvent.TrackUnsubscribed, (track, publication, participant) => {
- console.log("TrackUnSubscribed", track, publication, participant);
-
- track.detach();
- document.getElementById(track.sid)?.remove();
- });
-
- // --- 3) Connect to the room with a valid access token ---
-
- // Get a token from the application backend
- const token = await getToken(myRoomName, myParticipantName);
-
- await room.connect(LIVEKIT_URL, token);
-
- showRoom();
- // --- 4) Publish your local tracks ---
- await room.localParticipant.setMicrophoneEnabled(true);
- const publication = await room.localParticipant.setCameraEnabled(true);
- const element = publication.track.attach();
- element.className = "removable";
- document.getElementById("local-participant").appendChild(element);
-}
-
-async function toggleScreenShare() {
- console.log("Toggling screen share");
- const enabled = !isScreenShared;
-
- if (enabled) {
- openScreenShareModal();
- } else {
- // Disable screen sharing
- await stopScreenSharing();
- }
-}
-
-async function stopScreenSharing() {
- try {
- await room.localParticipant.unpublishTrack(screenSharePublication.track);
- isScreenShared = false;
- const trackSid = screenSharePublication?.trackSid;
-
- if (trackSid) {
- document.getElementById(trackSid)?.remove();
+// Quit when all windows are closed, except on macOS. There, it's common
+// for applications and their menu bar to stay active until the user quits
+// explicitly with Cmd + Q.
+app.on("window-all-closed", () => {
+ if (process.platform !== "darwin") {
+ app.quit();
}
- screenSharePublication = undefined;
- } catch (error) {
- console.error("Error stopping screen sharing", error);
- }
-}
-
-function leaveRoom() {
- // --- 5) 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
- removeAllParticipantElements();
- hideRoom();
-}
-
-function showRoom() {
- document.getElementById("room-header").innerText = myRoomName;
- document.getElementById("join").style.display = "none";
- document.getElementById("room").style.display = "block";
-}
-
-function hideRoom() {
- document.getElementById("join").style.display = "block";
- document.getElementById("room").style.display = "none";
-}
-
-function removeAllParticipantElements() {
- var elementsToRemove = document.getElementsByClassName("removable");
- while (elementsToRemove[0]) {
- elementsToRemove[0].parentNode.removeChild(elementsToRemove[0]);
- }
-}
-
-function openScreenShareModal() {
- let win = new BrowserWindow({
- parent: require("@electron/remote").getCurrentWindow(),
- modal: true,
- minimizable: false,
- maximizable: false,
- webPreferences: {
- nodeIntegration: true,
- enableRemoteModule: true,
- contextIsolation: false,
- },
- resizable: false,
- });
- require("@electron/remote")
- .require("@electron/remote/main")
- .enable(win.webContents);
-
- win.setMenu(null);
- // win.webContents.openDevTools();
-
- var theUrl = "file://" + __dirname + "/modal.html";
- win.loadURL(theUrl);
-}
-
-/**
- * --------------------------------------------
- * 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.
- *
- */
-async function getToken(roomName, participantName) {
- try {
- const response = await axios.post(
- APPLICATION_SERVER_URL + "token",
- {
- roomName,
- participantName,
- },
- {
- headers: {
- "Content-Type": "application/json",
- },
- }
- );
-
- return response.data;
- } catch (error) {
- console.error("No connection to application server", error);
- }
-}
+});
diff --git a/application-client/openvidu-electron/src/main.js b/application-client/openvidu-electron/src/main.js
deleted file mode 100644
index f9bca8df..00000000
--- a/application-client/openvidu-electron/src/main.js
+++ /dev/null
@@ -1,64 +0,0 @@
-// Modules to control application life and create native browser window
-const { app, BrowserWindow, ipcMain } = require("electron");
-const path = require("path");
-require("@electron/remote/main").initialize();
-
-function createWindow() {
- // Create the browser window.
- const mainWindow = new BrowserWindow({
- width: 1280,
- height: 720,
- webPreferences: {
- preload: path.join(__dirname, "preload.js"),
- nodeIntegration: true,
- enableRemoteModule: true,
- contextIsolation: false,
- },
- });
-
- // Enable remote module in the main window WebContents.
- require("@electron/remote/main").enable(mainWindow.webContents);
-
- // and load the index.html of the app.
- mainWindow.loadFile("src/index.html");
-
- // Open the DevTools.
- // mainWindow.webContents.openDevTools()
-
- ipcMain.on("screen-share-selected", (event, message) => {
- mainWindow.webContents.send("screen-share-ready", message);
- });
-}
-
-// This method will be called when Electron has finished
-// initialization and is ready to create browser windows.
-// Some APIs can only be used after this event occurs.
-app.whenReady().then(() => {
- createWindow();
-
- app.on("activate", function () {
- // On macOS it's common to re-create a window in the app when the
- // dock icon is clicked and there are no other windows open.
- if (BrowserWindow.getAllWindows().length === 0) createWindow();
- });
-});
-
-// Quit when all windows are closed, except on macOS. There, it's common
-// for applications and their menu bar to stay active until the user quits
-// explicitly with Cmd + Q.
-app.on("window-all-closed", function () {
- if (process.platform !== "darwin") app.quit();
-});
-
-app.on(
- "certificate-error",
- (event, webContents, url, error, certificate, callback) => {
- // On certificate error we disable default behaviour (stop loading the page)
- // and we then say "it is all fine - true" to the callback
- event.preventDefault();
- callback(true);
- }
-);
-
-// In this file you can include the rest of your app's specific main process
-// code. You can also put them in separate files and require them here.
diff --git a/application-client/openvidu-electron/src/modal.html b/application-client/openvidu-electron/src/modal.html
deleted file mode 100644
index 3e599b09..00000000
--- a/application-client/openvidu-electron/src/modal.html
+++ /dev/null
@@ -1,179 +0,0 @@
-
-
-
-
-
-
Screen Sharing Selection
-
-
-
-
-
-
Select a Screen to Share
-
-
-
-
-
-
diff --git a/application-client/openvidu-electron/src/preload.js b/application-client/openvidu-electron/src/preload.js
deleted file mode 100644
index b0acf6be..00000000
--- a/application-client/openvidu-electron/src/preload.js
+++ /dev/null
@@ -1,12 +0,0 @@
-// All of the Node.js APIs are available in the preload process.
-// It has the same sandbox as a Chrome extension.
-window.addEventListener('DOMContentLoaded', () => {
- const replaceText = (selector, text) => {
- const element = document.getElementById(selector)
- if (element) element.innerText = text
- }
-
- for (const dependency of ['chrome', 'node', 'electron']) {
- replaceText(`${dependency}-version`, process.versions[dependency])
- }
-})
\ No newline at end of file
diff --git a/application-client/openvidu-electron/src/resources/images/favicon.ico b/application-client/openvidu-electron/src/resources/images/favicon.ico
new file mode 100644
index 00000000..0e2249ad
Binary files /dev/null and b/application-client/openvidu-electron/src/resources/images/favicon.ico differ
diff --git a/application-client/openvidu-electron/src/resources/images/openvidu_logo.png b/application-client/openvidu-electron/src/resources/images/openvidu_logo.png
new file mode 100644
index 00000000..e0309e62
Binary files /dev/null and b/application-client/openvidu-electron/src/resources/images/openvidu_logo.png differ
diff --git a/application-client/openvidu-electron/src/style.css b/application-client/openvidu-electron/src/style.css
deleted file mode 100644
index d3f1f901..00000000
--- a/application-client/openvidu-electron/src/style.css
+++ /dev/null
@@ -1,92 +0,0 @@
-html,
-body {
- height: 100%;
- font-family: 'Open Sans', sans-serif;
-}
-
-html {
- display: table;
- margin: auto;
-}
-
-body {
- display: table-cell;
- vertical-align: middle;
- text-align: center;
-}
-
-#local-participant {
- float: left;
- margin: 10px;
- width: 40%;
-}
-
-#remote-participants {
- float: right;
- margin: 10px;
- width: 40%;
-}
-
-video {
- width: 70%;
- margin: 10px auto 0 auto;
- display: block;
-}
-
-h1 {
- font-size: 24px;
- margin-bottom: 20px;
-}
-
-.form-group {
- margin: 10px 0;
- text-align: left;
-}
-
-label {
- display: block;
- font-weight: bold;
- margin-bottom: 5px;
-}
-
-input[type='text'],
-input[type='checkbox'],
-button {
- width: 95%;
- padding: 10px;
- border: 1px solid #ccc;
- border-radius: 3px;
- font-size: 16px;
- box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
-}
-
-input[type='checkbox'] {
- width: auto;
- display: inline;
- margin-right: 10px;
-}
-
-button {
- background: #007bff;
- color: #fff;
- cursor: pointer;
- transition: background 0.3s;
-}
-
-.leave-button,
-.share-screen-button {
- color: #fff;
- padding: 10px 20px;
- border: none;
- border-radius: 5px;
- cursor: pointer;
- width: 50%;
- margin: 10px 10px;
-}
-
-.leave-button {
- background-color: rgb(151, 28, 28);
-}
-.share-screen-button {
- background-color: #0056b3;
-}
diff --git a/application-client/openvidu-electron/src/styles.css b/application-client/openvidu-electron/src/styles.css
new file mode 100644
index 00000000..2f8b1ce9
--- /dev/null
+++ b/application-client/openvidu-electron/src/styles.css
@@ -0,0 +1,264 @@
+html {
+ height: 100%;
+}
+
+body {
+ margin: 0;
+ padding: 0;
+ padding-top: 50px;
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+}
+
+header {
+ height: 50px;
+ width: 100%;
+ position: fixed;
+ top: 0;
+ left: 0;
+ z-index: 1;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 10px 30px;
+ background-color: #4d4d4d;
+}
+
+header h1 {
+ margin: 0;
+ font-size: 1.5em;
+ font-weight: bold;
+}
+
+header a {
+ color: #ccc;
+ text-decoration: none;
+}
+
+header a:hover {
+ color: #a9a9a9;
+}
+
+header i {
+ padding: 5px 5px;
+ font-size: 2em;
+}
+
+main {
+ flex: 1;
+ padding: 20px;
+}
+
+#join {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ height: 100%;
+}
+
+#join-dialog {
+ width: 70%;
+ max-width: 900px;
+ padding: 60px;
+ border-radius: 6px;
+ background-color: #f0f0f0;
+}
+
+#join-dialog h2 {
+ color: #4d4d4d;
+ font-size: 60px;
+ font-weight: bold;
+ text-align: center;
+}
+
+#join-dialog form {
+ text-align: left;
+}
+
+#join-dialog label {
+ display: block;
+ margin-bottom: 10px;
+ color: #0088aa;
+ font-weight: bold;
+ font-size: 20px;
+}
+
+.form-control {
+ width: 100%;
+ padding: 8px;
+ margin-bottom: 10px;
+ box-sizing: border-box;
+ color: #0088aa;
+ font-weight: bold;
+}
+
+.form-control:focus {
+ color: #0088aa;
+ 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);
+}
+
+#join-dialog button {
+ display: block;
+ margin: 20px auto 0;
+}
+
+.btn {
+ font-weight: bold;
+}
+
+.btn-success {
+ background-color: #06d362;
+ border-color: #06d362;
+}
+
+.btn-success:hover {
+ background-color: #1abd61;
+ border-color: #1abd61;
+}
+
+#room {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+}
+
+#room-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ width: 100%;
+ max-width: 1000px;
+ padding: 0 20px;
+ margin-bottom: 20px;
+}
+
+#room-title {
+ font-size: 2em;
+ font-weight: bold;
+ margin: 0;
+}
+
+#layout-container {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
+ gap: 10px;
+ justify-content: center;
+ align-items: center;
+ width: 100%;
+ max-width: 1000px;
+ height: 100%;
+}
+
+.video-container {
+ position: relative;
+ background: #3b3b3b;
+ aspect-ratio: 16/9;
+ border-radius: 6px;
+ overflow: hidden;
+}
+
+.video-container video {
+ width: 100%;
+ height: 100%;
+}
+
+.video-container .participant-data {
+ position: absolute;
+ top: 0;
+ left: 0;
+}
+
+.participant-data p {
+ background: #f8f8f8;
+ margin: 0;
+ padding: 0 5px;
+ color: #777777;
+ font-weight: bold;
+ border-bottom-right-radius: 4px;
+}
+
+footer {
+ height: 60px;
+ width: 100%;
+ padding: 10px 30px;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ background-color: #4d4d4d;
+}
+
+footer a {
+ color: #ffffff;
+ text-decoration: none;
+}
+
+footer .text {
+ color: #ccc;
+ margin: 0;
+}
+
+footer .text span {
+ color: white;
+ font-weight: bold;
+}
+
+#openvidu-logo {
+ height: 35px;
+ -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);
+}
+
+/* Media Queries */
+@media screen and (max-width: 768px) {
+ header {
+ padding: 10px 15px;
+ }
+
+ #join-dialog {
+ width: 90%;
+ padding: 30px;
+ }
+
+ #join-dialog h2 {
+ font-size: 50px;
+ }
+
+ #layout-container {
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
+ }
+
+ footer {
+ padding: 10px 15px;
+ }
+}
+
+@media screen and (max-width: 480px) {
+ header {
+ padding: 10px;
+ }
+
+ #join-dialog {
+ width: 100%;
+ padding: 20px;
+ }
+
+ #join-dialog h2 {
+ font-size: 40px;
+ }
+
+ footer {
+ padding: 10px;
+ }
+}
diff --git a/application-client/openvidu-react/index.html b/application-client/openvidu-react/index.html
index ad170b68..dc5b4b4a 100644
--- a/application-client/openvidu-react/index.html
+++ b/application-client/openvidu-react/index.html
@@ -33,14 +33,14 @@
Basic React