写在前面
前段时间研究如何使用nodejs
静默截屏,搜索了很多库,发现了screenshot-desktop这个项目。
我选它的原因只有一个,在内存中生成图片Promise<Buffer>
对象,不在磁盘上生成临时文件。
一、安装
npm install screenshot-desktop
用TypeScript
需要额外安装
npm install @types/screenshot-desktop
二、基于screenshot-desktop定义截屏业务(可以直接拿来用)
import screenshotDesktop from "screenshot-desktop";
import * as fs from "fs";
class ScreenService {
/**
* 截屏事件开关
*/
private suspendFag: boolean = false;
private indexSuspend: number = 0;
/**
* 截取屏幕
*/
createScreenshot(): Promise<Buffer> {
return screenshotDesktop({format: "png"})
.then(img => {
return img;
}).catch(err => {
console.log('截屏失败', err);
return err;
})
}
/**
* 定时器触发截取屏幕
* @param callback 回调函数处理截取的图片buffer
*/
startScreenshotTimer(callback: Function) {
let time = new Date();
if (this.isSuspend()) {
if (this.indexSuspend > 0) return
console.log("截屏暂停[", this.indexSuspend, "]", time)
this.indexSuspend++
} else {
console.log("我开始截屏了", time)
this.createScreenshot().then((img): void => {
callback(img);
})
}
}
isSuspend(): boolean {
return this.suspendFag
}
/**
* 暂停截取
*/
suspend() {
if (this.isSuspend()) return
this.suspendFag = true;
console.log("截屏任务暂停")
}
/**
* 恢复截取
*/
continued() {
if (!this.isSuspend()) return
this.suspendFag = false;
console.log("截屏任务继续开始")
}
}
export const screenService = new ScreenService()
三、使用
/**
* 截取屏幕的定时,单位ms
*/
const SCREENSHOT_INTERVAL = 1000;
setInterval((): void => {
//todo test
if (screenService.isSuspend()) return;
if (this.socketId == undefined) {
console.warn("没有 socketId")
screenService.suspend()
return
}
if (this.rooms.length <= 0) {
console.warn("没有要接收的rooms")
screenService.suspend()
return
}
if (this.desktops.length >= this.desktopsMax) {
console.warn("desktops超过上限,放弃本次增加:", new Date())
screenService.suspend()
return
}
//这里最重要的调用截屏方法
screenService.startScreenshotTimer(((imgBuffer: Buffer): void => {
// imgBuffer 对象就是截取到的屏幕数据,这里可以开始压缩图片,写磁盘等操作
}))
}, SCREENSHOT_INTERVAL)
四、插曲
发现打成exe
程序后,无法在windows
下工作。在开源项目下找到了一个run correctly with bundlers问题跟我遇到的一样,但是已经提交了修复方案,作者没有合并。
于是我按照说明进行本地源代码修改,成功搞定。作者回复我说已经进行合并了。但截止本文发布时间,screenshot-desktop
版本为1.12.7
,@types/screenshot-desktop
版本为1.12.0
,并没有新的版本发布。也就意味着需要使用本地修改源代码的方案来使用。
五、更改源代码以支持windows打包运行
- 新建一个文件名
index.js
(必须叫这个名字)下面是完整的文件内容
const Promise = require('pinkie-promise')
const exec = require('child_process').exec
const temp = require('temp')
const path = require('path')
const utils = require('../utils')
const fs = require('fs')
const os = require('os')
const {
readAndUnlinkP,
defaultAll
} = utils
function copyToTemp() {
const tmpBat = path.join(os.tmpdir(), 'screenCapture', 'screenCapture_1.3.2.bat')
const tmpManifest = path.join(os.tmpdir(), 'screenCapture', 'app.manifest')
const includeBat = path.join(__dirname, 'screenCapture_1.3.2.bat').replace('app.asar', 'app.asar.unpacked')
const includeManifest = path.join(__dirname, 'app.manifest').replace('app.asar', 'app.asar.unpacked')
if (!fs.existsSync(tmpBat)) {
fs.mkdirSync(path.join(os.tmpdir(), 'screenCapture'))
const sourceData = {
bat: fs.readFileSync(includeBat),
manifest: fs.readFileSync(includeManifest)
}
fs.writeFileSync(tmpBat, sourceData.bat)
fs.writeFileSync(tmpManifest, sourceData.manifest)
}
return tmpBat
}
function windowsSnapshot (options = {}) {
return new Promise((resolve, reject) => {
const displayName = options.screen
const format = options.format || 'jpg'
const tmpPath = temp.path({
suffix: `.${format}`
})
const imgPath = path.resolve(options.filename || tmpPath)
const displayChoice = displayName ? ` /d "${displayName}"` : ''
const tmpBat = copyToTemp()
exec('"' + tmpBat + '" "' + imgPath + '" ' + displayChoice, {
cwd: path.join(os.tmpdir(), 'screenCapture'),
windowsHide: true
}, (err, stdout) => {
if (err) {
return reject(err)
} else {
if (options.filename) {
resolve(imgPath)
} else {
readAndUnlinkP(tmpPath)
.then(resolve)
.catch(reject)
}
}
})
})
}
const EXAMPLE_DISPLAYS_OUTPUT = '\r\nC:\\Users\\devetry\\screenshot-desktop\\lib\\win32>// 2>nul || \r\n\\.\\DISPLAY1;0;1920;1080;0\r\n\\.\\DISPLAY2;0;3840;1080;1920\r\n'
function parseDisplaysOutput (output) {
const displaysStartPattern = /2>nul {2}\|\| /
const {
0: match,
index
} = displaysStartPattern.exec(output)
return output.slice(index + match.length)
.split('\n')
.map(s => s.replace(/[\n\r]/g, ''))
.map(s => s.match(/(.*?);(.?\d+);(.?\d+);(.?\d+);(.?\d+);(.?\d*[\.,]?\d+)/)) // eslint-disable-line
.filter(s => s)
.map(m => ({
id: m[1],
name: m[1],
top: +m[2],
right: +m[3],
bottom: +m[4],
left: +m[5],
dpiScale: +m[6].replace(',', '.')
}))
.map(d => Object.assign(d, {
height: d.bottom - d.top,
width: d.right - d.left
}))
}
function listDisplays () {
return new Promise((resolve, reject) => {
const tmpBat = copyToTemp()
exec(
'"' + tmpBat + '" /list', {
cwd: path.join(os.tmpdir(), 'screenCapture')
},
(err, stdout) => {
if (err) {
return reject(err)
}
resolve(parseDisplaysOutput(stdout))
})
})
}
windowsSnapshot.listDisplays = listDisplays
windowsSnapshot.availableDisplays = listDisplays
windowsSnapshot.parseDisplaysOutput = parseDisplaysOutput
windowsSnapshot.EXAMPLE_DISPLAYS_OUTPUT = EXAMPLE_DISPLAYS_OUTPUT
windowsSnapshot.all = () => defaultAll(windowsSnapshot)
module.exports = windowsSnapshot
- 把刚刚弄好的
index.js
复制(强制覆盖)到你的项目node_modules/screenshot-desktop
文件夹下
然后就可以正常打包运行了。
六、送几个nodejs
打包命令
package.json
文件
{
"name": "rdvc-client-core",
"version": "0.0.0",
"private": true,
"bin": "./build/client/core/src/index.js",
"scripts": {
"dev": "set DEBUG=express:* & nodemon src/index.ts",
"build": "tsc",
"prod": "node ./build/client/core/src/index.js",
"pkg": "pkg . --output=bin/rdvc-client-core"
},
"pkg": {
"assets": [
"views/**/*"
],
"scripts": "./build/client/core/src/index.js",
"targets": [
"node16-macos-x64",
"node16-win-x64",
"node12-linux"
],
"outputPath": "dist"
},
"dependencies": {
"express-handlebars": "^6.0.6",
"@squoosh/lib": "^0.4.0",
"express": "^4.16.1",
"screenshot-desktop": "^1.12.7",
"socket.io-client": "^4.5.1",
"socket.io": "^4.5.1"
},
"devDependencies": {
"@types/express": "^4.16.1",
"@types/node": "^14.14.22",
"@types/screenshot-desktop": "^1.12.0",
"nodemon": "^2.0.16",
"ts-node": "^10.8.1",
"typescript": "^4.7.3"
}
}
-
./build/client/core/src/index.js
这个文件是编译后生成的js
入口文件,根据自己的项目路径来(build
目录与src
目录平级) -
运行
npm run dev
进行开发模式启动(更改实时生效,需要安装nodemon) -
运行
npm run build
进行编译 -
运行
npm run prod
使用编译后的文件启动 -
运行
npm run pkg
打包成mac、win、liunx
可执行文件
评论区