mirror of
https://github.com/rjNemo/screen_recorder
synced 2026-06-06 02:36:49 +00:00
v0 release
This commit is contained in:
commit
812a542dfe
7 changed files with 4915 additions and 0 deletions
89
.gitignore
vendored
Normal file
89
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||||
|
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
node_modules/
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# TypeScript v1 declaration files
|
||||||
|
typings/
|
||||||
|
|
||||||
|
# TypeScript cache
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variables file
|
||||||
|
.env
|
||||||
|
.env.test
|
||||||
|
|
||||||
|
# parcel-bundler cache (https://parceljs.org/)
|
||||||
|
.cache
|
||||||
|
|
||||||
|
# next.js build output
|
||||||
|
.next
|
||||||
|
|
||||||
|
# nuxt.js build output
|
||||||
|
.nuxt
|
||||||
|
|
||||||
|
# vuepress build output
|
||||||
|
.vuepress/dist
|
||||||
|
|
||||||
|
# Serverless directories
|
||||||
|
.serverless/
|
||||||
|
|
||||||
|
# FuseBox cache
|
||||||
|
.fusebox/
|
||||||
|
|
||||||
|
# DynamoDB Local files
|
||||||
|
.dynamodb/
|
||||||
|
|
||||||
|
# Webpack
|
||||||
|
.webpack/
|
||||||
|
|
||||||
|
# Electron-Forge
|
||||||
|
out/
|
||||||
4588
package-lock.json
generated
Normal file
4588
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
58
package.json
Normal file
58
package.json
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
{
|
||||||
|
"name": "screenrecorder",
|
||||||
|
"productName": "screenrecorder",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "My Electron application description",
|
||||||
|
"main": "src/index.js",
|
||||||
|
"scripts": {
|
||||||
|
"start": "electron-forge start",
|
||||||
|
"package": "electron-forge package",
|
||||||
|
"make": "electron-forge make",
|
||||||
|
"publish": "electron-forge publish",
|
||||||
|
"lint": "echo \"No linting configured\""
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": {
|
||||||
|
"name": "Ruidy Nemausat",
|
||||||
|
"email": "ruidy.nemausat@gmail.com"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"config": {
|
||||||
|
"forge": {
|
||||||
|
"packagerConfig": {},
|
||||||
|
"makers": [
|
||||||
|
{
|
||||||
|
"name": "@electron-forge/maker-squirrel",
|
||||||
|
"config": {
|
||||||
|
"name": "screenrecorder"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "@electron-forge/maker-zip",
|
||||||
|
"platforms": [
|
||||||
|
"darwin"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "@electron-forge/maker-deb",
|
||||||
|
"config": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "@electron-forge/maker-rpm",
|
||||||
|
"config": {}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"electron-squirrel-startup": "^1.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@electron-forge/cli": "^6.0.0-beta.50",
|
||||||
|
"@electron-forge/maker-deb": "^6.0.0-beta.50",
|
||||||
|
"@electron-forge/maker-rpm": "^6.0.0-beta.50",
|
||||||
|
"@electron-forge/maker-squirrel": "^6.0.0-beta.50",
|
||||||
|
"@electron-forge/maker-zip": "^6.0.0-beta.50",
|
||||||
|
"electron": "8.2.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
30
src/index.css
Normal file
30
src/index.css
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica,
|
||||||
|
Arial, sans-serif;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background-color: aliceblue;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
align-items: center;
|
||||||
|
margin: auto;
|
||||||
|
margin-top: 20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
video {
|
||||||
|
background-color: black;
|
||||||
|
width: 100%;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
div {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
position: relative;
|
||||||
|
bottom: -10px;
|
||||||
|
margin: auto;
|
||||||
|
width: 60%;
|
||||||
|
padding-top: 10px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
}
|
||||||
27
src/index.html
Normal file
27
src/index.html
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>📹 Screen Recorder</title>
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
|
||||||
|
integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh"
|
||||||
|
crossorigin="anonymous"
|
||||||
|
/>
|
||||||
|
<link rel="stylesheet" href="index.css" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>📹 Screen Recorder</h1>
|
||||||
|
<video src=""></video>
|
||||||
|
<div>
|
||||||
|
<button id="startBtn" class="btn btn-primary">Start</button>
|
||||||
|
<button id="stopBtn" class="btn btn-warning">Stop</button>
|
||||||
|
|
||||||
|
<button id="videoSelectBtn" class="btn btn-outline-primary">
|
||||||
|
Video source
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<script defer src="render.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
35
src/index.js
Normal file
35
src/index.js
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
const { app, BrowserWindow } = require("electron");
|
||||||
|
const { join } = require("path");
|
||||||
|
|
||||||
|
if (require("electron-squirrel-startup")) {
|
||||||
|
app.quit();
|
||||||
|
}
|
||||||
|
|
||||||
|
const createWindow = () => {
|
||||||
|
const mainWindow = new BrowserWindow({
|
||||||
|
width: 800,
|
||||||
|
height: 600,
|
||||||
|
webPreferences: {
|
||||||
|
nodeIntegration: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
mainWindow.loadFile(join(__dirname, "index.html"));
|
||||||
|
|
||||||
|
// Open the DevTools.
|
||||||
|
// mainWindow.webContents.openDevTools();
|
||||||
|
};
|
||||||
|
|
||||||
|
app.on("ready", createWindow);
|
||||||
|
|
||||||
|
app.on("window-all-closed", () => {
|
||||||
|
if (process.platform !== "darwin") {
|
||||||
|
app.quit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
app.on("activate", () => {
|
||||||
|
if (BrowserWindow.getAllWindows().length === 0) {
|
||||||
|
createWindow();
|
||||||
|
}
|
||||||
|
});
|
||||||
88
src/render.js
Normal file
88
src/render.js
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
const { desktopCapturer, remote } = require("electron");
|
||||||
|
const { writeFile } = require("fs");
|
||||||
|
|
||||||
|
const { dialog, Menu } = remote;
|
||||||
|
|
||||||
|
let mediaRecorder;
|
||||||
|
const recordedChunks = [];
|
||||||
|
|
||||||
|
const startBtn = document.getElementById("startBtn");
|
||||||
|
startBtn.onclick = (e) => {
|
||||||
|
mediaRecorder.start();
|
||||||
|
startBtn.classList.add("btn-danger");
|
||||||
|
startBtn.innerText = "Recording";
|
||||||
|
};
|
||||||
|
|
||||||
|
const stopBtn = document.getElementById("stopBtn");
|
||||||
|
stopBtn.onclick = (e) => {
|
||||||
|
mediaRecorder.stop();
|
||||||
|
startBtn.classList.remove("btn-danger");
|
||||||
|
startBtn.innerText = "Start";
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDataAvailable = (e) => {
|
||||||
|
recordedChunks.push(e.data);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleStop = async (e) => {
|
||||||
|
const blob = new Blob(recordedChunks, {
|
||||||
|
type: "video/webm; codecs=vp9",
|
||||||
|
});
|
||||||
|
|
||||||
|
const buffer = Buffer.from(await blob.arrayBuffer());
|
||||||
|
|
||||||
|
const { filePath } = await dialog.showSaveDialog({
|
||||||
|
buttonLabel: "Save",
|
||||||
|
defaultPath: `capture-${Date.now()}.webm`,
|
||||||
|
});
|
||||||
|
writeFile(filePath, buffer, () => alert("Video saved successfully"));
|
||||||
|
};
|
||||||
|
|
||||||
|
const videoPlayer = document.querySelector("video");
|
||||||
|
const videoSelectBtn = document.getElementById("videoSelectBtn");
|
||||||
|
|
||||||
|
// selectsource changes the videoSource to record.
|
||||||
|
const selectSource = async (src) => {
|
||||||
|
videoSelectBtn.innerText = `Source: ${src.name}`;
|
||||||
|
|
||||||
|
const constraints = {
|
||||||
|
audio: false,
|
||||||
|
video: {
|
||||||
|
mandatory: {
|
||||||
|
chromeMediaSource: "desktop",
|
||||||
|
chromeMediaSourceId: src.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const stream = await navigator.mediaDevices.getUserMedia(constraints);
|
||||||
|
|
||||||
|
// Preview the source in videoPlayer
|
||||||
|
videoPlayer.srcObject = stream;
|
||||||
|
videoPlayer.play();
|
||||||
|
|
||||||
|
const options = { mimeType: "video/webm; codecs=vp9" };
|
||||||
|
mediaRecorder = new MediaRecorder(stream, options);
|
||||||
|
|
||||||
|
mediaRecorder.ondataavailable = handleDataAvailable;
|
||||||
|
mediaRecorder.onstop = handleStop;
|
||||||
|
};
|
||||||
|
|
||||||
|
// videoSources gets the available video sources.
|
||||||
|
const videoSources = async () => {
|
||||||
|
const inputSources = await desktopCapturer.getSources({
|
||||||
|
types: ["window", "screen"],
|
||||||
|
});
|
||||||
|
const optionsMenu = Menu.buildFromTemplate(
|
||||||
|
inputSources.map((src) => {
|
||||||
|
return {
|
||||||
|
label: src.name,
|
||||||
|
click: () => selectSource(src),
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
optionsMenu.popup();
|
||||||
|
};
|
||||||
|
|
||||||
|
videoSelectBtn.onclick = videoSources;
|
||||||
Loading…
Reference in a new issue