Skip to content

Instantly share code, notes, and snippets.

@vuggy17
Created January 4, 2023 04:45
Show Gist options
  • Select an option

  • Save vuggy17/3b2f87d03b0ec934f747f43f2d6530e0 to your computer and use it in GitHub Desktop.

Select an option

Save vuggy17/3b2f87d03b0ec934f747f43f2d6530e0 to your computer and use it in GitHub Desktop.

Revisions

  1. vuggy17 revised this gist Jan 4, 2023. 6 changed files with 235 additions and 21 deletions.
    46 changes: 29 additions & 17 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -1,19 +1,31 @@
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
    <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'">
    <link href="./styles.css" rel="stylesheet">
    <title>Hello World!</title>
    </head>
    <body>
    <h1>Hello World!</h1>
    We are using Node.js <span id="node-version"></span>,
    Chromium <span id="chrome-version"></span>,
    and Electron <span id="electron-version"></span>.

    <!-- You can also require other files to run in this process -->
    <script src="./renderer.js"></script>
    </body>
    </html>

    <head>
    <meta charset="UTF-8">
    <!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->

    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/css/bulma.min.css" />
    <link href="./styles.css" rel="stylesheet">
    <title>Hello World!</title>
    </head>

    <body>
    <h1>⚡ Electron Screen Recorder</h1>

    <video></video>

    <button id="startBtn" class="button is-primary">Start</button>
    <button id="stopBtn" class="button is-warning">Stop</button>

    <hr />

    <button id="videoSelectBtn" class="button is-text">
    Choose a Video Source
    </button>

    <!-- You can also require other files to run in this process -->
    <script src="./renderer.js"></script>
    </body>

    </html>
    47 changes: 44 additions & 3 deletions main.js
    Original file line number Diff line number Diff line change
    @@ -1,30 +1,71 @@
    // Modules to control application life and create native browser window
    const {app, BrowserWindow} = require('electron')
    const { app, BrowserWindow, ipcMain, desktopCapturer, screen } = require('electron')
    const path = require('path')

    function createWindow () {
    const remoteMain = require('@electron/remote/main');
    remoteMain.initialize()

    function createWindow() {
    // Create the browser window.
    const mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
    preload: path.join(__dirname, 'preload.js')
    preload: path.join(__dirname, 'preload.js'),
    contextIsolation: false,
    nodeIntegration: true
    }
    })

    // and load the index.html of the app.
    mainWindow.loadFile('index.html')
    remoteMain.enable(mainWindow.webContents)

    // Open the DevTools.
    // mainWindow.webContents.openDevTools()
    }


    function createProtectedWindow() {
    // Create the browser window.
    let display = screen.getPrimaryDisplay();
    let width = display.bounds.width;

    const window = new BrowserWindow({
    width: 800,
    height: 600,
    x: width - 600,
    y: 0,
    transparent: true,
    alwaysOnTop: true,
    frame: false,
    webPreferences: {
    preload: path.join(__dirname, 'preload.js')
    }
    })

    // and load the index.html of the app.
    window.loadFile('protected-index.html')

    window.setIgnoreMouseEvents(true);
    window.setContentProtection(true)
    // Open the DevTools.
    // window.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(() => {
    createProtectedWindow()
    createWindow()

    ipcMain.handle('get-source', () => {
    return desktopCapturer.getSources({
    types: ['window', 'screen']
    });
    })

    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.
    25 changes: 25 additions & 0 deletions protected-index.html
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,25 @@
    <!-- Empty -->
    <!DOCTYPE html>
    <html>

    <head>
    <meta charset="UTF-8">
    <!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
    <meta http-equiv="Content-Security-Policy"
    content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/css/bulma.min.css" />
    <link href="./styles.css" rel="stylesheet">
    <title>Protected window</title>
    </head>

    <body>
    <h1>Hello World!</h1>
    We are using Node.js <span id="node-version"></span>,
    Chromium <span id="chrome-version"></span>,
    and Electron <span id="electron-version"></span>.
    <canvas id="canvas"/>
    <!-- You can also require other files to run in this process -->
    <script src="./protected-renderer.js"> </script>
    </body>

    </html>
    10 changes: 10 additions & 0 deletions protected-renderer.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,10 @@
    // Empty

    const c = document.getElementsByTagName('canvas')[0]
    const ctx = c.getContext('2d')
    if (ctx) {
    ctx.fillStyle = 'rgb(0, 197, 200)';
    ctx.fillRect(25, 25, 100, 100);
    ctx.clearRect(45, 45, 60, 60);
    ctx.strokeRect(50, 50, 50, 50);
    }
    109 changes: 109 additions & 0 deletions renderer.js
    Original file line number Diff line number Diff line change
    @@ -5,3 +5,112 @@
    * `contextIsolation` is turned on. Use the contextBridge API in `preload.js`
    * to expose Node.js functionality from the main process.
    */

    const { ipcRenderer } = require('electron');

    const { writeFile } = require('fs');

    const { Menu } = require('@electron/remote')


    // Global state
    let mediaRecorder; // MediaRecorder instance to capture footage
    const recordedChunks = [];

    // Buttons
    const videoElement = document.querySelector('video');

    const startBtn = document.getElementById('startBtn');
    startBtn.onclick = e => {
    mediaRecorder.start();
    startBtn.classList.add('is-danger');
    startBtn.innerText = 'Recording';
    };

    const stopBtn = document.getElementById('stopBtn');

    stopBtn.onclick = e => {
    mediaRecorder.stop();
    startBtn.classList.remove('is-danger');
    startBtn.innerText = 'Start';
    };

    const videoSelectBtn = document.getElementById('videoSelectBtn');
    videoSelectBtn.onclick = getVideoSources;

    // Get the available video sources
    async function getVideoSources() {
    const inputSources = await ipcRenderer.invoke('get-source')

    const videoOptionsMenu = Menu.buildFromTemplate(
    inputSources.map(source => {
    return {
    label: source.name,
    click: () => selectSource(source)
    };
    })
    );


    videoOptionsMenu.popup();
    }

    // Change the videoSource window to record
    async function selectSource(source) {

    videoSelectBtn.innerText = source.name;

    const constraints = {
    audio: false,
    video: {
    mandatory: {
    chromeMediaSource: 'desktop',
    chromeMediaSourceId: source.id
    }
    }
    };

    // Create a Stream
    const stream = await navigator.mediaDevices
    .getUserMedia(constraints);

    // Preview the source in a video element
    videoElement.srcObject = stream;
    videoElement.play();

    // Create the Media Recorder
    const options = { mimeType: 'video/webm; codecs=vp9' };
    mediaRecorder = new MediaRecorder(stream, options);

    // Register Event Handlers
    mediaRecorder.ondataavailable = handleDataAvailable;
    mediaRecorder.onstop = handleStop;

    // Updates the UI
    }

    // Captures all recorded chunks
    function handleDataAvailable(e) {
    console.log('video data available');
    recordedChunks.push(e.data);
    }

    // Saves the video file on stop
    async function handleStop(e) {
    const blob = new Blob(recordedChunks, {
    type: 'video/webm; codecs=vp9'
    });

    const buffer = Buffer.from(await blob.arrayBuffer());

    alert('Video stoped')
    // const { filePath } = await dialog.showSaveDialog({
    // buttonLabel: 'Save video',
    // defaultPath: `vid-${Date.now()}.webm`
    // });

    // if (filePath) {
    // writeFile(filePath, buffer, () => console.log('video saved successfully!'));
    // }

    }
    19 changes: 18 additions & 1 deletion styles.css
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,20 @@
    /* styles.css */

    /* Add styles here to customize the appearance of your app */
    /* Add styles here to customize the appearance of your app */

    body {
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    padding: 0;
    margin: 0;
    }


    video {
    width: 100%;
    height: auto;
    }

    body {
    text-align: center;
    padding-top: 20px;
    }
  2. vuggy17 created this gist Jan 4, 2023.
    19 changes: 19 additions & 0 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,19 @@
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
    <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'">
    <link href="./styles.css" rel="stylesheet">
    <title>Hello World!</title>
    </head>
    <body>
    <h1>Hello World!</h1>
    We are using Node.js <span id="node-version"></span>,
    Chromium <span id="chrome-version"></span>,
    and Electron <span id="electron-version"></span>.

    <!-- You can also require other files to run in this process -->
    <script src="./renderer.js"></script>
    </body>
    </html>
    43 changes: 43 additions & 0 deletions main.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,43 @@
    // Modules to control application life and create native browser window
    const {app, BrowserWindow} = require('electron')
    const path = require('path')

    function createWindow () {
    // Create the browser window.
    const mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
    preload: path.join(__dirname, 'preload.js')
    }
    })

    // and load the index.html of the app.
    mainWindow.loadFile('index.html')

    // 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()

    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()
    })

    // 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.
    23 changes: 23 additions & 0 deletions package.json
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,23 @@
    {
    "name": "cute-country-drain-yuhvw",
    "productName": "cute-country-drain-yuhvw",
    "description": "My Electron application description",
    "keywords": [],
    "main": "./main.js",
    "version": "1.0.0",
    "author": "shelby",
    "scripts": {
    "start": "electron ."
    },
    "dependencies": {
    "@electron/remote": "2.0.9",
    "@electron-forge/cli": "6.0.4",
    "@electron-forge/maker-deb": "6.0.4",
    "@electron-forge/maker-rpm": "6.0.4",
    "@electron-forge/maker-squirrel": "6.0.4",
    "@electron-forge/maker-zip": "6.0.4"
    },
    "devDependencies": {
    "electron": "22.0.0"
    }
    }
    17 changes: 17 additions & 0 deletions preload.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,17 @@
    /**
    * The preload script runs before. It has access to web APIs
    * as well as Electron's renderer process modules and some
    * polyfilled Node.js functions.
    *
    * https://www.electronjs.org/docs/latest/tutorial/sandbox
    */
    window.addEventListener('DOMContentLoaded', () => {
    const replaceText = (selector, text) => {
    const element = document.getElementById(selector)
    if (element) element.innerText = text
    }

    for (const type of ['chrome', 'node', 'electron']) {
    replaceText(`${type}-version`, process.versions[type])
    }
    })
    7 changes: 7 additions & 0 deletions renderer.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,7 @@
    /**
    * This file is loaded via the <script> tag in the index.html file and will
    * be executed in the renderer process for that window. No Node.js APIs are
    * available in this process because `nodeIntegration` is turned off and
    * `contextIsolation` is turned on. Use the contextBridge API in `preload.js`
    * to expose Node.js functionality from the main process.
    */
    3 changes: 3 additions & 0 deletions styles.css
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,3 @@
    /* styles.css */

    /* Add styles here to customize the appearance of your app */