🚀 Big News: Socket Acquires Coana to Bring Reachability Analysis to Every Appsec Team.Learn more
Socket
Sign inDemoInstall
Socket

aperture

Package Overview
Dependencies
Maintainers
3
Versions
23
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

aperture - npm Package Compare versions

Comparing version

to
6.1.0

234

index.d.ts
declare namespace aperture {
type Screen = {
id: number;
name: string;
};
type Screen = {
id: number;
name: string;
};
type AudioDevice = {
id: string;
name: string;
};
type AudioDevice = {
id: string;
name: string;
};
type VideoCodec = 'h264' | 'hevc' | 'proRes422' | 'proRes4444';
type VideoCodec = 'h264' | 'hevc' | 'proRes422' | 'proRes4444';
type RecordingOptions = {
/**
Number of frames per seconds.
*/
readonly fps?: number;
type RecordingOptions = {
/**
Number of frames per seconds.
*/
readonly fps?: number;
/**
Record only an area of the screen.
*/
readonly cropArea?: {
x: number;
y: number;
width: number;
height: number;
};
/**
Record only an area of the screen.
*/
readonly cropArea?: {
x: number;
y: number;
width: number;
height: number;
};
/**
Show the cursor in the screen recording.
*/
readonly showCursor?: boolean;
/**
Show the cursor in the screen recording.
*/
readonly showCursor?: boolean;
/**
Highlight cursor clicks in the screen recording.
/**
Highlight cursor clicks in the screen recording.
Enabling this will also enable the `showCursor` option.
*/
readonly highlightClicks?: boolean;
Enabling this will also enable the `showCursor` option.
*/
readonly highlightClicks?: boolean;
/**
Screen to record.
/**
Screen to record.
Defaults to primary screen.
*/
readonly screenId?: number;
Defaults to primary screen.
*/
readonly screenId?: number;
/**
Audio device to include in the screen recording.
/**
Audio device to include in the screen recording.
Should be one of the `id`'s from `aperture.audioDevices()`.
*/
readonly audioDeviceId?: string;
Should be one of the `id`'s from `aperture.audioDevices()`.
*/
readonly audioDeviceId?: string;
/**
Video codec to use.
/**
Video codec to use.
A computer with Intel 6th generation processor or newer is strongly recommended for the `hevc` codec, as otherwise it will use software encoding, which only produces 3 FPS fullscreen recording.
A computer with Intel 6th generation processor or newer is strongly recommended for the `hevc` codec, as otherwise it will use software encoding, which only produces 3 FPS fullscreen recording.
The `proRes422` and `proRes4444` codecs are uncompressed data. They will create huge files.
*/
readonly videoCodec?: VideoCodec;
};
The `proRes422` and `proRes4444` codecs are uncompressed data. They will create huge files.
*/
readonly videoCodec?: VideoCodec;
};
interface Recorder {
/**
Returns a `Promise` that fullfills when the recording starts or rejects if the recording didn't start after 5 seconds.
*/
startRecording: (options?: RecordingOptions) => Promise<void>;
interface Recorder {
/**
Returns a `Promise` that fullfills when the recording starts or rejects if the recording didn't start after 5 seconds.
*/
startRecording: (options?: RecordingOptions) => Promise<void>;
/**
`Promise` that fullfills with the path to the screen recording file when it's ready. This will never reject.
/**
`Promise` that fullfills with the path to the screen recording file when it's ready. This will never reject.
Only available while a recording is happening, `undefined` otherwise.
Only available while a recording is happening, `undefined` otherwise.
Usually, this resolves around 1 second before the recording starts, but that's not guaranteed.
*/
isFileReady: Promise<string> | undefined;
Usually, this resolves around 1 second before the recording starts, but that's not guaranteed.
*/
isFileReady: Promise<string> | undefined;
/**
Pauses the recording. To resume, call `recorder.resume()`.
/**
Pauses the recording. To resume, call `recorder.resume()`.
Returns a `Promise` that fullfills when the recording has been paused.
*/
pause: () => Promise<void>;
Returns a `Promise` that fullfills when the recording has been paused.
*/
pause: () => Promise<void>;
/**
Resumes the recording if it's been paused.
/**
Resumes the recording if it's been paused.
Returns a `Promise` that fullfills when the recording has been resumed.
*/
resume: () => Promise<void>;
Returns a `Promise` that fullfills when the recording has been resumed.
*/
resume: () => Promise<void>;
/**
Returns a `Promise` that resolves with a boolean indicating whether or not the recording is currently paused.
*/
isPaused: () => Promise<boolean>;
/**
Returns a `Promise` that resolves with a boolean indicating whether or not the recording is currently paused.
*/
isPaused: () => Promise<boolean>;
/**
Returns a `Promise` for the path to the screen recording file.
*/
stopRecording: () => Promise<string>;
}
/**
Returns a `Promise` for the path to the screen recording file.
*/
stopRecording: () => Promise<string>;
}
}
declare const aperture: (() => aperture.Recorder) & {
/**
Get a list of available video codecs.
/**
Get a list of available video codecs.
The key is the `videoCodec` option name and the value is the codec name.
The key is the `videoCodec` option name and the value is the codec name.
It only returns `hevc` if your computer supports HEVC hardware encoding.
It only returns `hevc` if your computer supports HEVC hardware encoding.
@example
```
Map {
'h264' => 'H264',
'hevc' => 'HEVC',
'proRes422' => 'Apple ProRes 422',
'proRes4444' => 'Apple ProRes 4444'
}
```
*/
videoCodecs: Map<aperture.VideoCodec, string>;
@example
```
Map {
'h264' => 'H264',
'hevc' => 'HEVC',
'proRes422' => 'Apple ProRes 422',
'proRes4444' => 'Apple ProRes 4444'
}
```
*/
videoCodecs: Map<aperture.VideoCodec, string>;
/**
Get a list of screens.
/**
Get a list of screens.
The first screen is the primary screen.
The first screen is the primary screen.
@example
```
[{
id: 69732482,
name: 'Color LCD'
}]
```
*/
screens: () => Promise<aperture.Screen[]>;
@example
```
[{
id: 69732482,
name: 'Color LCD'
}]
```
*/
screens: () => Promise<aperture.Screen[]>;
/**
Get a list of audio devices.
/**
Get a list of audio devices.
@example
```
[{
id: 'AppleHDAEngineInput:1B,0,1,0:1',
name: 'Built-in Microphone'
}]
```
*/
audioDevices: () => Promise<aperture.AudioDevice[]>;
@example
```
[{
id: 'AppleHDAEngineInput:1B,0,1,0:1',
name: 'Built-in Microphone'
}]
```
*/
audioDevices: () => Promise<aperture.AudioDevice[]>;
};
export = aperture;

