node-mac-recorder 2.22.5 → 2.22.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-mac-recorder",
3
- "version": "2.22.5",
3
+ "version": "2.22.6",
4
4
  "description": "Native macOS screen recording package for Node.js applications",
5
5
  "main": "index.js",
6
6
  "keywords": [
@@ -1383,11 +1383,46 @@ static void SCKPerformRecordingSetup(NSDictionary *config, SCShareableContent *c
1383
1383
  }
1384
1384
 
1385
1385
  if (targetWindow && targetApp) {
1386
- MRLog(@"🪟 Recording window: %@ (%ux%u)",
1387
- targetWindow.title, (unsigned)targetWindow.frame.size.width, (unsigned)targetWindow.frame.size.height);
1386
+ CGFloat windowLogicalWidth = targetWindow.frame.size.width;
1387
+ CGFloat windowLogicalHeight = targetWindow.frame.size.height;
1388
+
1389
+ // Find display containing this window to get scale factor
1390
+ CGFloat scaleFactor = 1.0;
1391
+ CGPoint windowCenter = CGPointMake(
1392
+ targetWindow.frame.origin.x + windowLogicalWidth / 2.0,
1393
+ targetWindow.frame.origin.y + windowLogicalHeight / 2.0
1394
+ );
1395
+ for (SCDisplay *disp in content.displays) {
1396
+ CGRect dispBounds = CGRectMake(disp.frame.origin.x, disp.frame.origin.y,
1397
+ disp.frame.size.width, disp.frame.size.height);
1398
+ if (CGRectContainsPoint(dispBounds, windowCenter)) {
1399
+ NSInteger physW = (NSInteger)CGDisplayPixelsWide(disp.displayID);
1400
+ NSInteger physH = (NSInteger)CGDisplayPixelsHigh(disp.displayID);
1401
+ CGFloat scX = (disp.width > 0) ? (CGFloat)physW / (CGFloat)disp.width : 1.0;
1402
+ CGFloat scY = (disp.height > 0) ? (CGFloat)physH / (CGFloat)disp.height : 1.0;
1403
+ scaleFactor = MAX(scX, scY);
1404
+ break;
1405
+ }
1406
+ }
1407
+ // Fallback: use main display scale factor
1408
+ if (scaleFactor == 1.0 && content.displays.count > 0) {
1409
+ SCDisplay *mainDisp = content.displays.firstObject;
1410
+ NSInteger physW = (NSInteger)CGDisplayPixelsWide(mainDisp.displayID);
1411
+ NSInteger physH = (NSInteger)CGDisplayPixelsHigh(mainDisp.displayID);
1412
+ CGFloat scX = (mainDisp.width > 0) ? (CGFloat)physW / (CGFloat)mainDisp.width : 1.0;
1413
+ CGFloat scY = (mainDisp.height > 0) ? (CGFloat)physH / (CGFloat)mainDisp.height : 1.0;
1414
+ scaleFactor = MAX(scX, scY);
1415
+ }
1416
+
1417
+ NSInteger physicalWindowWidth = (NSInteger)(windowLogicalWidth * scaleFactor);
1418
+ NSInteger physicalWindowHeight = (NSInteger)(windowLogicalHeight * scaleFactor);
1419
+
1420
+ MRLog(@"🪟 Recording window: %@ logical=%ux%u, physical=%ldx%ld, scale=%.2fx",
1421
+ targetWindow.title, (unsigned)windowLogicalWidth, (unsigned)windowLogicalHeight,
1422
+ (long)physicalWindowWidth, (long)physicalWindowHeight, scaleFactor);
1388
1423
  filter = [[SCContentFilter alloc] initWithDesktopIndependentWindow:targetWindow];
1389
- recordingWidth = (NSInteger)targetWindow.frame.size.width;
1390
- recordingHeight = (NSInteger)targetWindow.frame.size.height;
1424
+ recordingWidth = physicalWindowWidth;
1425
+ recordingHeight = physicalWindowHeight;
1391
1426
  } else {
1392
1427
  NSLog(@"❌ Window ID %@ not found", windowId);
1393
1428
  SCKFailScheduling();
@@ -1421,11 +1456,24 @@ static void SCKPerformRecordingSetup(NSDictionary *config, SCShareableContent *c
1421
1456
  }
1422
1457
 
1423
1458
  if (targetDisplay) {
1424
- MRLog(@"🖥️ Recording display %u (%dx%d)",
1425
- targetDisplay.displayID, (int)targetDisplay.width, (int)targetDisplay.height);
1459
+ // Use physical pixel dimensions for Retina displays (not logical points)
1460
+ CGDirectDisplayID displayID = targetDisplay.displayID;
1461
+ NSInteger physicalWidth = (NSInteger)CGDisplayPixelsWide(displayID);
1462
+ NSInteger physicalHeight = (NSInteger)CGDisplayPixelsHigh(displayID);
1463
+ NSInteger logicalWidth = targetDisplay.width;
1464
+ NSInteger logicalHeight = targetDisplay.height;
1465
+
1466
+ CGFloat scaleX = (logicalWidth > 0) ? (CGFloat)physicalWidth / (CGFloat)logicalWidth : 1.0;
1467
+ CGFloat scaleY = (logicalHeight > 0) ? (CGFloat)physicalHeight / (CGFloat)logicalHeight : 1.0;
1468
+ CGFloat scaleFactor = MAX(scaleX, scaleY);
1469
+
1470
+ MRLog(@"🖥️ Recording display %u: logical=%dx%d, physical=%ldx%ld, scale=%.2fx",
1471
+ displayID, (int)logicalWidth, (int)logicalHeight,
1472
+ (long)physicalWidth, (long)physicalHeight, scaleFactor);
1473
+
1426
1474
  filter = [[SCContentFilter alloc] initWithDisplay:targetDisplay excludingWindows:@[]];
1427
- recordingWidth = targetDisplay.width;
1428
- recordingHeight = targetDisplay.height;
1475
+ recordingWidth = physicalWidth;
1476
+ recordingHeight = physicalHeight;
1429
1477
  } else {
1430
1478
  NSLog(@"❌ No display available");
1431
1479
  SCKFailScheduling();
@@ -1437,11 +1485,22 @@ static void SCKPerformRecordingSetup(NSDictionary *config, SCShareableContent *c
1437
1485
  CGFloat cropWidth = [captureRect[@"width"] doubleValue];
1438
1486
  CGFloat cropHeight = [captureRect[@"height"] doubleValue];
1439
1487
  if (cropWidth > 0 && cropHeight > 0) {
1440
- MRLog(@"🔲 Crop area specified: %.0fx%.0f at (%.0f,%.0f)",
1441
- cropWidth, cropHeight,
1488
+ // Scale crop dimensions for Retina displays
1489
+ CGFloat cropScaleFactor = 1.0;
1490
+ if (targetDisplay) {
1491
+ NSInteger physW = (NSInteger)CGDisplayPixelsWide(targetDisplay.displayID);
1492
+ NSInteger physH = (NSInteger)CGDisplayPixelsHigh(targetDisplay.displayID);
1493
+ CGFloat scX = (targetDisplay.width > 0) ? (CGFloat)physW / (CGFloat)targetDisplay.width : 1.0;
1494
+ CGFloat scY = (targetDisplay.height > 0) ? (CGFloat)physH / (CGFloat)targetDisplay.height : 1.0;
1495
+ cropScaleFactor = MAX(scX, scY);
1496
+ }
1497
+ NSInteger physicalCropWidth = (NSInteger)(cropWidth * cropScaleFactor);
1498
+ NSInteger physicalCropHeight = (NSInteger)(cropHeight * cropScaleFactor);
1499
+ MRLog(@"🔲 Crop area: logical=%.0fx%.0f, physical=%ldx%ld, scale=%.2fx at (%.0f,%.0f)",
1500
+ cropWidth, cropHeight, (long)physicalCropWidth, (long)physicalCropHeight, cropScaleFactor,
1442
1501
  [captureRect[@"x"] doubleValue], [captureRect[@"y"] doubleValue]);
1443
- recordingWidth = (NSInteger)cropWidth;
1444
- recordingHeight = (NSInteger)cropHeight;
1502
+ recordingWidth = physicalCropWidth;
1503
+ recordingHeight = physicalCropHeight;
1445
1504
  }
1446
1505
  }
1447
1506
 
@@ -1,80 +0,0 @@
1
- const MacRecorder = require('./index.js');
2
- const path = require('path');
3
- const fs = require('fs');
4
-
5
- // Test output directory
6
- const outputDir = path.join(__dirname, 'test-output');
7
- if (!fs.existsSync(outputDir)) {
8
- fs.mkdirSync(outputDir, { recursive: true });
9
- }
10
-
11
- const timestamp = Date.now();
12
- const outputPath = path.join(outputDir, `noise-reduction-test-${timestamp}.mov`);
13
-
14
- console.log('🎙️ Starting microphone recording with noise reduction...');
15
- console.log('📝 Instructions:');
16
- console.log(' 1. Speak into your microphone (normal voice)');
17
- console.log(' 2. Type on your keyboard (heavy typing)');
18
- console.log(' 3. Click your mouse multiple times');
19
- console.log(' 4. Speak again to compare');
20
- console.log('');
21
- console.log('⏱️ Recording for 15 seconds...');
22
- console.log('');
23
-
24
- const recorder = new MacRecorder();
25
-
26
- recorder.on('recordingStarted', () => {
27
- console.log('✅ Recording started!');
28
- console.log('🎤 Custom noise reduction is ACTIVE');
29
- console.log('');
30
- console.log('Starting countdown:');
31
-
32
- let countdown = 15;
33
- const interval = setInterval(() => {
34
- process.stdout.write(`\r⏱️ ${countdown} seconds remaining... `);
35
- countdown--;
36
-
37
- if (countdown < 0) {
38
- clearInterval(interval);
39
- process.stdout.write('\r');
40
- console.log('⏱️ Time up! Stopping...');
41
- console.log('');
42
-
43
- recorder.stopRecording()
44
- .then(() => {
45
- console.log('✅ Recording saved to:', outputPath);
46
- console.log('');
47
- console.log('🎧 Play the recording to verify:');
48
- console.log(` open "${outputPath}"`);
49
- console.log('');
50
- console.log('Expected results:');
51
- console.log(' ✅ Voice should be clear');
52
- console.log(' ✅ Keyboard typing should be significantly reduced');
53
- console.log(' ✅ Mouse clicks should be filtered out');
54
- process.exit(0);
55
- })
56
- .catch(err => {
57
- console.error('❌ Error stopping:', err);
58
- process.exit(1);
59
- });
60
- }
61
- }, 1000);
62
- });
63
-
64
- recorder.on('stopped', () => {
65
- console.log('🛑 Recording stopped');
66
- });
67
-
68
- recorder.on('completed', (result) => {
69
- console.log('✅ Recording completed:', result);
70
- });
71
-
72
- // Start recording with microphone
73
- recorder.startRecording(outputPath, {
74
- includeMicrophone: true,
75
- includeSystemAudio: false,
76
- fps: 1 // Minimal FPS since we're only testing audio
77
- }).catch(err => {
78
- console.error('❌ Failed to start recording:', err);
79
- process.exit(1);
80
- });