node-mac-recorder 2.11.0 → 2.12.1
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 +1 -1
- package/src/mac_recorder.mm +16 -4
- package/src/screen_capture_kit.mm +52 -3
- package/src/window_selector.mm +17 -0
package/package.json
CHANGED
package/src/mac_recorder.mm
CHANGED
|
@@ -183,12 +183,13 @@ Napi::Value StartRecording(const Napi::CallbackInfo& info) {
|
|
|
183
183
|
};
|
|
184
184
|
}
|
|
185
185
|
|
|
186
|
-
// Use ScreenCaptureKit
|
|
186
|
+
// Use ScreenCaptureKit with window exclusion
|
|
187
187
|
NSError *sckError = nil;
|
|
188
188
|
if ([ScreenCaptureKitRecorder startRecordingWithConfiguration:sckConfig
|
|
189
189
|
delegate:g_delegate
|
|
190
190
|
error:&sckError]) {
|
|
191
|
-
NSLog(@"
|
|
191
|
+
NSLog(@"🎬 RECORDING METHOD: ScreenCaptureKit");
|
|
192
|
+
NSLog(@"✅ ScreenCaptureKit recording started with window exclusion");
|
|
192
193
|
g_isRecording = true;
|
|
193
194
|
return Napi::Boolean::New(env, true);
|
|
194
195
|
} else {
|
|
@@ -198,6 +199,7 @@ Napi::Value StartRecording(const Napi::CallbackInfo& info) {
|
|
|
198
199
|
}
|
|
199
200
|
|
|
200
201
|
// Fallback: Use AVFoundation (older macOS or ScreenCaptureKit failure)
|
|
202
|
+
NSLog(@"🎬 RECORDING METHOD: AVFoundation");
|
|
201
203
|
NSLog(@"📼 Falling back to AVFoundation - overlay windows may appear in recording");
|
|
202
204
|
|
|
203
205
|
// Create capture session
|
|
@@ -356,6 +358,7 @@ Napi::Value StartRecording(const Napi::CallbackInfo& info) {
|
|
|
356
358
|
NSURL *outputURL = [NSURL fileURLWithPath:[NSString stringWithUTF8String:outputPath.c_str()]];
|
|
357
359
|
[g_movieFileOutput startRecordingToOutputFileURL:outputURL recordingDelegate:g_delegate];
|
|
358
360
|
|
|
361
|
+
NSLog(@"✅ AVFoundation recording started");
|
|
359
362
|
g_isRecording = true;
|
|
360
363
|
return Napi::Boolean::New(env, true);
|
|
361
364
|
|
|
@@ -374,8 +377,17 @@ Napi::Value StopRecording(const Napi::CallbackInfo& info) {
|
|
|
374
377
|
}
|
|
375
378
|
|
|
376
379
|
@try {
|
|
377
|
-
|
|
378
|
-
|
|
380
|
+
if (g_movieFileOutput) {
|
|
381
|
+
[g_movieFileOutput stopRecording];
|
|
382
|
+
}
|
|
383
|
+
if (g_captureSession) {
|
|
384
|
+
[g_captureSession stopRunning];
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// Try to stop ScreenCaptureKit if it's being used
|
|
388
|
+
if (@available(macOS 12.3, *)) {
|
|
389
|
+
[ScreenCaptureKitRecorder stopRecording];
|
|
390
|
+
}
|
|
379
391
|
|
|
380
392
|
g_isRecording = false;
|
|
381
393
|
return Napi::Boolean::New(env, true);
|
|
@@ -59,19 +59,68 @@ static BOOL g_isRecording = NO;
|
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
// Get current app windows to exclude
|
|
62
|
+
// Get current process and Electron app windows to exclude
|
|
63
63
|
NSMutableArray *excludedWindows = [NSMutableArray array];
|
|
64
|
+
NSMutableArray *excludedApps = [NSMutableArray array];
|
|
65
|
+
|
|
66
|
+
// Exclude current Node.js process windows (overlay selectors)
|
|
64
67
|
for (SCWindow *window in content.windows) {
|
|
65
68
|
if (window.owningApplication.processID == currentPID) {
|
|
66
69
|
[excludedWindows addObject:window];
|
|
67
|
-
NSLog(@"🚫 Excluding overlay window: %@ (PID: %d)", window.title, currentPID);
|
|
70
|
+
NSLog(@"🚫 Excluding Node.js overlay window: %@ (PID: %d)", window.title, currentPID);
|
|
68
71
|
}
|
|
69
72
|
}
|
|
70
73
|
|
|
71
|
-
//
|
|
74
|
+
// Also try to exclude Electron app if running (common overlay use case)
|
|
75
|
+
for (SCWindow *window in content.windows) {
|
|
76
|
+
NSString *appName = window.owningApplication.applicationName;
|
|
77
|
+
NSString *windowTitle = window.title ? window.title : @"<No Title>";
|
|
78
|
+
|
|
79
|
+
// Debug: Log all windows to see what we're dealing with (only for small subset)
|
|
80
|
+
if ([appName containsString:@"Electron"] || [windowTitle containsString:@"camera"]) {
|
|
81
|
+
NSLog(@"📋 Found potential exclude window: '%@' from app: '%@' (PID: %d, Level: %ld)",
|
|
82
|
+
windowTitle, appName, window.owningApplication.processID, (long)window.windowLayer);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Comprehensive Electron window detection
|
|
86
|
+
BOOL shouldExclude = NO;
|
|
87
|
+
|
|
88
|
+
// Check app name patterns
|
|
89
|
+
if ([appName containsString:@"Electron"] ||
|
|
90
|
+
[appName isEqualToString:@"electron"] ||
|
|
91
|
+
[appName isEqualToString:@"Electron Helper"]) {
|
|
92
|
+
shouldExclude = YES;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Check window title patterns
|
|
96
|
+
if ([windowTitle containsString:@"Electron"] ||
|
|
97
|
+
[windowTitle containsString:@"camera"] ||
|
|
98
|
+
[windowTitle containsString:@"Camera"] ||
|
|
99
|
+
[windowTitle containsString:@"overlay"] ||
|
|
100
|
+
[windowTitle containsString:@"Overlay"]) {
|
|
101
|
+
shouldExclude = YES;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Check window properties (transparent, always on top windows)
|
|
105
|
+
if (window.windowLayer > 100) { // High window levels (like alwaysOnTop)
|
|
106
|
+
shouldExclude = YES;
|
|
107
|
+
NSLog(@"📋 High-level window detected: '%@' (Level: %ld)", windowTitle, (long)window.windowLayer);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (shouldExclude) {
|
|
111
|
+
[excludedWindows addObject:window];
|
|
112
|
+
NSLog(@"🚫 Excluding window: '%@' from %@ (PID: %d, Level: %ld)",
|
|
113
|
+
windowTitle, appName, window.owningApplication.processID, (long)window.windowLayer);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
NSLog(@"📊 Total windows to exclude: %lu", (unsigned long)excludedWindows.count);
|
|
118
|
+
|
|
119
|
+
// Create content filter - exclude overlay windows from recording
|
|
72
120
|
SCContentFilter *filter = [[SCContentFilter alloc]
|
|
73
121
|
initWithDisplay:targetDisplay
|
|
74
122
|
excludingWindows:excludedWindows];
|
|
123
|
+
NSLog(@"🎯 Using window-level exclusion for overlay prevention");
|
|
75
124
|
|
|
76
125
|
// Create stream configuration
|
|
77
126
|
SCStreamConfiguration *streamConfig = [[SCStreamConfiguration alloc] init];
|
package/src/window_selector.mm
CHANGED
|
@@ -15,6 +15,23 @@ static NSButton *g_selectButton = nil;
|
|
|
15
15
|
static NSTimer *g_trackingTimer = nil;
|
|
16
16
|
static NSDictionary *g_selectedWindowInfo = nil;
|
|
17
17
|
static NSMutableArray *g_allWindows = nil;
|
|
18
|
+
|
|
19
|
+
// Functions to hide/show main overlay window during recording
|
|
20
|
+
void hideAllOverlayWindows() {
|
|
21
|
+
if (g_overlayWindow && [g_overlayWindow isVisible]) {
|
|
22
|
+
[g_overlayWindow setAlphaValue:0.0];
|
|
23
|
+
[g_overlayWindow orderOut:nil];
|
|
24
|
+
NSLog(@"🫥 Hidden main overlay window for recording");
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
void showAllOverlayWindows() {
|
|
29
|
+
if (g_overlayWindow) {
|
|
30
|
+
[g_overlayWindow setAlphaValue:1.0];
|
|
31
|
+
[g_overlayWindow orderFront:nil];
|
|
32
|
+
NSLog(@"👁️ Restored main overlay window after recording");
|
|
33
|
+
}
|
|
34
|
+
}
|
|
18
35
|
static NSDictionary *g_currentWindowUnderCursor = nil;
|
|
19
36
|
static bool g_bringToFrontEnabled = false; // Default disabled for overlay-only highlighting
|
|
20
37
|
static bool g_hasToggledWindow = false; // Track if any window is currently toggled
|