node-mac-recorder 2.21.10 → 2.21.12

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.
@@ -1,19 +1,7 @@
1
1
  {
2
2
  "permissions": {
3
3
  "allow": [
4
- "Bash(node-gyp:*)",
5
- "Bash(node:*)",
6
- "Bash(timeout:*)",
7
- "Bash(open:*)",
8
- "Read(//Users/onur/codes/**)",
9
- "Bash(log show:*)",
10
- "Bash(MAC_RECORDER_DEBUG=1 node:*)",
11
- "Read(//private/tmp/test-recording/**)",
12
- "Bash(MAC_RECORDER_DEBUG=1 timeout 5 node:*)",
13
- "Read(//private/tmp/**)",
14
- "Bash(ffprobe:*)",
15
- "Bash(MAC_RECORDER_DEBUG=1 timeout 8 node:*)",
16
- "Bash(cat:*)"
4
+ "Bash(ffmpeg:*)"
17
5
  ],
18
6
  "deny": [],
19
7
  "ask": []
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-mac-recorder",
3
- "version": "2.21.10",
3
+ "version": "2.21.12",
4
4
  "description": "Native macOS screen recording package for Node.js applications",
5
5
  "main": "index.js",
6
6
  "keywords": [
@@ -122,7 +122,8 @@ static dispatch_queue_t g_audioCaptureQueue = nil;
122
122
  AVFormatIDKey: @(kAudioFormatMPEG4AAC),
123
123
  AVSampleRateKey: @(sampleRate),
124
124
  AVNumberOfChannelsKey: @(channels),
125
- AVEncoderBitRateKey: @(192000)
125
+ AVEncoderBitRateKey: @(256000), // Increased from 192k to 256k for better quality
126
+ AVEncoderAudioQualityKey: @(AVAudioQualityHigh)
126
127
  } mutableCopy];
127
128
 
128
129
  if (layoutSize > 0) {
@@ -212,22 +212,26 @@ extern "C" bool startAVFoundationRecording(const std::string& outputPath,
212
212
  MRLog(@"🎤 Starting audio capture (mic=%d, system=%d)", includeMicrophone, includeSystemAudio);
213
213
 
214
214
  // Use provided audio output path or generate one
215
+ NSString *audioPath = nil;
215
216
  if (audioOutputPath && [audioOutputPath length] > 0) {
216
- g_avAudioOutputPath = audioOutputPath;
217
- MRLog(@"🎤 Using provided audio path: %@", g_avAudioOutputPath);
217
+ audioPath = audioOutputPath;
218
+ MRLog(@"🎤 Using provided audio path: %@", audioPath);
218
219
  } else {
219
220
  NSString *videoDir = [outputPathStr stringByDeletingLastPathComponent];
220
221
  NSString *audioFilename = [NSString stringWithFormat:@"avf_audio_%ld.mov", (long)[[NSDate date] timeIntervalSince1970]];
221
- g_avAudioOutputPath = [videoDir stringByAppendingPathComponent:audioFilename];
222
- MRLog(@"🎤 Generated audio path: %@", g_avAudioOutputPath);
222
+ audioPath = [videoDir stringByAppendingPathComponent:audioFilename];
223
+ MRLog(@"🎤 Generated audio path: %@", audioPath);
223
224
  }
224
225
 
225
226
  // Ensure .mov extension
226
- if (![g_avAudioOutputPath.pathExtension.lowercaseString isEqualToString:@"mov"]) {
227
- g_avAudioOutputPath = [[g_avAudioOutputPath stringByDeletingPathExtension] stringByAppendingPathExtension:@"mov"];
228
- MRLog(@"🔧 Fixed audio extension to .mov: %@", g_avAudioOutputPath);
227
+ if (![audioPath.pathExtension.lowercaseString isEqualToString:@"mov"]) {
228
+ audioPath = [[audioPath stringByDeletingPathExtension] stringByAppendingPathExtension:@"mov"];
229
+ MRLog(@"🔧 Fixed audio extension to .mov: %@", audioPath);
229
230
  }
230
231
 
232
+ // Copy to retain the string (ARC will manage it)
233
+ g_avAudioOutputPath = [audioPath copy];
234
+
231
235
  // Create audio recorder
232
236
  g_avAudioRecorder = createNativeAudioRecorder();
233
237
 
@@ -404,9 +408,14 @@ extern "C" bool stopAVFoundationRecording() {
404
408
  // Stop audio recording if active
405
409
  if (g_avAudioRecorder) {
406
410
  MRLog(@"🛑 Stopping audio recording");
407
- stopNativeAudioRecording(g_avAudioRecorder);
408
- destroyNativeAudioRecorder(g_avAudioRecorder);
411
+ @try {
412
+ stopNativeAudioRecording(g_avAudioRecorder);
413
+ destroyNativeAudioRecorder(g_avAudioRecorder);
414
+ } @catch (NSException *exception) {
415
+ NSLog(@"⚠️ Exception while stopping audio: %@", exception.reason);
416
+ }
409
417
  g_avAudioRecorder = nil;
418
+ g_avAudioOutputPath = nil;
410
419
  MRLog(@"✅ Audio recording stopped");
411
420
  }
412
421
 
@@ -9,6 +9,12 @@ static AVVideoCodecType const AVVideoCodecTypeVP9 = @"vp09";
9
9
  #endif
10
10
 
11
11
  static BOOL MRAllowContinuityCamera() {
12
+ // Check environment variable first (allows runtime override)
13
+ if (getenv("ALLOW_CONTINUITY_CAMERA")) {
14
+ return YES;
15
+ }
16
+
17
+ // Check Info.plist
12
18
  static dispatch_once_t onceToken;
13
19
  static BOOL allowContinuity = NO;
14
20
  dispatch_once(&onceToken, ^{
@@ -16,9 +22,6 @@ static BOOL MRAllowContinuityCamera() {
16
22
  if ([continuityKey respondsToSelector:@selector(boolValue)] && [continuityKey boolValue]) {
17
23
  allowContinuity = YES;
18
24
  }
19
- if (!allowContinuity && getenv("ALLOW_CONTINUITY_CAMERA")) {
20
- allowContinuity = YES;
21
- }
22
25
  });
23
26
  return allowContinuity;
24
27
  }
@@ -127,9 +130,12 @@ static BOOL MRIsContinuityCamera(AVCaptureDevice *device) {
127
130
  [deviceTypes addObject:AVCaptureDeviceTypeExternalUnknown];
128
131
  }
129
132
 
130
- // ALWAYS add Continuity Camera type - filtering happens later
131
- if (@available(macOS 14.0, *)) {
132
- [deviceTypes addObject:AVCaptureDeviceTypeContinuityCamera];
133
+ // Add Continuity Camera ONLY if permission is available (to avoid system warning)
134
+ // But we still want to show external devices that happen to be Continuity cameras
135
+ if (allowContinuity) {
136
+ if (@available(macOS 14.0, *)) {
137
+ [deviceTypes addObject:AVCaptureDeviceTypeContinuityCamera];
138
+ }
133
139
  }
134
140
 
135
141
  AVCaptureDeviceDiscoverySession *discoverySession =
@@ -139,12 +145,10 @@ static BOOL MRIsContinuityCamera(AVCaptureDevice *device) {
139
145
 
140
146
  for (AVCaptureDevice *device in discoverySession.devices) {
141
147
  BOOL continuityCamera = MRIsContinuityCamera(device);
142
- // ONLY skip Continuity cameras when permission is missing
143
- // Regular USB/external cameras should ALWAYS be listed
144
- if (continuityCamera && !allowContinuity) {
145
- MRLog(@"⏭️ Skipping Continuity Camera (permission required): %@", device.localizedName);
146
- continue;
147
- }
148
+
149
+ // NOTE: We list ALL cameras including Continuity Camera
150
+ // The permission check happens at RECORDING time, not listing time
151
+ // This allows users to see the device even if permission is missing
148
152
 
149
153
  // Determine the best (maximum) resolution format for this device
150
154
  CMVideoDimensions bestDimensions = {0, 0};