node-mac-recorder 2.16.13 โ†’ 2.16.15

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,7 +1,7 @@
1
1
  {
2
2
  "permissions": {
3
3
  "allow": [
4
- "Bash(FORCE_AVFOUNDATION=1 node -e \"\nconsole.log(''๐Ÿงช Testing Electron crash fix (macOS 14 simulation)...'');\nconst MacRecorder = require(''./index.js'');\nconst recorder = new MacRecorder();\n\nrecorder.on(''recordingStarted'', (details) => {\n console.log(''โœ… Recording started safely'');\n});\n\nrecorder.on(''stopped'', () => {\n console.log(''โœ… Recording stopped without crash'');\n});\n\nlet recordingStarted = false;\nrecorder.startRecording(''/tmp/crash-fix-test.mov'')\n .then(success => {\n console.log(''Start result:'', success ? ''โœ… SUCCESS'' : ''โŒ FAILED'');\n if (success) {\n recordingStarted = true;\n // Test quick start/stop cycles to stress test memory handling\n setTimeout(() => {\n console.log(''โน๏ธ Quick stop test...'');\n recorder.stopRecording().then(() => {\n console.log(''โœ… Quick stop completed'');\n \n // Test immediate restart\n setTimeout(() => {\n console.log(''๐Ÿ”„ Testing immediate restart...'');\n recorder.startRecording(''/tmp/crash-fix-test2.mov'').then(() => {\n setTimeout(() => {\n recorder.stopRecording().then(() => {\n console.log(''๐ŸŽ‰ Crash fix test completed successfully!'');\n const fs = require(''fs'');\n const files = [''/tmp/crash-fix-test.mov'', ''/tmp/crash-fix-test2.mov''];\n files.forEach(file => {\n if (fs.existsSync(file)) {\n const size = Math.round(fs.statSync(file).size/1024);\n console.log(''๐Ÿ“น '' + file + '':'', size + ''KB'');\n }\n });\n });\n }, 1000);\n });\n }, 500);\n });\n }, 2000);\n }\n })\n .catch(err => {\n console.error(''โŒ Error:'', err);\n if (recordingStarted) {\n recorder.stopRecording();\n }\n });\n\")"
4
+ "Bash(FORCE_AVFOUNDATION=1 node -e \"\nconsole.log(''๐Ÿงช Testing area recording with scaling...'');\nconst MacRecorder = require(''./index.js'');\nconst recorder = new MacRecorder();\n\nconst options = {\n captureArea: {\n x: 200,\n y: 200, \n width: 800,\n height: 600\n }\n};\n\nrecorder.startRecording(''/tmp/area-scaling-test.mov'', options)\n .then(success => {\n console.log(''Area recording:'', success ? ''โœ… SUCCESS'' : ''โŒ FAILED'');\n if (success) {\n setTimeout(() => {\n recorder.stopRecording().then(() => {\n console.log(''โœ… Area scaling test complete'');\n const fs = require(''fs'');\n if (fs.existsSync(''/tmp/area-scaling-test.mov'')) {\n const size = Math.round(fs.statSync(''/tmp/area-scaling-test.mov'').size/1024);\n console.log(''๐Ÿ“น File size:'', size + ''KB'');\n }\n });\n }, 2000);\n }\n })\n .catch(console.error);\n\")"
5
5
  ],
6
6
  "deny": [],
7
7
  "ask": []
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-mac-recorder",
3
- "version": "2.16.13",
3
+ "version": "2.16.15",
4
4
  "description": "Native macOS screen recording package for Node.js applications",
5
5
  "main": "index.js",
6
6
  "keywords": [
@@ -53,9 +53,33 @@ extern "C" bool startAVFoundationRecording(const std::string& outputPath,
53
53
  return false;
54
54
  }
55
55
 
56
- // Get display dimensions
56
+ // Get display dimensions with proper scaling for macOS 14/13 compatibility
57
57
  CGRect displayBounds = CGDisplayBounds(displayID);
58
- CGSize recordingSize = captureRect.size.width > 0 ? captureRect.size : displayBounds.size;
58
+
59
+ // Get both logical (bounds) and physical (pixels) dimensions
60
+ CGSize logicalSize = displayBounds.size;
61
+ CGSize physicalSize = CGSizeMake(CGDisplayPixelsWide(displayID), CGDisplayPixelsHigh(displayID));
62
+
63
+ // Calculate scale factor
64
+ CGFloat scaleX = physicalSize.width / logicalSize.width;
65
+ CGFloat scaleY = physicalSize.height / logicalSize.height;
66
+ CGFloat scaleFactor = MAX(scaleX, scaleY); // Use max to handle non-uniform scaling
67
+
68
+ // For AVFoundation, use logical size (what CGDisplayCreateImage actually captures)
69
+ CGSize recordingSize = captureRect.size.width > 0 ? captureRect.size : logicalSize;
70
+
71
+ NSLog(@"๐Ÿ–ฅ๏ธ Display bounds (logical): %.0fx%.0f", logicalSize.width, logicalSize.height);
72
+ NSLog(@"๐Ÿ–ฅ๏ธ Display pixels (physical): %.0fx%.0f", physicalSize.width, physicalSize.height);
73
+
74
+ if (scaleFactor > 1.5) {
75
+ NSLog(@"๐Ÿ” Scale factor: %.1fx โ†’ Retina display detected (macOS 14/13 scaling fix applied)", scaleFactor);
76
+ } else if (scaleFactor > 1.1) {
77
+ NSLog(@"๐Ÿ” Scale factor: %.1fx โ†’ Non-standard scaling detected", scaleFactor);
78
+ } else {
79
+ NSLog(@"๐Ÿ” Scale factor: %.1fx โ†’ Standard display", scaleFactor);
80
+ }
81
+
82
+ NSLog(@"๐ŸŽฏ Recording size: %.0fx%.0f (using logical dimensions for AVFoundation)", recordingSize.width, recordingSize.height);
59
83
 
60
84
  // Video settings with macOS compatibility
61
85
  NSString *codecKey;
@@ -118,9 +142,20 @@ extern "C" bool startAVFoundationRecording(const std::string& outputPath,
118
142
  g_avStartTime = CMTimeMakeWithSeconds(CACurrentMediaTime(), 600);
119
143
  [g_avWriter startSessionAtSourceTime:g_avStartTime];
120
144
 
121
- // Store recording parameters
145
+ // Store recording parameters with scaling correction
122
146
  g_avDisplayID = displayID;
123
- g_avCaptureRect = captureRect;
147
+
148
+ // Apply scaling to capture rect if provided (for macOS 14/13 compatibility)
149
+ if (!CGRectIsEmpty(captureRect)) {
150
+ // Note: captureRect comes in logical coordinates, keep as-is for CGDisplayCreateImage
151
+ g_avCaptureRect = captureRect;
152
+ NSLog(@"๐Ÿ”ฒ Capture area (logical): %.0f,%.0f %.0fx%.0f",
153
+ captureRect.origin.x, captureRect.origin.y, captureRect.size.width, captureRect.size.height);
154
+ } else {
155
+ g_avCaptureRect = CGRectZero; // Full screen
156
+ NSLog(@"๐Ÿ–ฅ๏ธ Full screen capture (logical bounds)");
157
+ }
158
+
124
159
  g_avFrameNumber = 0;
125
160
 
126
161
  // Start capture timer (10 FPS for Electron compatibility)
@@ -173,6 +208,16 @@ extern "C" bool startAVFoundationRecording(const std::string& outputPath,
173
208
  CVReturn cvRet = CVPixelBufferPoolCreatePixelBuffer(NULL, localPixelBufferAdaptor.pixelBufferPool, &pixelBuffer);
174
209
 
175
210
  if (cvRet == kCVReturnSuccess && pixelBuffer) {
211
+ // Check pixel buffer dimensions match screen image
212
+ size_t bufferWidth = CVPixelBufferGetWidth(pixelBuffer);
213
+ size_t bufferHeight = CVPixelBufferGetHeight(pixelBuffer);
214
+ size_t imageWidth = CGImageGetWidth(screenImage);
215
+ size_t imageHeight = CGImageGetHeight(screenImage);
216
+
217
+ if (bufferWidth != imageWidth || bufferHeight != imageHeight) {
218
+ NSLog(@"โš ๏ธ Size mismatch! Buffer %zux%zu vs Image %zux%zu", bufferWidth, bufferHeight, imageWidth, imageHeight);
219
+ }
220
+
176
221
  CVPixelBufferLockBaseAddress(pixelBuffer, 0);
177
222
 
178
223
  void *pixelData = CVPixelBufferGetBaseAddress(pixelBuffer);
@@ -875,25 +875,32 @@ void updateOverlay() {
875
875
  }
876
876
 
877
877
  // Determine if this is a primary display window
878
- BOOL isPrimaryDisplayWindow = (x >= 0 && x <= 2048); // Primary display width
878
+ NSArray *allScreens = [NSScreen screens];
879
+ NSScreen *primaryScreen = [allScreens objectAtIndex:0]; // Primary screen
880
+ NSRect primaryFrame = [primaryScreen frame];
881
+ BOOL isPrimaryDisplayWindow = (x >= primaryFrame.origin.x &&
882
+ x <= primaryFrame.origin.x + primaryFrame.size.width &&
883
+ y >= primaryFrame.origin.y &&
884
+ y <= primaryFrame.origin.y + primaryFrame.size.height);
879
885
 
880
886
  CGFloat localX, localY;
881
887
  if (isPrimaryDisplayWindow) {
882
- // Primary display windows: Offset to their position in the combined overlay
883
- // Primary display starts at (3440, 56) within the combined frame
884
- localX = x + 3440; // Primary starts 3440px from overlay origin
885
- localY = ([g_overlayView frame].size.height - (y + 56)) - height; // Y offset for primary
888
+ // Primary display windows: Calculate dynamic offset from combined frame
889
+ NSRect combinedFrame = [g_overlayWindow frame];
890
+
891
+ // Calculate primary screen offset within combined frame
892
+ CGFloat primaryOffsetX = primaryFrame.origin.x - combinedFrame.origin.x;
893
+ CGFloat primaryOffsetY = primaryFrame.origin.y - combinedFrame.origin.y;
894
+
895
+ localX = x + primaryOffsetX;
896
+ localY = ([g_overlayView frame].size.height - (y + primaryOffsetY)) - height;
897
+
886
898
  } else {
887
899
  // Secondary display windows: Apply standard coordinate transformation
888
900
  localX = x - globalOffset.x;
889
901
  localY = ([g_overlayView frame].size.height - (y - globalOffset.y)) - height;
890
902
  }
891
903
 
892
- NSLog(@"๐Ÿ”ง COORDINATE DEBUG: Window (%d, %d) %dx%d [%@]", (int)x, (int)y, (int)width, (int)height, isPrimaryDisplayWindow ? @"PRIMARY" : @"SECONDARY");
893
- NSLog(@" GlobalOffset: (%.0f, %.0f)", globalOffset.x, globalOffset.y);
894
- NSLog(@" LocalCoords: (%.0f, %.0f)", localX, localY);
895
- NSLog(@" ViewFrame: %.0fx%.0f", [g_overlayView frame].size.width, [g_overlayView frame].size.height);
896
-
897
904
  // Update overlay view window info for highlighting
898
905
  [overlayView setWindowInfo:targetWindow];
899
906
  [overlayView setHighlightFrame:NSMakeRect(localX, localY, width, height)];