node-mac-recorder 2.2.1 β†’ 2.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # node-mac-recorder
2
2
 
3
- A powerful native macOS screen recording Node.js package with advanced window selection, multi-display support, and granular audio controls. Built with AVFoundation and ScreenCaptureKit for optimal performance.
3
+ A powerful native macOS screen recording Node.js package with advanced window selection, multi-display support, and granular audio controls. Built with AVFoundation for optimal performance.
4
4
 
5
5
  ## Features
6
6
 
@@ -12,7 +12,6 @@ A powerful native macOS screen recording Node.js package with advanced window se
12
12
  - πŸ–±οΈ **Multi-Display Support** - Automatic display detection and selection
13
13
  - 🎨 **Cursor Control** - Toggle cursor visibility in recordings
14
14
  - πŸ–±οΈ **Cursor Tracking** - Track mouse position, cursor types, and click events
15
- - 🚫 **Exclude Apps/Windows (macOS 15+)** - Use ScreenCaptureKit to exclude specific apps (bundle id/PID) and windows
16
15
 
17
16
  🎡 **Granular Audio Controls**
18
17
 
@@ -50,11 +49,6 @@ npm install node-mac-recorder
50
49
  - **Screen Recording Permission** (automatically requested)
51
50
  - **CPU Architecture**: Intel (x64) and Apple Silicon (ARM64) supported
52
51
 
53
- ScreenCaptureKit path and exclusion support:
54
-
55
- - Requires macOS 15.0+ for on-disk recording via `SCRecordingOutput`
56
- - On older systems (<=14), the library falls back to AVFoundation automatically (exclusions not available)
57
-
58
52
  ### Build Requirements
59
53
 
60
54
  ```bash
@@ -116,22 +110,9 @@ await recorder.startRecording("./recording.mov", {
116
110
  quality: "high", // 'low', 'medium', 'high'
117
111
  frameRate: 30, // FPS (15, 30, 60)
118
112
  captureCursor: false, // Show cursor (default: false)
119
-
120
- // ScreenCaptureKit (macOS 15+) - optional, backward compatible
121
- useScreenCaptureKit: false, // If true and available, prefers ScreenCaptureKit
122
- excludedAppBundleIds: ["com.apple.Safari"], // Exclude by bundle id
123
- excludedPIDs: [process.pid], // Exclude by PID
124
- excludedWindowIds: [12345, 67890], // Exclude specific window IDs
125
- // When running under Electron, autoExcludeSelf defaults to true
126
- autoExcludeSelf: true,
127
113
  });
128
114
  ```
129
115
 
130
- Notes
131
-
132
- - If any of `excludedAppBundleIds`, `excludedPIDs`, `excludedWindowIds` are provided, the library automatically switches to ScreenCaptureKit on supported macOS versions.
133
- - When running inside Electron, the current app PID is excluded by default (`autoExcludeSelf: true`).
134
-
135
116
  #### `stopRecording()`
136
117
 
137
118
  Stops the current recording.
@@ -324,29 +305,6 @@ await new Promise((resolve) => setTimeout(resolve, 10000)); // 10 seconds
324
305
  await recorder.stopRecording();
325
306
  ```
326
307
 
327
- ### Exclude Electron app and other windows (ScreenCaptureKit)
328
-
329
- ```javascript
330
- const recorder = new MacRecorder();
331
-
332
- // By default, when running inside Electron, your app is auto-excluded.
333
- // You can also exclude other apps/windows explicitly:
334
- await recorder.startRecording("./excluded.mov", {
335
- captureCursor: true,
336
- // Prefer SC explicitly (optional β€” auto-enabled when exclusions are set)
337
- useScreenCaptureKit: true,
338
- excludedAppBundleIds: ["com.apple.Safari"],
339
- excludedWindowIds: [
340
- /* CGWindowID list */
341
- ],
342
- // autoExcludeSelf is true by default on Electron; set false to include your app
343
- // autoExcludeSelf: false,
344
- });
345
-
346
- // ... later
347
- await recorder.stopRecording();
348
- ```
349
-
350
308
  ### Multi-Display Recording
351
309
 
352
310
  ```javascript