@@ -19,213 +19,212 @@ 'use strict';

const supportsHevcHardwareEncoding = (() => {
const cpuModel = os.cpus()[0].model;
const cpuModel = os.cpus()[0].model;
// All Apple silicon Macs support HEVC hardware encoding.
if (cpuModel.startsWith('Apple ')) { // Source string example: `'Apple M1'`
return true;
}
// All Apple silicon Macs support HEVC hardware encoding.
if (cpuModel.startsWith('Apple ')) { // Source string example: `'Apple M1'`
return true;
}
// Get the Intel Core generation, the `4` in `Intel(R) Core(TM) i7-4850HQ CPU @ 2.30GHz`
// More info: https://www.intel.com/content/www/us/en/processors/processor-numbers.html
// Example strings:
// - `Intel(R) Core(TM) i9-9980HK CPU @ 2.40GHz`
// - `Intel(R) Core(TM) i7-4850HQ CPU @ 2.30GHz`
const result = /Intel.*Core.*i\d+-(\d)/.exec(cpuModel);
// Get the Intel Core generation, the `4` in `Intel(R) Core(TM) i7-4850HQ CPU @ 2.30GHz`
// More info: https://www.intel.com/content/www/us/en/processors/processor-numbers.html
// Example strings:
// - `Intel(R) Core(TM) i9-9980HK CPU @ 2.40GHz`
// - `Intel(R) Core(TM) i7-4850HQ CPU @ 2.30GHz`
const result = /Intel.*Core.*i\d+-(\d)/.exec(cpuModel);
// Intel Core generation 6 or higher supports HEVC hardware encoding
return result && Number.parseInt(result[1], 10) >= 6;
// Intel Core generation 6 or higher supports HEVC hardware encoding
return result && Number.parseInt(result[1], 10) >= 6;
})();
class Aperture {
constructor() {
macosVersion.assertGreaterThanOrEqualTo('10.13');
}
constructor() {
macosVersion.assertGreaterThanOrEqualTo('10.13');
}
startRecording({
fps = 30,
cropArea = undefined,
showCursor = true,
highlightClicks = false,
screenId = 0,
audioDeviceId = undefined,
videoCodec = undefined
} = {}) {
this.processId = getRandomId();
startRecording({
fps = 30,
cropArea = undefined,
showCursor = true,
highlightClicks = false,
screenId = 0,
audioDeviceId = undefined,
videoCodec = 'h264'
} = {}) {
this.processId = getRandomId();
return new Promise((resolve, reject) => {
if (this.recorder !== undefined) {
reject(new Error('Call `.stopRecording()` first'));
return;
}
return new Promise((resolve, reject) => {
if (this.recorder !== undefined) {
reject(new Error('Call `.stopRecording()` first'));
return;
}
this.tmpPath = tempy.file({extension: 'mp4'});
this.tmpPath = tempy.file({extension: 'mp4'});
if (highlightClicks === true) {
showCursor = true;
}
if (highlightClicks === true) {
showCursor = true;
}
if (typeof cropArea === 'object' &&
(
typeof cropArea.x !== 'number' ||
typeof cropArea.y !== 'number' ||
typeof cropArea.width !== 'number' ||
typeof cropArea.height !== 'number'
)
) {
reject(new Error('Invalid `cropArea` option object'));
return;
}
if (typeof cropArea === 'object' &&
(
typeof cropArea.x !== 'number' ||
typeof cropArea.y !== 'number' ||
typeof cropArea.width !== 'number' ||
typeof cropArea.height !== 'number'
)
) {
reject(new Error('Invalid `cropArea` option object'));
return;
}
const recorderOptions = {
destination: fileUrl(this.tmpPath),
framesPerSecond: fps,
showCursor,
highlightClicks,
screenId,
audioDeviceId
};
const recorderOptions = {
destination: fileUrl(this.tmpPath),
framesPerSecond: fps,
showCursor,
highlightClicks,
screenId,
audioDeviceId
};
if (cropArea) {
recorderOptions.cropRect = [
[cropArea.x, cropArea.y],
[cropArea.width, cropArea.height]
];
}
if (cropArea) {
recorderOptions.cropRect = [
[cropArea.x, cropArea.y],
[cropArea.width, cropArea.height]
];
}
if (videoCodec) {
const codecMap = new Map([
['h264', 'avc1'],
['hevc', 'hvc1'],
['proRes422', 'apcn'],
['proRes4444', 'ap4h']
]);
if (videoCodec) {
const codecMap = new Map([
['h264', 'avc1'],
['hevc', 'hvc1'],
['proRes422', 'apcn'],
['proRes4444', 'ap4h']
]);
if (!supportsHevcHardwareEncoding) {
codecMap.delete('hevc');
}
if (!supportsHevcHardwareEncoding) {
codecMap.delete('hevc');
}
if (!codecMap.has(videoCodec)) {
throw new Error(`Unsupported video codec specified: ${videoCodec}`);
}
if (!codecMap.has(videoCodec)) {
throw new Error(`Unsupported video codec specified: ${videoCodec}`);
}
recorderOptions.videoCodec = codecMap.get(videoCodec);
}
recorderOptions.videoCodec = codecMap.get(videoCodec);
}
this.recorder = execa(
BIN, [
'record',
'--process-id',
this.processId,
JSON.stringify(recorderOptions)
]
);
this.recorder = execa(
BIN, [
'record',
'--process-id',
this.processId,
JSON.stringify(recorderOptions)
]
);
this.isFileReady = (async () => {
await this.waitForEvent('onFileReady');
return this.tmpPath;
})();
this.isFileReady = (async () => {
await this.waitForEvent('onFileReady');
return this.tmpPath;
})();
const timeout = setTimeout(() => {
// `.stopRecording()` was called already
if (this.recorder === undefined) {
return;
}
const timeout = setTimeout(() => {
// `.stopRecording()` was called already
if (this.recorder === undefined) {
return;
}
const error = new Error('Could not start recording within 5 seconds');
error.code = 'RECORDER_TIMEOUT';
this.recorder.kill();
delete this.recorder;
reject(error);
}, 5000);
const error = new Error('Could not start recording within 5 seconds');
error.code = 'RECORDER_TIMEOUT';
this.recorder.kill();
delete this.recorder;
reject(error);
}, 5000);
this.recorder.catch(error => {
clearTimeout(timeout);
delete this.recorder;
reject(error);
});
this.recorder.catch(error => {
clearTimeout(timeout);
delete this.recorder;
reject(error);
});
this.recorder.stdout.setEncoding('utf8');
this.recorder.stdout.on('data', log);
this.recorder.stdout.setEncoding('utf8');
this.recorder.stdout.on('data', log);
(async () => {
try {
await this.waitForEvent('onStart');
clearTimeout(timeout);
setTimeout(resolve, 1000);
} catch (error) {
reject(error);
}
})();
});
}
(async () => {
try {
await this.waitForEvent('onStart');
clearTimeout(timeout);
setTimeout(resolve, 1000);
} catch (error) {
reject(error);
}
})();
});
}
async waitForEvent(name, parse) {
const {stdout} = await execa(
BIN, [
'events',
'listen',
'--process-id',
this.processId,
'--exit',
name
]
);
async waitForEvent(name, parse) {
const {stdout} = await execa(
BIN, [
'events',
'listen',
'--process-id',
this.processId,
'--exit',
name
]
);
if (parse) {
return parse(stdout.trim());
}
}
if (parse) {
return parse(stdout.trim());
}
}
async sendEvent(name, parse) {
const {stdout} = await execa(
BIN, [
'events',
'send',
'--process-id',
this.processId,
name
]
);
async sendEvent(name, parse) {
const {stdout} = await execa(
BIN, [
'events',
'send',
'--process-id',
this.processId,
name
]
);
if (parse) {
return parse(stdout.trim());
}
}
if (parse) {
return parse(stdout.trim());
}
}
throwIfNotStarted() {
if (this.recorder === undefined) {
throw new Error('Call `.startRecording()` first');
}
}
throwIfNotStarted() {
if (this.recorder === undefined) {
throw new Error('Call `.startRecording()` first');
}
}
async pause() {
this.throwIfNotStarted();
async pause() {
this.throwIfNotStarted();
await this.sendEvent('pause');
}
await this.sendEvent('pause');
}
async resume() {
this.throwIfNotStarted();
async resume() {
this.throwIfNotStarted();
await this.sendEvent('resume');
await this.sendEvent('resume');
// It takes about 1s after the promise resolves for the recording to actually start
await delay(1000);
}
// It takes about 1s after the promise resolves for the recording to actually start
await delay(1000);
}
async isPaused() {
this.throwIfNotStarted();
async isPaused() {
this.throwIfNotStarted();
return this.sendEvent('isPaused', value => value === 'true');
}
return this.sendEvent('isPaused', value => value === 'true');
}
async stopRecording() {
this.throwIfNotStarted();
async stopRecording() {
this.throwIfNotStarted();
this.recorder.kill();
await this.recorder;
delete this.recorder;
delete this.isFileReady;
this.recorder.kill();
await this.recorder;
delete this.recorder;
delete this.isFileReady;
return this.tmpPath;
}
return this.tmpPath;
}
}

