node-mac-recorder 2.21.8 → 2.21.9

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.
@@ -12,7 +12,8 @@
12
12
  "Bash(MAC_RECORDER_DEBUG=1 timeout 5 node:*)",
13
13
  "Read(//private/tmp/**)",
14
14
  "Bash(ffprobe:*)",
15
- "Bash(MAC_RECORDER_DEBUG=1 timeout 8 node:*)"
15
+ "Bash(MAC_RECORDER_DEBUG=1 timeout 8 node:*)",
16
+ "Bash(cat:*)"
16
17
  ],
17
18
  "deny": [],
18
19
  "ask": []
package/index.js CHANGED
@@ -152,21 +152,40 @@ class MacRecorder extends EventEmitter {
152
152
  * Kayıt seçeneklerini ayarlar
153
153
  */
154
154
  setOptions(options = {}) {
155
- this.options = {
156
- includeMicrophone: options.includeMicrophone === true, // Explicit true required, default false
157
- includeSystemAudio: options.includeSystemAudio === true, // Explicit true required, default false
158
- captureCursor: options.captureCursor || false,
159
- displayId: options.displayId || null, // null = ana ekran
160
- windowId: options.windowId || null, // null = tam ekran
161
- audioDeviceId: options.audioDeviceId || null, // null = default device
162
- systemAudioDeviceId: options.systemAudioDeviceId || null, // null = auto-detect system audio device
163
- captureArea: options.captureArea || null,
164
- captureCamera: options.captureCamera === true,
165
- cameraDeviceId:
155
+ // Merge options instead of replacing to preserve previously set values
156
+ if (options.includeMicrophone !== undefined) {
157
+ this.options.includeMicrophone = options.includeMicrophone === true;
158
+ }
159
+ if (options.includeSystemAudio !== undefined) {
160
+ this.options.includeSystemAudio = options.includeSystemAudio === true;
161
+ }
162
+ if (options.captureCursor !== undefined) {
163
+ this.options.captureCursor = options.captureCursor || false;
164
+ }
165
+ if (options.displayId !== undefined) {
166
+ this.options.displayId = options.displayId || null;
167
+ }
168
+ if (options.windowId !== undefined) {
169
+ this.options.windowId = options.windowId || null;
170
+ }
171
+ if (options.audioDeviceId !== undefined) {
172
+ this.options.audioDeviceId = options.audioDeviceId || null;
173
+ }
174
+ if (options.systemAudioDeviceId !== undefined) {
175
+ this.options.systemAudioDeviceId = options.systemAudioDeviceId || null;
176
+ }
177
+ if (options.captureArea !== undefined) {
178
+ this.options.captureArea = options.captureArea || null;
179
+ }
180
+ if (options.captureCamera !== undefined) {
181
+ this.options.captureCamera = options.captureCamera === true;
182
+ }
183
+ if (options.cameraDeviceId !== undefined) {
184
+ this.options.cameraDeviceId =
166
185
  typeof options.cameraDeviceId === "string" && options.cameraDeviceId.length > 0
167
186
  ? options.cameraDeviceId
168
- : null,
169
- };
187
+ : null;
188
+ }
170
189
  }
171
190
 
172
191
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-mac-recorder",
3
- "version": "2.21.8",
3
+ "version": "2.21.9",
4
4
  "description": "Native macOS screen recording package for Node.js applications",
5
5
  "main": "index.js",
6
6
  "keywords": [
@@ -26,23 +26,24 @@ static void* g_avAudioRecorder = nil;
26
26
  static NSString* g_avAudioOutputPath = nil;
27
27
 
28
28
  // AVFoundation screen recording implementation
29
- extern "C" bool startAVFoundationRecording(const std::string& outputPath,
29
+ extern "C" bool startAVFoundationRecording(const std::string& outputPath,
30
30
  CGDirectDisplayID displayID,
31
31
  uint32_t windowID,
32
32
  CGRect captureRect,
33
33
  bool captureCursor,
34
- bool includeMicrophone,
34
+ bool includeMicrophone,
35
35
  bool includeSystemAudio,
36
- NSString* audioDeviceId) {
36
+ NSString* audioDeviceId,
37
+ NSString* audioOutputPath) {
37
38
 
38
39
  if (g_avIsRecording) {
39
40
  NSLog(@"❌ AVFoundation recording already in progress");
40
41
  return false;
41
42
  }
42
-
43
+
43
44
  @try {
44
45
  MRLog(@"🎬 AVFoundation: Starting recording initialization");
45
-
46
+
46
47
  // Create output URL
47
48
  NSString *outputPathStr = [NSString stringWithUTF8String:outputPath.c_str()];
48
49
  NSURL *outputURL = [NSURL fileURLWithPath:outputPathStr];
@@ -210,12 +211,22 @@ extern "C" bool startAVFoundationRecording(const std::string& outputPath,
210
211
  if (includeMicrophone || includeSystemAudio) {
211
212
  MRLog(@"🎤 Starting audio capture (mic=%d, system=%d)", includeMicrophone, includeSystemAudio);
212
213
 
213
- // Generate audio output path
214
- NSString *videoDir = [outputPathStr stringByDeletingLastPathComponent];
215
- NSString *audioFilename = [NSString stringWithFormat:@"avf_audio_%ld.mov", (long)[[NSDate date] timeIntervalSince1970]];
216
- g_avAudioOutputPath = [videoDir stringByAppendingPathComponent:audioFilename];
214
+ // Use provided audio output path or generate one
215
+ if (audioOutputPath && [audioOutputPath length] > 0) {
216
+ g_avAudioOutputPath = audioOutputPath;
217
+ MRLog(@"🎤 Using provided audio path: %@", g_avAudioOutputPath);
218
+ } else {
219
+ NSString *videoDir = [outputPathStr stringByDeletingLastPathComponent];
220
+ 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);
223
+ }
217
224
 
218
- MRLog(@"🎤 Audio output: %@", g_avAudioOutputPath);
225
+ // 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);
229
+ }
219
230
 
220
231
  // Create audio recorder
221
232
  g_avAudioRecorder = createNativeAudioRecorder();
@@ -225,7 +236,7 @@ extern "C" bool startAVFoundationRecording(const std::string& outputPath,
225
236
  const char* outputPathCStr = [g_avAudioOutputPath UTF8String];
226
237
 
227
238
  if (startNativeAudioRecording(g_avAudioRecorder, deviceIdCStr, outputPathCStr)) {
228
- MRLog(@"✅ Audio recording started");
239
+ MRLog(@"✅ Audio recording started to: %@", g_avAudioOutputPath);
229
240
  } else {
230
241
  NSLog(@"❌ Failed to start audio recording");
231
242
  destroyNativeAudioRecorder(g_avAudioRecorder);
@@ -452,3 +463,7 @@ extern "C" bool stopAVFoundationRecording() {
452
463
  extern "C" bool isAVFoundationRecording() {
453
464
  return g_avIsRecording;
454
465
  }
466
+
467
+ extern "C" NSString* getAVFoundationAudioPath() {
468
+ return g_avAudioOutputPath;
469
+ }
@@ -11,16 +11,18 @@
11
11
 
12
12
  // AVFoundation fallback declarations
13
13
  extern "C" {
14
- bool startAVFoundationRecording(const std::string& outputPath,
14
+ bool startAVFoundationRecording(const std::string& outputPath,
15
15
  CGDirectDisplayID displayID,
16
16
  uint32_t windowID,
17
17
  CGRect captureRect,
18
18
  bool captureCursor,
19
- bool includeMicrophone,
19
+ bool includeMicrophone,
20
20
  bool includeSystemAudio,
21
- NSString* audioDeviceId);
21
+ NSString* audioDeviceId,
22
+ NSString* audioOutputPath);
22
23
  bool stopAVFoundationRecording();
23
24
  bool isAVFoundationRecording();
25
+ NSString* getAVFoundationAudioPath();
24
26
 
25
27
  NSArray<NSDictionary *> *listCameraDevices();
26
28
  bool startCameraRecording(NSString *outputPath, NSString *deviceId, NSError **error);
@@ -196,7 +198,7 @@ Napi::Value StartRecording(const Napi::CallbackInfo& info) {
196
198
 
197
199
  if (info.Length() > 1 && info[1].IsObject()) {
198
200
  Napi::Object options = info[1].As<Napi::Object>();
199
-
201
+
200
202
  // Capture area
201
203
  if (options.Has("captureArea") && options.Get("captureArea").IsObject()) {
202
204
  Napi::Object rectObj = options.Get("captureArea").As<Napi::Object>();
@@ -217,15 +219,15 @@ Napi::Value StartRecording(const Napi::CallbackInfo& info) {
217
219
 
218
220
  // Microphone
219
221
  if (options.Has("includeMicrophone")) {
220
- includeMicrophone = options.Get("includeMicrophone").As<Napi::Boolean>();
222
+ includeMicrophone = options.Get("includeMicrophone").As<Napi::Boolean>().Value();
221
223
  }
222
-
224
+
223
225
  // Audio device ID
224
226
  if (options.Has("audioDeviceId") && !options.Get("audioDeviceId").IsNull()) {
225
227
  std::string deviceId = options.Get("audioDeviceId").As<Napi::String>().Utf8Value();
226
228
  audioDeviceId = [NSString stringWithUTF8String:deviceId.c_str()];
227
229
  }
228
-
230
+
229
231
  // System audio
230
232
  if (options.Has("includeSystemAudio")) {
231
233
  includeSystemAudio = options.Get("includeSystemAudio").As<Napi::Boolean>();
@@ -349,9 +351,7 @@ Napi::Value StartRecording(const Napi::CallbackInfo& info) {
349
351
  BOOL forceAVFoundation = YES;
350
352
 
351
353
  MRLog(@"🔧 CRITICAL: ScreenCaptureKit disabled globally (segfault issue)");
352
- MRLog(@" Using AVFoundation for stability");
353
- MRLog(@" ⚠️ WARNING: Audio capture not available in AVFoundation mode");
354
- MRLog(@" Audio files will be created but will be empty");
354
+ MRLog(@" Using AVFoundation for stability with integrated audio capture");
355
355
 
356
356
  if (isElectron) {
357
357
  MRLog(@"⚡ Electron environment detected - using stable AVFoundation");
@@ -474,20 +474,21 @@ Napi::Value StartRecording(const Napi::CallbackInfo& info) {
474
474
 
475
475
  // AVFoundation recording (either fallback from ScreenCaptureKit or direct)
476
476
  MRLog(@"🎥 Starting AVFoundation recording...");
477
-
477
+
478
478
  @try {
479
479
  // Import AVFoundation recording functions (if available)
480
- extern bool startAVFoundationRecording(const std::string& outputPath,
480
+ extern bool startAVFoundationRecording(const std::string& outputPath,
481
481
  CGDirectDisplayID displayID,
482
482
  uint32_t windowID,
483
483
  CGRect captureRect,
484
484
  bool captureCursor,
485
- bool includeMicrophone,
485
+ bool includeMicrophone,
486
486
  bool includeSystemAudio,
487
- NSString* audioDeviceId);
488
-
489
- bool avResult = startAVFoundationRecording(outputPath, displayID, windowID, captureRect,
490
- captureCursor, includeMicrophone, includeSystemAudio, audioDeviceId);
487
+ NSString* audioDeviceId,
488
+ NSString* audioOutputPath);
489
+
490
+ bool avResult = startAVFoundationRecording(outputPath, displayID, windowID, captureRect,
491
+ captureCursor, includeMicrophone, includeSystemAudio, audioDeviceId, audioOutputPath);
491
492
 
492
493
  if (avResult) {
493
494
  MRLog(@"🎥 RECORDING METHOD: AVFoundation");
@@ -554,6 +555,7 @@ Napi::Value StopRecording(const Napi::CallbackInfo& info) {
554
555
  // Try AVFoundation fallback (supports both Node.js and Electron)
555
556
  extern bool isAVFoundationRecording();
556
557
  extern bool stopAVFoundationRecording();
558
+ extern NSString* getAVFoundationAudioPath();
557
559
 
558
560
  @try {
559
561
  if (isAVFoundationRecording()) {
@@ -867,6 +869,9 @@ Napi::Value GetAudioRecordingPath(const Napi::CallbackInfo& info) {
867
869
  if (!path || [path length] == 0) {
868
870
  path = currentStandaloneAudioRecordingPath();
869
871
  }
872
+ if (!path || [path length] == 0) {
873
+ path = getAVFoundationAudioPath();
874
+ }
870
875
  if (!path || [path length] == 0) {
871
876
  return env.Null();
872
877
  }