@@ -404,17 +362,16 @@ audioDevices.forEach((device, i) => {
404
362
  });
405
363
 
406
364
  // Find system audio device (like BlackHole, Soundflower, etc.)
407
- const systemAudioDevice = audioDevices.find(
408
- (device) =>
409
- device.name.toLowerCase().includes("blackhole") ||
410
- device.name.toLowerCase().includes("soundflower") ||
411
- device.name.toLowerCase().includes("loopback") ||
412
- device.name.toLowerCase().includes("aggregate")
365
+ const systemAudioDevice = audioDevices.find(device =>
366
+ device.name.toLowerCase().includes('blackhole') ||
367
+ device.name.toLowerCase().includes('soundflower') ||
368
+ device.name.toLowerCase().includes('loopback') ||
369
+ device.name.toLowerCase().includes('aggregate')
413
370
  );
414
371
 
415
372
  if (systemAudioDevice) {
416
373
  console.log(`Using system audio device: ${systemAudioDevice.name}`);
417
-
374
+
418
375
  // Record with specific system audio device
419
376
  await recorder.startRecording("./system-audio-specific.mov", {
420
377
  includeMicrophone: false,
@@ -423,10 +380,8 @@ if (systemAudioDevice) {
423
380
  captureArea: { x: 0, y: 0, width: 1, height: 1 }, // Minimal video
424
381
  });
425
382
  } else {
426
- console.log(
427
- "No system audio device found. Installing BlackHole or Soundflower recommended."
428
- );
429
-
383
+ console.log("No system audio device found. Installing BlackHole or Soundflower recommended.");
384
+
430
385
  // Record with default system audio capture (may not work without virtual audio device)
431
386
  await recorder.startRecording("./system-audio-default.mov", {
432
387
  includeMicrophone: false,
@@ -436,7 +391,7 @@ if (systemAudioDevice) {
436
391
  }
437
392
 
438
393
  // Record for 10 seconds
439
- await new Promise((resolve) => setTimeout(resolve, 10000));
394
+ await new Promise(resolve => setTimeout(resolve, 10000));
440
395
  await recorder.stopRecording();
441
396
  ```
442
397
 
@@ -445,7 +400,7 @@ await recorder.stopRecording();
445
400
  For reliable system audio capture, install a virtual audio device:
446
401
 
447
402
  1. **BlackHole** (Free): https://github.com/ExistentialAudio/BlackHole
448
- 2. **Soundflower** (Free): https://github.com/mattingalls/Soundflower
403
+ 2. **Soundflower** (Free): https://github.com/mattingalls/Soundflower
449
404
  3. **Loopback** (Paid): https://rogueamoeba.com/loopback/
450
405
 
451
406
  These create aggregate audio devices that the package can detect and use for system audio capture.
@@ -765,11 +720,6 @@ npm cache clean --force
765
720
  xcode-select --install
766
721
  ```
767
722
 
768
- ### ScreenCaptureKit availability
769
-
770
- - Exclusions require macOS 15+ (uses `SCRecordingOutput`).
771
- - On macOS 12.3–14, the module will fall back to AVFoundation (no exclusions). This is automatic and backward-compatible.
772
-
773
723
  ### Recording Issues
774
724
 
775
725
  1. **Empty/Black Video**: Check screen recording permissions
@@ -824,24 +774,12 @@ MIT License - see [LICENSE](LICENSE) file for details.
824
774
 
825
775
  ### Latest Updates
826
776
 
827
- - βœ… ScreenCaptureKit path with exclusions (apps by bundle id/PID and window IDs) on macOS 15+
828
- - βœ… Electron apps auto-excluded by default (can be disabled with `autoExcludeSelf: false`)
829
- - βœ… Prebuilt binaries for darwin-arm64 and darwin-x64; automatic loading via `node-gyp-build`
830
- - βœ… Cursor Tracking: Track mouse position, cursor types, and click events with JSON export
831
- - βœ… Window Recording: Automatic coordinate conversion for multi-display setups
832
- - βœ… Audio Controls: Separate microphone and system audio controls
833
- - βœ… Display Selection: Multi-monitor support with automatic detection
834
- - βœ… Smart Filtering: Improved window detection and filtering
835
- - βœ… Performance: Optimized native implementation
836
-
837
- ## Prebuilt binaries
838
-
839
- This package ships prebuilt native binaries for macOS:
840
-
841
- - darwin-arm64 (Apple Silicon)
842
- - darwin-x64 (Intel)
843
-
844
- At runtime, the correct binary is loaded automatically via `node-gyp-build`. If a prebuilt is not available for your environment, the module falls back to a local build.
777
+ - βœ… **Cursor Tracking**: Track mouse position, cursor types, and click events with JSON export
778
+ - βœ… **Window Recording**: Automatic coordinate conversion for multi-display setups
779
+ - βœ… **Audio Controls**: Separate microphone and system audio controls
780
+ - βœ… **Display Selection**: Multi-monitor support with automatic detection
781
+ - βœ… **Smart Filtering**: Improved window detection and filtering
782
+ - βœ… **Performance**: Optimized native implementation
845
783
 
846
784
  ---
847
785
 
package/binding.gyp CHANGED
@@ -5,7 +5,6 @@
5
5
  "sources": [
6
6
  "src/mac_recorder.mm",
7
7
  "src/screen_capture.mm",
8
- "src/screen_capture_kit.mm",
9
8
  "src/audio_capture.mm",
10
9
  "src/cursor_tracker.mm",
11
10
  "src/window_selector.mm"
@@ -22,11 +21,8 @@
22
21
  "GCC_ENABLE_CPP_EXCEPTIONS": "YES",
23
22
  "CLANG_CXX_LIBRARY": "libc++",
24
23
  "MACOSX_DEPLOYMENT_TARGET": "12.3",
25
- <<<<<<< HEAD
26
- =======
27
24
  "ARCHS": ["arm64"],
28
25
  "VALID_ARCHS": ["arm64"],
29
- >>>>>>> screencapture
30
26
  "OTHER_CFLAGS": [
31
27
  "-ObjC++",
32
28
  "-fmodules"
@@ -35,22 +31,15 @@
35
31
  },
36
32
  "link_settings": {
37
33
  "libraries": [
38
- <<<<<<< HEAD
39
- =======
40
34
  "-framework ScreenCaptureKit",
41
35
  "-framework AVFoundation",
42
36
  "-framework CoreMedia",
43
37
  "-framework CoreVideo",
44
- >>>>>>> screencapture
45
38
  "-framework Foundation",
46
39
  "-framework AppKit",
47
40
  "-framework ApplicationServices",
48
41
  "-framework Carbon",
49
- "-framework Accessibility",
50
- "-framework CoreAudio",
51
- "-framework AVFoundation",
52
- "-framework CoreMedia",
53
- "-framework CoreVideo"
42
+ "-framework Accessibility"
54
43
  ]
55
44
  },
56
45
  "defines": [
package/index.js CHANGED
@@ -2,22 +2,26 @@ const { EventEmitter } = require("events");
2
2
  const path = require("path");
3
3
  const fs = require("fs");
4
4
 
5
- // Native modΓΌlΓΌ yΓΌkle (prebuilt > local build fallback)
5
+ // Native modΓΌlΓΌ yΓΌkle
6
6
  let nativeBinding;
7
7
  try {
8
- // Prebuilt
9
- nativeBinding = require("node-gyp-build")(__dirname);
10
- } catch (e) {
8
+ // Prefer prebuild on arm64
9
+ if (process.platform === "darwin" && process.arch === "arm64") {
10
+ nativeBinding = require("./prebuilds/darwin-arm64/node.napi.node");
11
+ } else {
12
+ nativeBinding = require("./build/Release/mac_recorder.node");
13
+ }
14
+ } catch (error) {
11
15
  try {
12
16
  nativeBinding = require("./build/Release/mac_recorder.node");
13
- } catch (error) {
17
+ } catch (_) {
14
18
  try {
15
19
  nativeBinding = require("./build/Debug/mac_recorder.node");
16
20
  } catch (debugError) {
17
21
  throw new Error(
18
22
  'Native module not found. Please run "npm run build" to compile the native module.\n' +
19
23
  "Original error: " +
20
- (error?.message || e?.message)
24
+ error.message
21
25
  );
22
26
  }
23
27
  }
@@ -50,12 +54,6 @@ class MacRecorder extends EventEmitter {
50
54
  showClicks: false,
51
55
  displayId: null, // Hangi ekranΔ± kaydedeceği (null = ana ekran)
52
56
  windowId: null, // Hangi pencereyi kaydedeceği (null = tam ekran)
53
- // SC (gizli, opsiyonel) - mevcut kullanΔ±cΔ±larΔ± bozmaz
54
- useScreenCaptureKit: false,
55
- excludedAppBundleIds: [],
56
- excludedPIDs: [],
57
- excludedWindowIds: [],
58
- autoExcludeSelf: !!(process.versions && process.versions.electron),
59
57
  };
60
58
 
61
59
  // Display cache iΓ§in async initialization
@@ -129,22 +127,11 @@ class MacRecorder extends EventEmitter {
129
127
  audioDeviceId: options.audioDeviceId || null, // null = default device
130
128
  systemAudioDeviceId: options.systemAudioDeviceId || null, // null = auto-detect system audio device
131
129
  captureArea: options.captureArea || null,
132
- <<<<<<< HEAD
133
- useScreenCaptureKit: options.useScreenCaptureKit || false,
134
- excludedAppBundleIds: options.excludedAppBundleIds || [],
135
- excludedPIDs: options.excludedPIDs || [],
136
- excludedWindowIds: options.excludedWindowIds || [],
137
- autoExcludeSelf:
138
- typeof options.autoExcludeSelf === "boolean"
139
- ? options.autoExcludeSelf
140
- : !!(process.versions && process.versions.electron),
141
- =======
142
130
  // Exclusion options
143
131
  excludeCurrentApp: options.excludeCurrentApp || false,
144
132
  excludeWindowIds: Array.isArray(options.excludeWindowIds)
145
133
  ? options.excludeWindowIds
146
134
  : [],
147
- >>>>>>> screencapture
148
135
  };
149
136
  }
150
137
 
@@ -305,17 +292,9 @@ class MacRecorder extends EventEmitter {
305
292
  windowId: this.options.windowId || null, // null = tam ekran
306
293
  audioDeviceId: this.options.audioDeviceId || null, // null = default device
307
294
  systemAudioDeviceId: this.options.systemAudioDeviceId || null, // null = auto-detect system audio device
308
- <<<<<<< HEAD
309
- useScreenCaptureKit: this.options.useScreenCaptureKit || false,
310
- excludedAppBundleIds: this.options.excludedAppBundleIds || [],
311
- excludedPIDs: this.options.excludedPIDs || [],
312
- excludedWindowIds: this.options.excludedWindowIds || [],
313
- autoExcludeSelf: this.options.autoExcludeSelf === true,
314
- =======
315
295
  // Exclusion options passthrough
316
296
  excludeCurrentApp: this.options.excludeCurrentApp || false,
317
297
  excludeWindowIds: this.options.excludeWindowIds || [],
318
- >>>>>>> screencapture
319
298
  };
320
299
 
321
300
  // Manuel captureArea varsa onu kullan
@@ -328,34 +307,10 @@ class MacRecorder extends EventEmitter {
328
307
  };
329
308
  }
330
309
 
331
- // SC yolu: kullanΔ±cΔ±dan SC talebi varsa veya exclude listeleri doluysa ve SC mevcutsa otomatik kullan
332
- let success;
333
- try {
334
- const wantsSC = !!(
335
- this.options.useScreenCaptureKit ||
336
- this.options.excludedAppBundleIds?.length ||
337
- this.options.excludedPIDs?.length ||
338
- this.options.excludedWindowIds?.length
339
- );
340
- const scAvailable =
341
- typeof nativeBinding.isScreenCaptureKitAvailable === "function" &&
342
- nativeBinding.isScreenCaptureKitAvailable();
343
- if (wantsSC && scAvailable) {
344
- const scOptions = {
345
- ...recordingOptions,
346
- useScreenCaptureKit: true,
347
- };
348
- success = nativeBinding.startRecording(outputPath, scOptions);
349
- } else {
350
- success = nativeBinding.startRecording(
351
- outputPath,
352
- recordingOptions
353
- );
354
- }
355
- } catch (e) {
356
- // Fallback AVFoundation
357
- success = nativeBinding.startRecording(outputPath, recordingOptions);
358
- }
310
+ const success = nativeBinding.startRecording(
311
+ outputPath,
312
+ recordingOptions
313
+ );
359
314
 
360
315
  if (success) {
361
316
  this.isRecording = true;
package/install.js CHANGED
@@ -2,7 +2,7 @@ const { spawn } = require("child_process");
2
2
  const fs = require("fs");
3
3
  const path = require("path");
4
4
 
5
- console.log("πŸ”¨ Building native macOS recorder module...\n");
5
+ console.log("πŸ”¨ Installing node-mac-recorder...\n");
6
6
 
7
7
  // Check if we're on macOS
8
8
  if (process.platform !== "darwin") {
@@ -10,7 +10,24 @@ if (process.platform !== "darwin") {
10
10
  process.exit(1);
11
11
  }
12
12
 
13
- // Check if Xcode Command Line Tools are installed
13
+ // Prefer prebuilds on supported platforms
14
+ const prebuildPath = path.join(
15
+ __dirname,
16
+ "prebuilds",
17
+ `darwin-${process.arch}`,
18
+ "node.napi.node"
19
+ );
20
+ if (
21
+ process.platform === "darwin" &&
22
+ process.arch === "arm64" &&
23
+ fs.existsSync(prebuildPath)
24
+ ) {
25
+ console.log("βœ… Using prebuilt binary:", prebuildPath);
26
+ console.log("πŸŽ‰ node-mac-recorder is ready to use (no compilation needed)");
27
+ process.exit(0);
28
+ }
29
+
30
+ // Fallback to building from source
14
31
  console.log("πŸ” Checking Xcode Command Line Tools...");
15
32
  const xcodebuild = spawn("xcode-select", ["--print-path"], { stdio: "pipe" });
16
33
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-mac-recorder",
3
- "version": "2.2.1",
3
+ "version": "2.4.1",
4
4
  "description": "Native macOS screen recording package for Node.js applications",
5
5
  "main": "index.js",
6
6
  "keywords": [
@@ -37,20 +37,19 @@
37
37
  "install": "node install.js",
38
38
  "build": "node-gyp build",
39
39
  "rebuild": "node-gyp rebuild",
40
+ "prebuild:node-arm64": "prebuildify --platform darwin --arch arm64 --napi --strip",
41
+ "prebuild:electron-arm64": "prebuildify --platform darwin --arch arm64 --napi --strip --targets electron@27.0.0",
42
+ "prebuild:all-arm64": "npm run prebuild:node-arm64 && npm run prebuild:electron-arm64",
40
43
  "clean": "node-gyp clean",
41
- "prebuild:darwin:arm64": "prebuildify --napi --strip --platform darwin --arch arm64",
42
- "prebuild:darwin:x64": "prebuildify --napi --strip --platform darwin --arch x64",
43
- "prebuild": "npm run prebuild:darwin:arm64 && npm run prebuild:darwin:x64",
44
44
  "test:window-selector": "node window-selector-test.js",
45
45
  "example:window-selector": "node examples/window-selector-example.js"
46
46
  },
47
47
  "dependencies": {
48
- "node-addon-api": "^7.0.0",
49
- "node-gyp-build": "^4.6.0"
48
+ "node-addon-api": "^7.0.0"
50
49
  },
51
50
  "devDependencies": {
52
51
  "node-gyp": "^10.0.0",
53
- "prebuildify": "^5.0.0"
52
+ "prebuildify": "^6.0.1"
54
53
  },
55
54
  "gypfile": true
56
55
  }