@@ -236,36 +235,36 @@

module.exports.screens = async () => {
const {stderr} = await execa(BIN, ['list', 'screens']);
const {stderr} = await execa(BIN, ['list', 'screens']);
try {
return JSON.parse(stderr);
} catch {
return stderr;
}
try {
return JSON.parse(stderr);
} catch {
return stderr;
}
};
module.exports.audioDevices = async () => {
const {stderr} = await execa(BIN, ['list', 'audio-devices']);
const {stderr} = await execa(BIN, ['list', 'audio-devices']);
try {
return JSON.parse(stderr);
} catch {
return stderr;
}
try {
return JSON.parse(stderr);
} catch {
return stderr;
}
};
Object.defineProperty(module.exports, 'videoCodecs', {
get() {
const codecs = new Map([
['h264', 'H264'],
['hevc', 'HEVC'],
['proRes422', 'Apple ProRes 422'],
['proRes4444', 'Apple ProRes 4444']
]);
get() {
const codecs = new Map([
['h264', 'H264'],
['hevc', 'HEVC'],
['proRes422', 'Apple ProRes 422'],
['proRes4444', 'Apple ProRes 4444']
]);
if (!supportsHevcHardwareEncoding) {
codecs.delete('hevc');
}
if (!supportsHevcHardwareEncoding) {
codecs.delete('hevc');
}
return codecs;
}
return codecs;
}
});
{
"name": "aperture",
"version": "6.0.1",
"description": "Record the screen on macOS",
"license": "MIT",
"repository": "wulkano/aperture-node",
"engines": {
"node": ">=10"
},
"scripts": {
"test": "xo && ava && tsd",
"build": "swift build --configuration=release && mv .build/release/aperture .",
"prepublish": "npm run build"
},
"files": [
"index.js",
"aperture",
"index.d.ts"
],
"dependencies": {
"delay": "^5.0.0",
"electron-util": "^0.14.2",
"execa": "^5.0.0",
"file-url": "^3.0.0",
"macos-version": "^5.2.1",
"tempy": "^1.0.0"
},
"devDependencies": {
"ava": "^2.4.0",
"file-type": "^12.0.0",
"read-chunk": "^3.2.0",
"tsd": "^0.14.0",
"xo": "^0.38.2"
},
"xo": {
"space": true
}
"name": "aperture",
"version": "6.1.0",
"description": "Record the screen on macOS",
"license": "MIT",
"repository": "wulkano/aperture-node",
"engines": {
"node": ">=10"
},
"scripts": {
"test": "xo && ava && tsd",
"build": "swift build --configuration=release --arch arm64 --arch x86_64 && mv .build/apple/Products/Release/aperture .",
"prepublish": "npm run build"
},
"files": [
"index.js",
"aperture",
"index.d.ts"
],
"dependencies": {
"delay": "^5.0.0",
"electron-util": "^0.14.2",
"execa": "^5.0.0",
"file-url": "^3.0.0",
"macos-version": "^5.2.1",
"tempy": "^1.0.0"
},
"devDependencies": {
"ava": "^2.4.0",
"file-type": "^12.0.0",
"read-chunk": "^3.2.0",
"tsd": "^0.14.0",
"xo": "^0.38.2"
}
}

