node-mac-recorder 2.17.0 → 2.17.2

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/index.js CHANGED
@@ -357,18 +357,62 @@ class MacRecorder extends EventEmitter {
357
357
  this.recordingStartTime = Date.now();
358
358
 
359
359
  // Start cursor tracking automatically with recording
360
- this.startCursorCapture(cursorFilePath, {
361
- windowRelative: !!this.options.windowId,
362
- windowInfo: this.options.windowId ? {
363
- x: this.options.captureArea?.x || 0,
364
- y: this.options.captureArea?.y || 0,
365
- width: this.options.captureArea?.width || 1920,
366
- height: this.options.captureArea?.height || 1080,
367
- displayId: this.options.displayId
368
- } : null
369
- }).catch(cursorError => {
370
- console.warn('Cursor tracking failed to start:', cursorError.message);
371
- });
360
+ let cursorOptions = {};
361
+
362
+ // For window recording, use the original window coordinates (not display-relative captureArea)
363
+ if (this.options.windowId) {
364
+ // Use cached window info from the earlier window detection
365
+ this.getWindows().then(windows => {
366
+ const targetWindow = windows.find(w => w.id === this.options.windowId);
367
+ if (targetWindow) {
368
+ // Restart cursor capture with correct window coordinates
369
+ if (this.cursorCaptureInterval) {
370
+ this.stopCursorCapture().then(() => {
371
+ this.startCursorCapture(cursorFilePath, {
372
+ windowRelative: true,
373
+ windowInfo: {
374
+ x: targetWindow.x, // Global window X coordinate
375
+ y: targetWindow.y, // Global window Y coordinate
376
+ width: targetWindow.width,
377
+ height: targetWindow.height,
378
+ displayId: this.options.displayId,
379
+ originalWindow: targetWindow
380
+ }
381
+ }).catch(cursorError => {
382
+ console.warn('Cursor tracking failed to restart:', cursorError.message);
383
+ });
384
+ }).catch(stopError => {
385
+ console.warn('Failed to stop cursor capture:', stopError.message);
386
+ });
387
+ } else {
388
+ this.startCursorCapture(cursorFilePath, {
389
+ windowRelative: true,
390
+ windowInfo: {
391
+ x: targetWindow.x, // Global window X coordinate
392
+ y: targetWindow.y, // Global window Y coordinate
393
+ width: targetWindow.width,
394
+ height: targetWindow.height,
395
+ displayId: this.options.displayId,
396
+ originalWindow: targetWindow
397
+ }
398
+ }).catch(cursorError => {
399
+ console.warn('Cursor tracking failed to start:', cursorError.message);
400
+ });
401
+ }
402
+ }
403
+ }).catch(error => {
404
+ console.warn('Could not get window info for cursor tracking:', error.message);
405
+ // Fallback to basic cursor tracking
406
+ this.startCursorCapture(cursorFilePath, cursorOptions).catch(cursorError => {
407
+ console.warn('Cursor tracking failed to start:', cursorError.message);
408
+ });
409
+ });
410
+ } else {
411
+ // For display recording, use basic cursor tracking
412
+ this.startCursorCapture(cursorFilePath, cursorOptions).catch(cursorError => {
413
+ console.warn('Cursor tracking failed to start:', cursorError.message);
414
+ });
415
+ }
372
416
 
373
417
  // Timer başlat (progress tracking için)
374
418
  this.recordingTimer = setInterval(() => {
@@ -728,32 +772,9 @@ class MacRecorder extends EventEmitter {
728
772
  let coordinateSystem = "global";
729
773
 
730
774
  if (this.cursorDisplayInfo) {
731
- // CRITICAL FIX: Handle DPR scaling for cursor coordinates
732
- // Get scaling information from native cursor position
733
- const scaleFactor = position.scaleFactor || 1;
734
- const displayInfo = position.displayInfo;
735
-
736
- // Convert logical cursor position to physical (if recording uses physical coordinates)
737
- let physicalX = position.x;
738
- let physicalY = position.y;
739
-
740
- if (scaleFactor > 1.1 && displayInfo) {
741
- // Convert to display-relative logical, then to physical
742
- const displayRelativeX = position.x - displayInfo.displayX;
743
- const displayRelativeY = position.y - displayInfo.displayY;
744
-
745
- // Scale to physical coordinates
746
- const physicalRelativeX = displayRelativeX * scaleFactor;
747
- const physicalRelativeY = displayRelativeY * scaleFactor;
748
-
749
- // Convert back to global physical
750
- physicalX = displayInfo.displayX + physicalRelativeX;
751
- physicalY = displayInfo.displayY + physicalRelativeY;
752
- }
753
-
754
- // Offset'leri çıkar (display veya window) - use physical coordinates
755
- x = physicalX - this.cursorDisplayInfo.x;
756
- y = physicalY - this.cursorDisplayInfo.y;
775
+ // Offset'leri çıkar (display veya window)
776
+ x = position.x - this.cursorDisplayInfo.x;
777
+ y = position.y - this.cursorDisplayInfo.y;
757
778
 
758
779
  if (this.cursorDisplayInfo.windowRelative) {
759
780
  // Window-relative koordinatlar
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-mac-recorder",
3
- "version": "2.17.0",
3
+ "version": "2.17.2",
4
4
  "description": "Native macOS screen recording package for Node.js applications",
5
5
  "main": "index.js",
6
6
  "keywords": [
@@ -0,0 +1,149 @@
1
+ #!/usr/bin/env node
2
+
3
+ const MacRecorder = require('./index.js');
4
+ const path = require('path');
5
+ const fs = require('fs');
6
+
7
+ // Test output directory
8
+ const outputDir = './test-output';
9
+ if (!fs.existsSync(outputDir)) {
10
+ fs.mkdirSync(outputDir, { recursive: true });
11
+ }
12
+
13
+ const recorder = new MacRecorder();
14
+
15
+ async function testWindowRecording() {
16
+ console.log('🎬 Testing window recording with automatic cursor tracking...');
17
+
18
+ try {
19
+ // Test permissions first
20
+ const permissions = await recorder.checkPermissions();
21
+ console.log('📋 Permissions:', permissions);
22
+
23
+ if (!permissions.screenRecording) {
24
+ console.log('❌ Screen recording permission required');
25
+ return;
26
+ }
27
+
28
+ // Get windows
29
+ const windows = await recorder.getWindows();
30
+ console.log(`🪟 Found ${windows.length} windows`);
31
+
32
+ // Find a suitable window to record (preferably a visible one)
33
+ const visibleWindows = windows.filter(w => w.width > 200 && w.height > 100);
34
+ if (visibleWindows.length === 0) {
35
+ console.log('❌ No suitable windows found for recording');
36
+ return;
37
+ }
38
+
39
+ const targetWindow = visibleWindows[0];
40
+ console.log(`🎯 Target window: ${targetWindow.appName} (${targetWindow.width}x${targetWindow.height})`);
41
+ console.log(`📍 Window position: (${targetWindow.x}, ${targetWindow.y})`);
42
+
43
+ // Setup recording options
44
+ const timestamp = Date.now();
45
+ const videoPath = path.join(outputDir, `window-test-${timestamp}.mov`);
46
+
47
+ console.log(`🎥 Starting window recording to: ${videoPath}`);
48
+ console.log('🖱️ Cursor tracking will use window-relative coordinates');
49
+ console.log('💡 Move your mouse over the target window to generate cursor data');
50
+
51
+ // Start recording the specific window (cursor tracking will start automatically)
52
+ await recorder.startRecording(videoPath, {
53
+ includeMicrophone: false,
54
+ includeSystemAudio: false,
55
+ captureCursor: true,
56
+ windowId: targetWindow.id
57
+ });
58
+
59
+ console.log('✅ Window recording started successfully!');
60
+ console.log('🖱️ Cursor tracking started automatically with window-relative coordinates');
61
+ console.log('⏰ Recording for 8 seconds...');
62
+ console.log(`🔍 Please move your mouse over the "${targetWindow.appName}" window`);
63
+
64
+ // Wait for 8 seconds
65
+ await new Promise(resolve => setTimeout(resolve, 8000));
66
+
67
+ console.log('🛑 Stopping recording...');
68
+
69
+ // Stop recording (cursor tracking will stop automatically)
70
+ const result = await recorder.stopRecording();
71
+
72
+ console.log('✅ Recording stopped:', result);
73
+ console.log('📁 Checking output files...');
74
+
75
+ // Wait a moment for files to be written
76
+ await new Promise(resolve => setTimeout(resolve, 1000));
77
+
78
+ // Check for video file
79
+ if (fs.existsSync(videoPath)) {
80
+ const videoStats = fs.statSync(videoPath);
81
+ console.log(`📹 Video file created: ${path.basename(videoPath)} (${videoStats.size} bytes)`);
82
+ } else {
83
+ console.log('❌ Video file not found');
84
+ }
85
+
86
+ // Check for cursor file
87
+ const files = fs.readdirSync(outputDir);
88
+ const cursorFile = files.find(f => f.startsWith('temp_cursor_') && f.endsWith('.json'));
89
+
90
+ if (cursorFile) {
91
+ const cursorPath = path.join(outputDir, cursorFile);
92
+ const cursorStats = fs.statSync(cursorPath);
93
+ console.log(`🖱️ Cursor file created: ${cursorFile} (${cursorStats.size} bytes)`);
94
+
95
+ // Read and validate cursor data
96
+ try {
97
+ const cursorData = JSON.parse(fs.readFileSync(cursorPath, 'utf8'));
98
+ console.log(`📊 Cursor data points: ${cursorData.length}`);
99
+
100
+ if (cursorData.length > 0) {
101
+ const first = cursorData[0];
102
+ const last = cursorData[cursorData.length - 1];
103
+
104
+ console.log(`📍 First cursor position: (${first.x}, ${first.y}) at ${first.timestamp}ms`);
105
+ console.log(`📍 Last cursor position: (${last.x}, ${last.y}) at ${last.timestamp}ms`);
106
+ console.log(`📐 Coordinate system: ${first.coordinateSystem}`);
107
+
108
+ if (first.windowInfo) {
109
+ console.log(`🪟 Window info: ${first.windowInfo.width}x${first.windowInfo.height}`);
110
+ console.log(`🌐 Original window position: (${first.windowInfo.originalWindow?.x}, ${first.windowInfo.originalWindow?.y})`);
111
+ }
112
+
113
+ // Analyze cursor positions
114
+ const windowRelativePositions = cursorData.filter(d => d.coordinateSystem === 'window-relative');
115
+ if (windowRelativePositions.length > 0) {
116
+ console.log(`✅ ${windowRelativePositions.length} window-relative cursor positions recorded`);
117
+
118
+ // Check if coordinates are within window bounds
119
+ const validPositions = windowRelativePositions.filter(d =>
120
+ d.x >= 0 && d.y >= 0 &&
121
+ d.x <= targetWindow.width && d.y <= targetWindow.height
122
+ );
123
+
124
+ console.log(`✅ ${validPositions.length}/${windowRelativePositions.length} positions within window bounds`);
125
+ }
126
+ }
127
+ } catch (parseError) {
128
+ console.log('⚠️ Could not parse cursor data:', parseError.message);
129
+ }
130
+ } else {
131
+ console.log('❌ Cursor file not found');
132
+ }
133
+
134
+ console.log('✅ Window recording test completed!');
135
+
136
+ } catch (error) {
137
+ console.error('❌ Test failed:', error.message);
138
+ console.error(error.stack);
139
+ }
140
+ }
141
+
142
+ // Run the test
143
+ testWindowRecording().then(() => {
144
+ console.log('🏁 Window recording test finished');
145
+ process.exit(0);
146
+ }).catch(error => {
147
+ console.error('💥 Fatal error:', error);
148
+ process.exit(1);
149
+ });