node-mac-recorder 2.22.23 → 2.22.32

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/binding.gyp CHANGED
@@ -11,7 +11,6 @@
11
11
  "src/audio_recorder.mm",
12
12
  "src/audio_mixer.mm",
13
13
  "src/cursor_tracker.mm",
14
- "src/text_input_ax_snapshot.mm",
15
14
  "src/window_selector.mm"
16
15
  ],
17
16
  "include_dirs": [
@@ -7,7 +7,6 @@
7
7
  "src/electron_safe/screen_capture_electron.mm",
8
8
  "src/electron_safe/audio_capture_electron.mm",
9
9
  "src/electron_safe/cursor_tracker_electron.mm",
10
- "src/text_input_ax_snapshot.mm",
11
10
  "src/electron_safe/window_selector_electron.mm"
12
11
  ],
13
12
  "include_dirs": [
@@ -1,7 +1,6 @@
1
1
  const { EventEmitter } = require("events");
2
2
  const path = require("path");
3
3
  const fs = require("fs");
4
- const cursorCapturePolling = require("./lib/cursorCapture/polling");
5
4
 
6
5
  // Electron-safe native module loading
7
6
  let electronSafeNativeBinding;
@@ -42,17 +41,6 @@ class ElectronSafeMacRecorder extends EventEmitter {
42
41
  this.recordingTimer = null;
43
42
  this.recordingStartTime = null;
44
43
 
45
- this.cursorCaptureInterval = null;
46
- this.cursorCaptureFile = null;
47
- this.cursorCaptureStartTime = null;
48
- this.cursorCaptureFirstWrite = true;
49
- this.lastCapturedData = null;
50
- this.cursorDisplayInfo = null;
51
- this.recordingDisplayInfo = null;
52
- this.cursorCaptureSessionTimestamp = null;
53
- this.sessionTimestamp = null;
54
- this.syncTimestamp = null;
55
-
56
44
  this.options = {
57
45
  includeMicrophone: false,
58
46
  includeSystemAudio: false,
@@ -97,8 +85,10 @@ class ElectronSafeMacRecorder extends EventEmitter {
97
85
  throw new Error("Output path is required");
98
86
  }
99
87
 
88
+ // Update options
100
89
  this.setOptions(options);
101
90
 
91
+ // Ensure output directory exists
102
92
  const outputDir = path.dirname(outputPath);
103
93
  if (!fs.existsSync(outputDir)) {
104
94
  fs.mkdirSync(outputDir, { recursive: true });
@@ -106,123 +96,63 @@ class ElectronSafeMacRecorder extends EventEmitter {
106
96
 
107
97
  this.outputPath = outputPath;
108
98
 
109
- console.log("🎬 Starting Electron-safe recording...");
110
- console.log("📁 Output path:", outputPath);
111
- console.log("⚙️ Options:", this.options);
112
-
113
- const startTimeout = setTimeout(() => {
114
- this.isRecording = false;
115
- }, 10000);
116
-
117
- let success = false;
118
- try {
119
- success = electronSafeNativeBinding.startRecording(
120
- outputPath,
121
- this.options,
122
- );
123
- } finally {
124
- clearTimeout(startTimeout);
125
- }
126
-
127
- if (!success) {
128
- console.error("❌ Failed to start Electron-safe recording");
129
- throw new Error("Failed to start recording - check permissions");
130
- }
131
-
132
- this.isRecording = true;
133
- this.recordingStartTime = Date.now();
134
-
135
- this.recordingTimer = setInterval(() => {
136
- const elapsed = Math.floor(
137
- (Date.now() - this.recordingStartTime) / 1000,
138
- );
139
- this.emit("timeUpdate", elapsed);
140
- }, 1000);
141
-
142
- const sessionTs =
143
- options.sessionTimestamp ||
144
- this.sessionTimestamp ||
145
- this.recordingStartTime;
146
- this.sessionTimestamp = sessionTs;
147
- const cursorFilePath = path.join(
148
- outputDir,
149
- `temp_cursor_${sessionTs}.json`,
150
- );
151
-
152
- let recordingDisplayInfo = null;
153
- try {
154
- const displays = await this.getDisplays();
155
- const did = this.options.displayId;
156
- let target = null;
157
- if (did != null && did !== undefined) {
158
- target = displays.find((d) => d.id === did);
159
- }
160
- if (!target) {
161
- target = displays.find((d) => d.isPrimary) || displays[0];
162
- }
163
- if (target) {
164
- recordingDisplayInfo = {
165
- displayId: target.id,
166
- x: target.x || 0,
167
- y: target.y || 0,
168
- width: target.width,
169
- height: target.height,
170
- logicalWidth: target.width,
171
- logicalHeight: target.height,
172
- };
99
+ return new Promise((resolve, reject) => {
100
+ try {
101
+ console.log("🎬 Starting Electron-safe recording...");
102
+ console.log("📁 Output path:", outputPath);
103
+ console.log("⚙️ Options:", this.options);
104
+
105
+ // Call native function with timeout protection
106
+ const startTimeout = setTimeout(() => {
107
+ this.isRecording = false;
108
+ reject(new Error("Recording start timeout - Electron protection"));
109
+ }, 10000); // 10 second timeout
110
+
111
+ const success = electronSafeNativeBinding.startRecording(
112
+ outputPath,
113
+ this.options
114
+ );
115
+ clearTimeout(startTimeout);
116
+
117
+ if (success) {
118
+ this.isRecording = true;
119
+ this.recordingStartTime = Date.now();
120
+
121
+ // Start progress timer
122
+ this.recordingTimer = setInterval(() => {
123
+ const elapsed = Math.floor(
124
+ (Date.now() - this.recordingStartTime) / 1000
125
+ );
126
+ this.emit("timeUpdate", elapsed);
127
+ }, 1000);
128
+
129
+ // Emit started event
130
+ setTimeout(() => {
131
+ this.emit("recordingStarted", {
132
+ outputPath: this.outputPath,
133
+ timestamp: this.recordingStartTime,
134
+ options: this.options,
135
+ electronSafe: true,
136
+ });
137
+ }, 100);
138
+
139
+ this.emit("started", this.outputPath);
140
+ console.log("✅ Electron-safe recording started successfully");
141
+ resolve(this.outputPath);
142
+ } else {
143
+ console.error("❌ Failed to start Electron-safe recording");
144
+ reject(new Error("Failed to start recording - check permissions"));
145
+ }
146
+ } catch (error) {
147
+ console.error("❌ Exception during recording start:", error);
148
+ this.isRecording = false;
149
+ if (this.recordingTimer) {
150
+ clearInterval(this.recordingTimer);
151
+ this.recordingTimer = null;
152
+ }
153
+ reject(error);
173
154
  }
174
- } catch {
175
- recordingDisplayInfo = null;
176
- }
177
- this.recordingDisplayInfo = recordingDisplayInfo;
178
-
179
- const syncTimestamp = Date.now();
180
- this.syncTimestamp = syncTimestamp;
181
-
182
- try {
183
- await cursorCapturePolling.startCursorCapture(
184
- this,
185
- electronSafeNativeBinding,
186
- cursorFilePath,
187
- {
188
- videoRelative: !!recordingDisplayInfo,
189
- displayInfo: recordingDisplayInfo,
190
- recordingType: this.options.windowId
191
- ? "window"
192
- : this.options.captureArea
193
- ? "area"
194
- : "display",
195
- captureArea: this.options.captureArea || null,
196
- windowId: this.options.windowId || null,
197
- startTimestamp: syncTimestamp,
198
- },
199
- );
200
- } catch (cursorError) {
201
- console.warn(
202
- "⚠️ Cursor tracking failed to start:",
203
- cursorError.message,
204
- );
205
- }
206
-
207
- const startPayloadTs = this.syncTimestamp || this.recordingStartTime;
208
- const fileTimestampPayload = this.sessionTimestamp;
209
-
210
- setTimeout(() => {
211
- this.emit("recordingStarted", {
212
- outputPath: this.outputPath,
213
- timestamp: startPayloadTs,
214
- options: this.options,
215
- electronSafe: true,
216
- cursorOutputPath: cursorFilePath,
217
- sessionTimestamp: fileTimestampPayload,
218
- syncTimestamp: startPayloadTs,
219
- fileTimestamp: fileTimestampPayload,
220
- });
221
- }, 100);
222
-
223
- this.emit("started", this.outputPath);
224
- console.log("✅ Electron-safe recording started successfully");
225
- return this.outputPath;
155
+ });
226
156
  }
227
157
 
228
158
  /**
@@ -233,66 +163,64 @@ class ElectronSafeMacRecorder extends EventEmitter {
233
163
  throw new Error("No recording in progress");
234
164
  }
235
165
 
236
- try {
237
- console.log("🛑 Stopping Electron-safe recording...");
238
-
239
- if (this.cursorCaptureInterval) {
240
- try {
241
- await cursorCapturePolling.stopCursorCapture(this);
242
- } catch (cursorErr) {
243
- console.warn(
244
- "⚠️ Cursor capture stop:",
245
- cursorErr.message,
246
- );
247
- }
248
- }
166
+ return new Promise((resolve, reject) => {
167
+ try {
168
+ console.log("🛑 Stopping Electron-safe recording...");
169
+
170
+ // Call native function with timeout protection
171
+ const stopTimeout = setTimeout(() => {
172
+ this.isRecording = false;
173
+ if (this.recordingTimer) {
174
+ clearInterval(this.recordingTimer);
175
+ this.recordingTimer = null;
176
+ }
177
+ reject(new Error("Recording stop timeout - forced cleanup"));
178
+ }, 10000); // 10 second timeout
179
+
180
+ const success = electronSafeNativeBinding.stopRecording();
181
+ clearTimeout(stopTimeout);
249
182
 
250
- const stopTimeout = setTimeout(() => {
183
+ // Always cleanup
251
184
  this.isRecording = false;
252
185
  if (this.recordingTimer) {
253
186
  clearInterval(this.recordingTimer);
254
187
  this.recordingTimer = null;
255
188
  }
256
- }, 10000);
257
189
 
258
- const success = electronSafeNativeBinding.stopRecording();
259
- clearTimeout(stopTimeout);
260
-
261
- this.isRecording = false;
262
- if (this.recordingTimer) {
263
- clearInterval(this.recordingTimer);
264
- this.recordingTimer = null;
265
- }
190
+ const result = {
191
+ code: success ? 0 : 1,
192
+ outputPath: this.outputPath,
193
+ electronSafe: true,
194
+ };
266
195
 
267
- const result = {
268
- code: success ? 0 : 1,
269
- outputPath: this.outputPath,
270
- electronSafe: true,
271
- };
196
+ this.emit("stopped", result);
197
+
198
+ if (success) {
199
+ // Check if file exists
200
+ setTimeout(() => {
201
+ if (fs.existsSync(this.outputPath)) {
202
+ this.emit("completed", this.outputPath);
203
+ console.log("✅ Recording completed successfully");
204
+ } else {
205
+ console.warn("⚠️ Recording completed but file not found");
206
+ }
207
+ }, 1000);
208
+ }
272
209
 
273
- this.emit("stopped", result);
210
+ resolve(result);
211
+ } catch (error) {
212
+ console.error("❌ Exception during recording stop:", error);
274
213
 
275
- if (success) {
276
- setTimeout(() => {
277
- if (fs.existsSync(this.outputPath)) {
278
- this.emit("completed", this.outputPath);
279
- console.log("✅ Recording completed successfully");
280
- } else {
281
- console.warn("⚠️ Recording completed but file not found");
282
- }
283
- }, 1000);
284
- }
214
+ // Force cleanup
215
+ this.isRecording = false;
216
+ if (this.recordingTimer) {
217
+ clearInterval(this.recordingTimer);
218
+ this.recordingTimer = null;
219
+ }
285
220
 
286
- return result;
287
- } catch (error) {
288
- console.error("❌ Exception during recording stop:", error);
289
- this.isRecording = false;
290
- if (this.recordingTimer) {
291
- clearInterval(this.recordingTimer);
292
- this.recordingTimer = null;
221
+ reject(error);
293
222
  }
294
- throw error;
295
- }
223
+ });
296
224
  }
297
225
 
298
226
  /**
@@ -452,26 +380,6 @@ class ElectronSafeMacRecorder extends EventEmitter {
452
380
  }
453
381
  }
454
382
 
455
- async startCursorCapture(intervalOrFilepath, options = {}) {
456
- if (!loadElectronSafeModule()) {
457
- throw new Error("Failed to load Electron-safe native module");
458
- }
459
- return cursorCapturePolling.startCursorCapture(
460
- this,
461
- electronSafeNativeBinding,
462
- intervalOrFilepath,
463
- options,
464
- );
465
- }
466
-
467
- async stopCursorCapture() {
468
- loadElectronSafeModule();
469
- if (!electronSafeNativeBinding) {
470
- return false;
471
- }
472
- return cursorCapturePolling.stopCursorCapture(this);
473
- }
474
-
475
383
  /**
476
384
  * Get module information
477
385
  */