Skip to content

Commit e622583

Browse files
Colin TroisemaineColin Troisemaine
Colin Troisemaine
authored and
Colin Troisemaine
committed
Working video source selection
1 parent aa62046 commit e622583

10 files changed

+164
-107
lines changed

electron_app/control_menu.html

-28
This file was deleted.

electron_app/main.js

-40
This file was deleted.

electron_app/renderer.js

-38
This file was deleted.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "live_desktop_translator",
33
"version": "0.0.1",
4-
"main": "electron_app/main.js",
4+
"main": "src/main.js",
55
"scripts": {
66
"start": "electron-forge start",
77
"test": "echo \"Error: no test specified\" && exit 1",

src/bulma.min.css

+3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
File renamed without changes.

src/control_menu.html

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1">
6+
<meta http-equiv="Content-Security-Policy"
7+
content="img-src 'self' data: filesystem:; default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; script-src-elem 'self' 'unsafe-eval' https://unpkg.com">
8+
9+
<title>Live Desktop Translator</title>
10+
<link rel="stylesheet" href="bulma.min.css">
11+
<link rel="stylesheet" href="control_menu.html">
12+
<script defer src="renderer.js"></script>
13+
</head>
14+
15+
<body>
16+
<section class="section">
17+
<div class="container" style="text-align: center;">
18+
<button id="startButton" class="button">Start</button>
19+
<button id="stopButton" class="button">Stop</button>
20+
21+
<hr/>
22+
23+
<video width="800" height="450" autoplay></video>
24+
25+
<hr/>
26+
27+
<button id="videoSelectButton" class="button">Choose a Video Source</button>
28+
</div>
29+
</section>
30+
</body>
31+
</html>

src/main.js

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
const { app, BrowserWindow, ipcMain, desktopCapturer, session, Menu } = require('electron')
2+
const path = require('node:path')
3+
4+
let selectedSourceId = null; // Store the selected source ID
5+
6+
app.whenReady().then(() => {
7+
const win = new BrowserWindow({
8+
width: 1000,
9+
height: 700,
10+
webPreferences: {
11+
preload: path.join(__dirname, 'preload.js'),
12+
contextIsolation: true,
13+
enableRemoteModule: false,
14+
nodeIntegration: false,
15+
}
16+
});
17+
18+
win.loadFile(path.join(__dirname, 'control_menu.html'));
19+
20+
session.defaultSession.setDisplayMediaRequestHandler((request, callback) => {
21+
if (selectedSourceId) {
22+
desktopCapturer.getSources({ types: ['screen', 'window'] }).then((sources) => {
23+
const selectedSource = sources.find(source => source.id === selectedSourceId);
24+
if (selectedSource) {
25+
// Grant access to the selected screen or window
26+
callback({ video: selectedSource });
27+
} else {
28+
console.log("Selected source not found!");
29+
}
30+
}).catch(err => console.error("Error getting sources: ", err));
31+
} else {
32+
console.log("No source selected.");
33+
}
34+
}, { useSystemPicker: true });
35+
36+
// Save the selected source ID when it's chosen from the menu
37+
ipcMain.on('select-source', (event, sourceId) => {
38+
selectedSourceId = sourceId; // Update the selected source ID
39+
console.log(`Source selected: ${sourceId}`);
40+
});
41+
42+
app.on('activate', () => {
43+
if (BrowserWindow.getAllWindows().length === 0) createWindow()
44+
})
45+
46+
ipcMain.handle('DESKTOP_CAPTURER_GET_SOURCES', async (event, opts) => {
47+
return await desktopCapturer.getSources(opts);
48+
});
49+
50+
// Handle showing the popup menu
51+
ipcMain.on('show-popup-menu', (event, menuTemplate) => {
52+
const menu = Menu.buildFromTemplate(menuTemplate.map(item => ({
53+
label: item.label,
54+
click: () => {
55+
// console.log("Selected source with ID: " + item.id + " and name: " + item.label);
56+
win.webContents.send('source-selected', item.id)
57+
selectedSourceId = item.id;
58+
}
59+
})));
60+
61+
// Show the popup menu in the appropriate window
62+
menu.popup(BrowserWindow.fromWebContents(event.sender));
63+
});
64+
65+
win.removeMenu()
66+
win.webContents.openDevTools()
67+
});
68+
69+
app.on('window-all-closed', () => {
70+
if (process.platform !== 'darwin') {
71+
app.quit()
72+
}
73+
})

src/preload.js

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
const { contextBridge, ipcRenderer } = require('electron');
2+
3+
contextBridge.exposeInMainWorld('electron', {
4+
getSources: (opts) => ipcRenderer.invoke('DESKTOP_CAPTURER_GET_SOURCES', opts),
5+
showPopupMenu: (menuTemplate) => ipcRenderer.send('show-popup-menu', menuTemplate),
6+
onSourceSelected: (callback) => ipcRenderer.on('source-selected', (event, sourceId) => {callback(sourceId);})
7+
});

src/renderer.js

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
const video = document.querySelector('video')
2+
const startButton = document.getElementById('startButton')
3+
const stopButton = document.getElementById('stopButton')
4+
const videoSelectButton = document.getElementById('videoSelectButton')
5+
6+
let selectedSourceId = null; // Track the selected source ID
7+
8+
startButton.addEventListener('click', () => {
9+
// Prevent starting if no source is selected
10+
if (!selectedSourceId) {
11+
console.log("No source selected!");
12+
alert("Please select a source first!");
13+
return;
14+
}
15+
16+
navigator.mediaDevices.getDisplayMedia({
17+
audio: false,
18+
video: {
19+
width: 800,
20+
height: 450,
21+
frameRate: 30
22+
}
23+
}).then(stream => {
24+
video.srcObject = stream
25+
video.onloadedmetadata = (e) => video.play()
26+
}).catch(e => console.log(e))
27+
})
28+
29+
stopButton.addEventListener('click', () => {
30+
video.pause()
31+
})
32+
33+
videoSelectButton.addEventListener('click', async () => {
34+
// Use the exposed API from preload.js to get the sources from the main process
35+
const inputSources = await window.electron.getSources({ types: ['window', 'screen'] });
36+
37+
// Create a menu with only serializable properties: source `name` and `id`
38+
const menuTemplate = inputSources.map(source => ({
39+
label: source.name,
40+
id: source.id
41+
}));
42+
43+
// Show the popup menu
44+
window.electron.showPopupMenu(menuTemplate);
45+
});
46+
47+
window.electron.onSourceSelected((sourceId) => {
48+
selectedSourceId = sourceId;
49+
});

0 commit comments

Comments
 (0)