@@ -7,5 +7,5 @@ # aperture-node

```sh
npm install aperture
```
$ npm install aperture
```

@@ -21,16 +21,16 @@ *Requires macOS 10.13 or later.*

const options = {
fps: 30,
cropArea: {
x: 100,
y: 100,
width: 500,
height: 500
}
fps: 30,
cropArea: {
x: 100,
y: 100,
width: 500,
height: 500
}
};
(async () => {
await aperture.startRecording(options);
await delay(3000);
console.log(await aperture.stopRecording());
//=> '/private/var/folders/3x/jf5977fn79jbglr7rk0tq4d00000gn/T/cdf4f7df426c97880f8c10a1600879f7.mp4'
await aperture.startRecording(options);
await delay(3000);
console.log(await aperture.stopRecording());
//=> '/private/var/folders/3x/jf5977fn79jbglr7rk0tq4d00000gn/T/cdf4f7df426c97880f8c10a1600879f7.mp4'
})();

@@ -51,4 +51,4 @@ ```

[{
id: 69732482,
name: 'Color LCD'
id: 69732482,
name: 'Color LCD'
}]

@@ -65,4 +65,4 @@ ```

[{
id: 'AppleHDAEngineInput:1B,0,1,0:1',
name: 'Built-in Microphone'
id: 'AppleHDAEngineInput:1B,0,1,0:1',
name: 'Built-in Microphone'
}]

@@ -79,6 +79,6 @@ ```

Map {
'h264' => 'H264',
'hevc' => 'HEVC',
'proRes422' => 'Apple ProRes 422',
'proRes4444' => 'Apple ProRes 4444'
'h264' => 'H264',
'hevc' => 'HEVC',
'proRes422' => 'Apple ProRes 422',
'proRes4444' => 'Apple ProRes 4444'
}

@@ -85,0 +85,0 @@ ```

Sorry, the diff of this file is not supported yet