node-mac-recorder 2.17.19 → 2.17.20

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
@@ -456,9 +456,11 @@ class MacRecorder extends EventEmitter {
456
456
 
457
457
  // Stop cursor tracking automatically
458
458
  if (this.cursorCaptureInterval) {
459
- this.stopCursorCapture().catch(cursorError => {
459
+ try {
460
+ this.stopCursorCapture();
461
+ } catch (cursorError) {
460
462
  console.warn('Cursor tracking failed to stop:', cursorError.message);
461
- });
463
+ }
462
464
  }
463
465
 
464
466
  // Timer durdur
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-mac-recorder",
3
- "version": "2.17.19",
3
+ "version": "2.17.20",
4
4
  "description": "Native macOS screen recording package for Node.js applications",
5
5
  "main": "index.js",
6
6
  "keywords": [
@@ -0,0 +1 @@
1
+ [{"x":609,"y":795,"timestamp":184,"unixTimeMs":1758313956779,"cursorType":"default","type":"move","coordinateSystem":"display-relative"},{"x":609,"y":795,"timestamp":205,"unixTimeMs":1758313956800,"cursorType":"default","type":"mousedown","coordinateSystem":"display-relative"},{"x":609,"y":795,"timestamp":224,"unixTimeMs":1758313956819,"cursorType":"default","type":"move","coordinateSystem":"display-relative"},{"x":609,"y":795,"timestamp":286,"unixTimeMs":1758313956881,"cursorType":"default","type":"mouseup","coordinateSystem":"display-relative"},{"x":609,"y":795,"timestamp":306,"unixTimeMs":1758313956901,"cursorType":"default","type":"move","coordinateSystem":"display-relative"},{"x":609,"y":802,"timestamp":824,"unixTimeMs":1758313957419,"cursorType":"default","type":"move","coordinateSystem":"display-relative"},{"x":608,"y":812,"timestamp":842,"unixTimeMs":1758313957437,"cursorType":"default","type":"move","coordinateSystem":"display-relative"},{"x":605,"y":828,"timestamp":863,"unixTimeMs":1758313957458,"cursorType":"default","type":"move","coordinateSystem":"display-relative"},{"x":601,"y":851,"timestamp":883,"unixTimeMs":1758313957478,"cursorType":"default","type":"move","coordinateSystem":"display-relative"},{"x":596,"y":877,"timestamp":905,"unixTimeMs":1758313957500,"cursorType":"default","type":"move","coordinateSystem":"display-relative"},{"x":594,"y":887,"timestamp":923,"unixTimeMs":1758313957518,"cursorType":"default","type":"move","coordinateSystem":"display-relative"},{"x":591,"y":900,"timestamp":942,"unixTimeMs":1758313957537,"cursorType":"default","type":"move","coordinateSystem":"display-relative"},{"x":590,"y":905,"timestamp":965,"unixTimeMs":1758313957560,"cursorType":"default","type":"move","coordinateSystem":"display-relative"},{"x":589,"y":908,"timestamp":984,"unixTimeMs":1758313957579,"cursorType":"default","type":"move","coordinateSystem":"display-relative"},{"x":588,"y":910,"timestamp":1025,"unixTimeMs":1758313957620,"cursorType":"default","type":"move","coordinateSystem":"display-relative"},{"x":588,"y":912,"timestamp":1065,"unixTimeMs":1758313957660,"cursorType":"default","type":"move","coordinateSystem":"display-relative"},{"x":588,"y":914,"timestamp":1124,"unixTimeMs":1758313957719,"cursorType":"default","type":"move","coordinateSystem":"display-relative"},{"x":587,"y":917,"timestamp":1145,"unixTimeMs":1758313957740,"cursorType":"default","type":"move","coordinateSystem":"display-relative"},{"x":586,"y":920,"timestamp":1167,"unixTimeMs":1758313957762,"cursorType":"default","type":"move","coordinateSystem":"display-relative"},{"x":586,"y":922,"timestamp":1186,"unixTimeMs":1758313957781,"cursorType":"default","type":"move","coordinateSystem":"display-relative"}
@@ -0,0 +1,83 @@
1
+ #!/usr/bin/env node
2
+
3
+ const MacRecorder = require('./index.js');
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+
7
+ async function testRecordingWithCursor() {
8
+ const recorder = new MacRecorder();
9
+
10
+ console.log('🎯 Testing recording with cursor tracking...');
11
+
12
+ // Create test output directory
13
+ const outputDir = './test-output';
14
+ if (!fs.existsSync(outputDir)) {
15
+ fs.mkdirSync(outputDir);
16
+ }
17
+
18
+ const videoPath = path.join(outputDir, `test-recording-${Date.now()}.mov`);
19
+
20
+ try {
21
+ console.log('Starting recording with auto cursor tracking...');
22
+
23
+ // Start recording (should auto-start cursor tracking)
24
+ await recorder.startRecording(videoPath, {
25
+ captureCursor: true, // This should trigger auto cursor tracking
26
+ quality: 'low',
27
+ frameRate: 10
28
+ });
29
+
30
+ console.log('✅ Recording started successfully');
31
+ console.log('🔥 Recording for 3 seconds...');
32
+
33
+ // Wait 3 seconds
34
+ await new Promise(resolve => setTimeout(resolve, 3000));
35
+
36
+ console.log('Stopping recording...');
37
+ const stopResult = await recorder.stopRecording();
38
+ console.log('✅ Recording stopped:', stopResult);
39
+
40
+ // Wait a bit for file to be written
41
+ await new Promise(resolve => setTimeout(resolve, 1000));
42
+
43
+ // Check if files exist
44
+ if (fs.existsSync(videoPath)) {
45
+ const videoStats = fs.statSync(videoPath);
46
+ console.log('📹 Video file size:', videoStats.size, 'bytes');
47
+ } else {
48
+ console.log('❌ Video file not found:', videoPath);
49
+ }
50
+
51
+ // Check for cursor files
52
+ const cursorFiles = fs.readdirSync(outputDir).filter(f => f.includes('cursor') && f.endsWith('.json'));
53
+ console.log('📊 Cursor files found:', cursorFiles.length);
54
+
55
+ if (cursorFiles.length > 0) {
56
+ const cursorFile = path.join(outputDir, cursorFiles[0]);
57
+ const cursorContent = fs.readFileSync(cursorFile, 'utf8');
58
+ console.log('📁 Cursor file size:', cursorContent.length, 'bytes');
59
+
60
+ if (cursorContent.trim()) {
61
+ try {
62
+ const cursorData = JSON.parse(cursorContent);
63
+ console.log('📍 Cursor data points:', cursorData.length);
64
+ } catch (parseError) {
65
+ console.log('⚠️ Cursor file content preview:', cursorContent.substring(0, 200));
66
+ }
67
+ }
68
+ }
69
+
70
+ } catch (error) {
71
+ console.error('❌ Test failed:', error.message);
72
+ console.error('Stack:', error.stack);
73
+ }
74
+ }
75
+
76
+ // Run test
77
+ testRecordingWithCursor().then(() => {
78
+ console.log('\n✨ Test completed');
79
+ process.exit(0);
80
+ }).catch(error => {
81
+ console.error('💥 Test error:', error);
82
+ process.exit(1);
83
+ });
@@ -1 +0,0 @@
1
- []
@@ -1 +0,0 @@
1
- []
package/cursor-data.json DELETED
@@ -1 +0,0 @@
1
- [{"x":1151,"y":726,"timestamp":20,"cursorType":"text","type":"move"}
@@ -1 +0,0 @@
1
- [{"x":48,"y":72,"timestamp":22,"unixTimeMs":1752410259890,"cursorType":"pointer","type":"move"},{"x":47,"y":71,"timestamp":87,"unixTimeMs":1752410259955,"cursorType":"pointer","type":"mousedown"},{"x":47,"y":71,"timestamp":107,"unixTimeMs":1752410259975,"cursorType":"pointer","type":"move"},{"x":47,"y":71,"timestamp":169,"unixTimeMs":1752410260037,"cursorType":"pointer","type":"mouseup"},{"x":47,"y":71,"timestamp":781,"unixTimeMs":1752410260649,"cursorType":"default","type":"move"}]
@@ -1,230 +0,0 @@
1
- // Electron Integration Example for node-mac-recorder
2
- // This example shows how to use the Electron-safe version in an Electron app
3
-
4
- const { app, BrowserWindow, ipcMain, dialog } = require("electron");
5
- const path = require("path");
6
-
7
- // Import the Electron-safe version
8
- const ElectronSafeMacRecorder = require("../electron-safe-index");
9
-
10
- let mainWindow;
11
- let recorder;
12
-
13
- function createWindow() {
14
- // Create the browser window
15
- mainWindow = new BrowserWindow({
16
- width: 1200,
17
- height: 800,
18
- webPreferences: {
19
- nodeIntegration: false,
20
- contextIsolation: true,
21
- preload: path.join(__dirname, "electron-preload.js"),
22
- },
23
- });
24
-
25
- // Load the app
26
- mainWindow.loadFile("electron-renderer.html");
27
-
28
- // Initialize the Electron-safe recorder
29
- try {
30
- recorder = new ElectronSafeMacRecorder();
31
- console.log("✅ ElectronSafeMacRecorder initialized");
32
-
33
- // Setup event listeners
34
- recorder.on("recordingStarted", (data) => {
35
- console.log("🎬 Recording started:", data);
36
- mainWindow.webContents.send("recording-started", data);
37
- });
38
-
39
- recorder.on("stopped", (result) => {
40
- console.log("🛑 Recording stopped:", result);
41
- mainWindow.webContents.send("recording-stopped", result);
42
- });
43
-
44
- recorder.on("completed", (outputPath) => {
45
- console.log("✅ Recording completed:", outputPath);
46
- mainWindow.webContents.send("recording-completed", outputPath);
47
- });
48
-
49
- recorder.on("timeUpdate", (elapsed) => {
50
- mainWindow.webContents.send("recording-time-update", elapsed);
51
- });
52
- } catch (error) {
53
- console.error("❌ Failed to initialize recorder:", error);
54
- dialog.showErrorBox(
55
- "Recorder Error",
56
- "Failed to initialize screen recorder. Please ensure the Electron-safe module is built."
57
- );
58
- }
59
- }
60
-
61
- // IPC handlers for safe communication with renderer
62
- ipcMain.handle("recorder:getModuleInfo", async () => {
63
- try {
64
- return recorder ? recorder.getModuleInfo() : null;
65
- } catch (error) {
66
- console.error("Error getting module info:", error);
67
- return { error: error.message };
68
- }
69
- });
70
-
71
- ipcMain.handle("recorder:checkPermissions", async () => {
72
- try {
73
- return await recorder.checkPermissions();
74
- } catch (error) {
75
- console.error("Error checking permissions:", error);
76
- return { error: error.message };
77
- }
78
- });
79
-
80
- ipcMain.handle("recorder:getDisplays", async () => {
81
- try {
82
- return await recorder.getDisplays();
83
- } catch (error) {
84
- console.error("Error getting displays:", error);
85
- return [];
86
- }
87
- });
88
-
89
- ipcMain.handle("recorder:getWindows", async () => {
90
- try {
91
- return await recorder.getWindows();
92
- } catch (error) {
93
- console.error("Error getting windows:", error);
94
- return [];
95
- }
96
- });
97
-
98
- ipcMain.handle(
99
- "recorder:startRecording",
100
- async (event, outputPath, options) => {
101
- try {
102
- if (!recorder) {
103
- throw new Error("Recorder not initialized");
104
- }
105
-
106
- console.log("🎬 Starting recording with options:", options);
107
- const result = await recorder.startRecording(outputPath, options);
108
- return { success: true, result };
109
- } catch (error) {
110
- console.error("Error starting recording:", error);
111
- return { success: false, error: error.message };
112
- }
113
- }
114
- );
115
-
116
- ipcMain.handle("recorder:stopRecording", async () => {
117
- try {
118
- if (!recorder) {
119
- throw new Error("Recorder not initialized");
120
- }
121
-
122
- console.log("🛑 Stopping recording");
123
- const result = await recorder.stopRecording();
124
- return { success: true, result };
125
- } catch (error) {
126
- console.error("Error stopping recording:", error);
127
- return { success: false, error: error.message };
128
- }
129
- });
130
-
131
- ipcMain.handle("recorder:getStatus", async () => {
132
- try {
133
- return recorder ? recorder.getStatus() : null;
134
- } catch (error) {
135
- console.error("Error getting status:", error);
136
- return { error: error.message };
137
- }
138
- });
139
-
140
- ipcMain.handle("recorder:getCursorPosition", async () => {
141
- try {
142
- return recorder ? recorder.getCursorPosition() : null;
143
- } catch (error) {
144
- console.error("Error getting cursor position:", error);
145
- return { error: error.message };
146
- }
147
- });
148
-
149
- ipcMain.handle(
150
- "recorder:getDisplayThumbnail",
151
- async (event, displayId, options) => {
152
- try {
153
- return await recorder.getDisplayThumbnail(displayId, options);
154
- } catch (error) {
155
- console.error("Error getting display thumbnail:", error);
156
- return null;
157
- }
158
- }
159
- );
160
-
161
- ipcMain.handle(
162
- "recorder:getWindowThumbnail",
163
- async (event, windowId, options) => {
164
- try {
165
- return await recorder.getWindowThumbnail(windowId, options);
166
- } catch (error) {
167
- console.error("Error getting window thumbnail:", error);
168
- return null;
169
- }
170
- }
171
- );
172
-
173
- ipcMain.handle("dialog:showSaveDialog", async () => {
174
- const result = await dialog.showSaveDialog(mainWindow, {
175
- title: "Save Recording",
176
- defaultPath: "recording.mov",
177
- filters: [{ name: "Movies", extensions: ["mov", "mp4"] }],
178
- });
179
- return result;
180
- });
181
-
182
- // App event listeners
183
- app.whenReady().then(createWindow);
184
-
185
- app.on("window-all-closed", () => {
186
- // Stop any ongoing recording before quitting
187
- if (recorder && recorder.getStatus().isRecording) {
188
- console.log("🛑 Stopping recording before quit");
189
- recorder.stopRecording().finally(() => {
190
- if (process.platform !== "darwin") {
191
- app.quit();
192
- }
193
- });
194
- } else {
195
- if (process.platform !== "darwin") {
196
- app.quit();
197
- }
198
- }
199
- });
200
-
201
- app.on("activate", () => {
202
- if (BrowserWindow.getAllWindows().length === 0) {
203
- createWindow();
204
- }
205
- });
206
-
207
- // Handle app termination gracefully
208
- process.on("SIGINT", async () => {
209
- console.log("🛑 SIGINT received, stopping recording...");
210
- if (recorder && recorder.getStatus().isRecording) {
211
- try {
212
- await recorder.stopRecording();
213
- } catch (error) {
214
- console.error("Error stopping recording on exit:", error);
215
- }
216
- }
217
- app.quit();
218
- });
219
-
220
- process.on("SIGTERM", async () => {
221
- console.log("🛑 SIGTERM received, stopping recording...");
222
- if (recorder && recorder.getStatus().isRecording) {
223
- try {
224
- await recorder.stopRecording();
225
- } catch (error) {
226
- console.error("Error stopping recording on exit:", error);
227
- }
228
- }
229
- app.quit();
230
- });
@@ -1,46 +0,0 @@
1
- // Preload script for secure IPC communication
2
- const { contextBridge, ipcRenderer } = require("electron");
3
-
4
- // Expose protected methods that allow the renderer process to use
5
- // the ipcRenderer without exposing the entire object
6
- contextBridge.exposeInMainWorld("electronAPI", {
7
- // Recorder API
8
- recorder: {
9
- getModuleInfo: () => ipcRenderer.invoke("recorder:getModuleInfo"),
10
- checkPermissions: () => ipcRenderer.invoke("recorder:checkPermissions"),
11
- getDisplays: () => ipcRenderer.invoke("recorder:getDisplays"),
12
- getWindows: () => ipcRenderer.invoke("recorder:getWindows"),
13
- startRecording: (outputPath, options) =>
14
- ipcRenderer.invoke("recorder:startRecording", outputPath, options),
15
- stopRecording: () => ipcRenderer.invoke("recorder:stopRecording"),
16
- getStatus: () => ipcRenderer.invoke("recorder:getStatus"),
17
- getCursorPosition: () => ipcRenderer.invoke("recorder:getCursorPosition"),
18
- getDisplayThumbnail: (displayId, options) =>
19
- ipcRenderer.invoke("recorder:getDisplayThumbnail", displayId, options),
20
- getWindowThumbnail: (windowId, options) =>
21
- ipcRenderer.invoke("recorder:getWindowThumbnail", windowId, options),
22
-
23
- // Event listeners
24
- onRecordingStarted: (callback) =>
25
- ipcRenderer.on("recording-started", callback),
26
- onRecordingStopped: (callback) =>
27
- ipcRenderer.on("recording-stopped", callback),
28
- onRecordingCompleted: (callback) =>
29
- ipcRenderer.on("recording-completed", callback),
30
- onTimeUpdate: (callback) =>
31
- ipcRenderer.on("recording-time-update", callback),
32
-
33
- // Remove listeners
34
- removeAllListeners: () => {
35
- ipcRenderer.removeAllListeners("recording-started");
36
- ipcRenderer.removeAllListeners("recording-stopped");
37
- ipcRenderer.removeAllListeners("recording-completed");
38
- ipcRenderer.removeAllListeners("recording-time-update");
39
- },
40
- },
41
-
42
- // Dialog API
43
- dialog: {
44
- showSaveDialog: () => ipcRenderer.invoke("dialog:showSaveDialog"),
45
- },
46
- });