openvidu-electron: Migrated to livekit
This commit is contained in:
parent
1c7fa1b8ec
commit
43cd1ab955
7208
openvidu-electron/package-lock.json
generated
7208
openvidu-electron/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -10,22 +10,20 @@
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"lint": "echo \"No linting configured\""
|
||||
},
|
||||
"repository": "https://github.com/OpenVidu/openvidu-tutorials",
|
||||
"author": "pablofuenteperez@gmail.com",
|
||||
"license": "Apache-2.0",
|
||||
"devDependencies": {
|
||||
"@electron-forge/cli": "6.0.0-beta.65",
|
||||
"@electron-forge/maker-deb": "6.0.0-beta.65",
|
||||
"@electron-forge/maker-rpm": "6.0.0-beta.65",
|
||||
"@electron-forge/maker-squirrel": "6.0.0-beta.65",
|
||||
"@electron-forge/maker-zip": "6.0.0-beta.65",
|
||||
"@electron-forge/cli": "6.0.0",
|
||||
"@electron-forge/maker-deb": "6.0.0",
|
||||
"@electron-forge/maker-rpm": "6.0.0",
|
||||
"@electron-forge/maker-squirrel": "6.0.0",
|
||||
"@electron-forge/maker-zip": "6.0.0",
|
||||
"electron": "19.0.10"
|
||||
},
|
||||
"dependencies": {
|
||||
"@electron/remote": "2.0.8",
|
||||
"axios": "0.27.2",
|
||||
"@electron/remote": "2.0.12",
|
||||
"axios": "1.5.1",
|
||||
"electron-squirrel-startup": "1.0.0",
|
||||
"openvidu-browser": "2.27.0"
|
||||
"livekit-client": "1.14.1"
|
||||
},
|
||||
"config": {
|
||||
"forge": {
|
||||
|
||||
@ -17,29 +17,34 @@
|
||||
and Electron <span id="electron-version"></span>.
|
||||
|
||||
<div id="join">
|
||||
<h1>Join a video session</h1>
|
||||
<h1>Join a Video Room</h1>
|
||||
<form onsubmit="initPublisher(); return false">
|
||||
<p>
|
||||
<label>Session:</label>
|
||||
<input type="text" id="sessionId" value="SessionA" required>
|
||||
</p>
|
||||
<p>
|
||||
<input id="screen-sharing" type="checkbox" name="screenshare"> Share screen?<br>
|
||||
</p>
|
||||
<p>
|
||||
<input type="submit" value="JOIN">
|
||||
</p>
|
||||
<div class="form-group">
|
||||
<label for="roomName">Room Name:</label>
|
||||
<input type="text" id="roomName" placeholder="Enter room name" value="RoomA" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="participantName">Your Name:</label>
|
||||
<input type="text" id="participantName" placeholder="Enter your name" required>
|
||||
</div>
|
||||
<div class="form-group screen-chekbox">
|
||||
<input id="screen-sharing" type="checkbox" name="screenshare">
|
||||
<label for="screen-sharing">Share screen?</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button type="submit">JOIN</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div id="session" style="display: none;">
|
||||
<h1 id="session-header"></h1>
|
||||
<input type="button" onclick="leaveSession()" value="LEAVE">
|
||||
<div id="room" style="display: none;">
|
||||
<h1 id="room-header"></h1>
|
||||
<input type="button" onclick="leaveRoom()" value="LEAVE">
|
||||
<div>
|
||||
<div id="publisher">
|
||||
<div id="local-participant">
|
||||
<h3>YOU</h3>
|
||||
</div>
|
||||
<div id="subscriber">
|
||||
<div id="remote-participants">
|
||||
<h3>OTHERS</h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,126 +1,255 @@
|
||||
const ipcRenderer = require('electron').ipcRenderer;
|
||||
const { BrowserWindow } = require('@electron/remote');
|
||||
|
||||
const { OpenVidu } = require('openvidu-browser');
|
||||
const { Room, RoomEvent } = require('livekit-client');
|
||||
const axios = require('axios');
|
||||
|
||||
var openvidu;
|
||||
var session;
|
||||
var room;
|
||||
var publisher;
|
||||
var mySessionId;
|
||||
var myParticipantName;
|
||||
var myRoomName;
|
||||
var isScreenShared = false;
|
||||
var screenSharePublication;
|
||||
|
||||
ipcRenderer.on('screen-share-ready', (event, message) => {
|
||||
if (!!message) {
|
||||
// User has chosen a screen to share. screenId is message parameter
|
||||
showSession();
|
||||
publisher = openvidu.initPublisher("publisher", {
|
||||
videoSource: "screen:" + message
|
||||
});
|
||||
joinSession();
|
||||
}
|
||||
ipcRenderer.on('screen-share-ready', async (event, sourceId) => {
|
||||
if (sourceId) {
|
||||
// User has chosen a screen to share. screenId is message parameter
|
||||
showRoom();
|
||||
await joinRoom();
|
||||
|
||||
try {
|
||||
const stream = await navigator.mediaDevices.getUserMedia({
|
||||
audio: false,
|
||||
video: {
|
||||
mandatory: {
|
||||
chromeMediaSource: 'desktop',
|
||||
chromeMediaSourceId: sourceId,
|
||||
},
|
||||
},
|
||||
});
|
||||
console.log('screenVideoTrack ', stream);
|
||||
|
||||
const track = stream.getVideoTracks()[0];
|
||||
const screenPublication = await room.localParticipant.publishTrack(track);
|
||||
const element = screenPublication.track.attach();
|
||||
element.className = 'removable';
|
||||
document.getElementById('local-participant').appendChild(element);
|
||||
} catch (error) {
|
||||
console.error('Error enabling screen sharing', error);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function initPublisher() {
|
||||
async function initPublisher() {
|
||||
|
||||
openvidu = new OpenVidu();
|
||||
const shareScreen = document.getElementById('screen-sharing').checked;
|
||||
|
||||
const shareScreen = document.getElementById("screen-sharing").checked;
|
||||
if (shareScreen) {
|
||||
openScreenShareModal();
|
||||
} else {
|
||||
publisher = openvidu.initPublisher("publisher");
|
||||
joinSession();
|
||||
}
|
||||
if (shareScreen) {
|
||||
openScreenShareModal();
|
||||
} else {
|
||||
await joinRoom();
|
||||
}
|
||||
}
|
||||
|
||||
async function joinSession() {
|
||||
async function joinRoom() {
|
||||
myRoomName = document.getElementById('roomName').value;
|
||||
myParticipantName = document.getElementById('participantName').value;
|
||||
|
||||
session = openvidu.initSession();
|
||||
session.on("streamCreated", function (event) {
|
||||
session.subscribe(event.stream, "subscriber");
|
||||
});
|
||||
// --- 1) Get a Room object ---
|
||||
room = new Room();
|
||||
|
||||
mySessionId = document.getElementById("sessionId").value;
|
||||
// --- 2) Specify the actions when events take place in the room ---
|
||||
|
||||
const token = await getToken(mySessionId);
|
||||
// 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);
|
||||
});
|
||||
|
||||
await session.connect(token, { clientData: 'OpenVidu Electron' });
|
||||
showSession();
|
||||
session.publish(publisher);
|
||||
// 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();
|
||||
});
|
||||
|
||||
const token = await getToken(myRoomName, myParticipantName);
|
||||
const livekitUrl = getLivekitUrlFromMetadata(token);
|
||||
|
||||
await room.connect(livekitUrl, token);
|
||||
|
||||
showRoom();
|
||||
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);
|
||||
}
|
||||
|
||||
function leaveSession() {
|
||||
session.disconnect();
|
||||
hideSession();
|
||||
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('local-participant').appendChild(element);
|
||||
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
|
||||
function showSession() {
|
||||
document.getElementById("session-header").innerText = mySessionId;
|
||||
document.getElementById("join").style.display = "none";
|
||||
document.getElementById("session").style.display = "block";
|
||||
async function stopScreenSharing() {
|
||||
try {
|
||||
await room.localParticipant?.setScreenShareEnabled(false);
|
||||
isScreenShared = false;
|
||||
const trackSid = screenSharePublication?.trackSid;
|
||||
|
||||
if (trackSid) {
|
||||
document.getElementById(trackSid)?.remove();
|
||||
}
|
||||
screenSharePublication = undefined;
|
||||
} catch (error) {
|
||||
console.error('Error stopping screen sharing', error);
|
||||
}
|
||||
}
|
||||
|
||||
function hideSession() {
|
||||
document.getElementById("join").style.display = "block";
|
||||
document.getElementById("session").style.display = "none";
|
||||
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();
|
||||
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 removeAllUserData() {
|
||||
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);
|
||||
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();
|
||||
win.setMenu(null);
|
||||
// win.webContents.openDevTools();
|
||||
|
||||
var theUrl = 'file://' + __dirname + '/modal.html'
|
||||
win.loadURL(theUrl);
|
||||
var theUrl = 'file://' + __dirname + '/modal.html';
|
||||
win.loadURL(theUrl);
|
||||
}
|
||||
|
||||
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 Session and a Token to
|
||||
* 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.
|
||||
*
|
||||
* Visit https://docs.openvidu.io/en/stable/application-server to learn
|
||||
* more about the integration of OpenVidu in your application server.
|
||||
*
|
||||
*/
|
||||
|
||||
var APPLICATION_SERVER_URL = 'http://localhost:5000/';
|
||||
|
||||
async function getToken(mySessionId) {
|
||||
const sessionId = await createSession(mySessionId);
|
||||
return await createToken(sessionId);
|
||||
}
|
||||
async function getToken(roomName, participantName) {
|
||||
try {
|
||||
const response = await axios.post(
|
||||
APPLICATION_SERVER_URL + 'token',
|
||||
{
|
||||
roomName,
|
||||
participantName,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
async function createSession(sessionId) {
|
||||
const response = await axios.post(APPLICATION_SERVER_URL + 'api/sessions', { customSessionId: sessionId }, {
|
||||
headers: { 'Content-Type': 'application/json', },
|
||||
});
|
||||
return response.data; // The sessionId
|
||||
console.log(response.data);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('No connection to application server', error);
|
||||
}
|
||||
}
|
||||
|
||||
async function createToken(sessionId) {
|
||||
const response = await axios.post(APPLICATION_SERVER_URL + 'api/sessions/' + sessionId + '/connections', {}, {
|
||||
headers: { 'Content-Type': 'application/json', },
|
||||
});
|
||||
return response.data; // The token
|
||||
}
|
||||
@ -1,94 +1,177 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta
|
||||
http-equiv="Content-Security-Policy"
|
||||
content="script-src 'self' 'unsafe-inline';"
|
||||
/>
|
||||
<title>Screen Sharing Selection</title>
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';" />
|
||||
<title>OpenVidu Electron</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
background-color: #f0f0f0;
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
<style>
|
||||
#list-of-screens {
|
||||
margin-bottom: 60px;
|
||||
}
|
||||
h1 {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
#footer {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 25px;
|
||||
background: white;
|
||||
text-align: right;
|
||||
padding: 10px 0px 10px 0px;
|
||||
}
|
||||
#list-of-screens {
|
||||
/* display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
grid-gap: 10px;
|
||||
justify-items: center;
|
||||
align-items: start;
|
||||
margin: 20px 0; */
|
||||
|
||||
#share-btn {
|
||||
margin-right: 20px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
gap: 16px; /* Adjust the gap as needed for spacing */
|
||||
align-items: stretch; /* Makes all rows the same height */
|
||||
|
||||
<body>
|
||||
<h1>Share your screen</h1>
|
||||
<div id="list-of-screens"></div>
|
||||
<div id="footer">
|
||||
<button id="cancel-btn" onclick="cancelSelection()">Cancel</button>
|
||||
<button id="share-btn" onclick="sendScreenSelection()" disabled>Share</button>
|
||||
</div>
|
||||
</body>
|
||||
/* Additional styling for the grid container */
|
||||
padding: 16px;
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
<script>
|
||||
var availableScreens = [];
|
||||
var htmlElements = [];
|
||||
var selectedElement;
|
||||
.screen-item {
|
||||
border: 1px solid #ccc;
|
||||
padding: 10px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
transition: background-color 0.3s, border-color 0.3s;
|
||||
}
|
||||
|
||||
const desktopCapturer = require('@electron/remote').desktopCapturer;
|
||||
const ipcRenderer = require('electron').ipcRenderer;
|
||||
.screen-item:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
// Call Electron API to list all available screens
|
||||
desktopCapturer.getSources({
|
||||
types: ['window', 'screen']
|
||||
}).then(async sources => {
|
||||
const list = document.getElementById("list-of-screens");
|
||||
sources.forEach(source => {
|
||||
// Add new element to the list with the thumbnail of the screen
|
||||
var el = document.createElement("div");
|
||||
el.onclick = () => {
|
||||
// Style the new selected screen and store it as the current selection
|
||||
htmlElements.forEach(e => {
|
||||
e.style.border = "none";
|
||||
e.style.background = "none";
|
||||
})
|
||||
el.style.border = "2px solid #0088aa";
|
||||
el.style.background = "rgba(0, 0, 0, 0.06)";
|
||||
selectedElement = el;
|
||||
document.getElementById("share-btn").disabled = false;
|
||||
}
|
||||
// Store the new source and the new created HTML element
|
||||
availableScreens.push(source);
|
||||
htmlElements.push(el);
|
||||
var img = document.createElement("img");
|
||||
var name = document.createElement("span");
|
||||
img.src = source.thumbnail.toDataURL();
|
||||
name.innerHTML = source.name;
|
||||
// Append new elements to the template
|
||||
el.appendChild(img);
|
||||
el.appendChild(name);
|
||||
list.appendChild(el);
|
||||
});
|
||||
});
|
||||
.screen-item img {
|
||||
max-width: 100px;
|
||||
max-height: 100px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
function sendScreenSelection() {
|
||||
ipcRenderer.send('screen-share-selected', availableScreens[htmlElements.indexOf(selectedElement)].id);
|
||||
closeWindow();
|
||||
}
|
||||
.screen-item span {
|
||||
padding: 10px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
function cancelSelection() {
|
||||
ipcRenderer.send('screen-share-selected', undefined);
|
||||
closeWindow();
|
||||
}
|
||||
#footer {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
width: 95%;
|
||||
height: 50px;
|
||||
background: #f0f0f0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 0px;
|
||||
}
|
||||
|
||||
function closeWindow() {
|
||||
require('@electron/remote').getCurrentWindow().close();
|
||||
}
|
||||
</script>
|
||||
#share-btn,
|
||||
#cancel-btn {
|
||||
color: #fff;
|
||||
padding: 10px 20px;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#share-btn {
|
||||
background-color: #007bff;
|
||||
}
|
||||
#cancel-btn {
|
||||
background-color: rgb(151, 28, 28);
|
||||
}
|
||||
|
||||
.screen-name {
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Select a Screen to Share</h1>
|
||||
<div id="list-of-screens"></div>
|
||||
<div id="footer">
|
||||
<button id="cancel-btn" onclick="cancelSelection()">Cancel</button>
|
||||
<button id="share-btn" onclick="sendScreenSelection()" disabled>
|
||||
Share Screen
|
||||
</button>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<script>
|
||||
var availableScreens = [];
|
||||
var htmlElements = [];
|
||||
var selectedElement;
|
||||
|
||||
const desktopCapturer = require('@electron/remote').desktopCapturer;
|
||||
const ipcRenderer = require('electron').ipcRenderer;
|
||||
|
||||
// Call Electron API to list all available screens
|
||||
desktopCapturer
|
||||
.getSources({
|
||||
types: ['window', 'screen'],
|
||||
})
|
||||
.then(async (sources) => {
|
||||
const list = document.getElementById('list-of-screens');
|
||||
sources.forEach((source) => {
|
||||
// Add new element to the list with the thumbnail of the screen
|
||||
var el = document.createElement('div');
|
||||
el.className = 'screen-item';
|
||||
el.onclick = () => {
|
||||
// Style the new selected screen and store it as the current selection
|
||||
htmlElements.forEach((e) => {
|
||||
e.style.border = 'none';
|
||||
e.style.background = 'none';
|
||||
});
|
||||
el.style.border = '2px solid #0088aa';
|
||||
el.style.background = 'rgba(0, 0, 0, 0.06)';
|
||||
selectedElement = el;
|
||||
document.getElementById('share-btn').disabled = false;
|
||||
};
|
||||
// Store the new source and the new created HTML element
|
||||
availableScreens.push(source);
|
||||
htmlElements.push(el);
|
||||
var img = document.createElement('img');
|
||||
var name = document.createElement('span');
|
||||
img.src = source.thumbnail.toDataURL();
|
||||
name.innerHTML = source.name;
|
||||
img.style.width = '100px';
|
||||
img.style.height = '100px';
|
||||
|
||||
// Append new elements to the template
|
||||
el.appendChild(img);
|
||||
el.appendChild(name);
|
||||
list.appendChild(el);
|
||||
});
|
||||
});
|
||||
|
||||
function sendScreenSelection() {
|
||||
ipcRenderer.send(
|
||||
'screen-share-selected',
|
||||
availableScreens[htmlElements.indexOf(selectedElement)].id
|
||||
);
|
||||
closeWindow();
|
||||
}
|
||||
|
||||
function cancelSelection() {
|
||||
ipcRenderer.send('screen-share-selected', undefined);
|
||||
closeWindow();
|
||||
}
|
||||
|
||||
function closeWindow() {
|
||||
require('@electron/remote').getCurrentWindow().close();
|
||||
}
|
||||
</script>
|
||||
</html>
|
||||
|
||||
@ -14,13 +14,13 @@ body {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#publisher {
|
||||
#local-participant {
|
||||
float: left;
|
||||
margin: 10px;
|
||||
width: 40%;
|
||||
}
|
||||
|
||||
#subscriber {
|
||||
#remote-participants {
|
||||
float: right;
|
||||
margin: 10px;
|
||||
width: 40%;
|
||||
@ -30,4 +30,52 @@ 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;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background: #0056b3;
|
||||
}
|
||||
|
||||
.screen-chekbox {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user