node-mac-recorder 2.22.8 → 2.22.10
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 +5 -3
- package/package.json +1 -1
- package/src/avfoundation_recorder.mm +4 -6
- package/src/cursor_tracker.mm +139 -27
- package/src/screen_capture_kit.mm +4 -12
package/index.js
CHANGED
|
@@ -1604,13 +1604,15 @@ class MacRecorder extends EventEmitter {
|
|
|
1604
1604
|
windowHeight: wh
|
|
1605
1605
|
};
|
|
1606
1606
|
|
|
1607
|
-
// If this is a click event, mark click location
|
|
1608
|
-
// Native eventType values: 'mousedown', 'mouseup', 'rightmousedown', 'rightmouseup'
|
|
1607
|
+
// If this is a click/drag event, mark click location
|
|
1608
|
+
// Native eventType values: 'mousedown', 'mouseup', 'drag', 'rightmousedown', 'rightmouseup', 'rightdrag'
|
|
1609
1609
|
const eventType = position.eventType || '';
|
|
1610
1610
|
if (eventType === 'mousedown' ||
|
|
1611
1611
|
eventType === 'mouseup' ||
|
|
1612
|
+
eventType === 'drag' ||
|
|
1612
1613
|
eventType === 'rightmousedown' ||
|
|
1613
|
-
eventType === 'rightmouseup'
|
|
1614
|
+
eventType === 'rightmouseup' ||
|
|
1615
|
+
eventType === 'rightdrag') {
|
|
1614
1616
|
location.click = windowInfo.windowId;
|
|
1615
1617
|
}
|
|
1616
1618
|
break; // Found the window, stop searching
|
package/package.json
CHANGED
|
@@ -132,11 +132,9 @@ extern "C" bool startAVFoundationRecording(const std::string& outputPath,
|
|
|
132
132
|
codecKey = AVVideoCodecH264;
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
bitrate = MAX(bitrate, 70 * 1000 * 1000); // Minimum 70 Mbps
|
|
139
|
-
bitrate = MIN(bitrate, 250 * 1000 * 1000); // Maximum 250 Mbps
|
|
135
|
+
NSInteger bitrate = (NSInteger)(recordingSize.width * recordingSize.height * 100);
|
|
136
|
+
bitrate = MAX(bitrate, 120 * 1000 * 1000);
|
|
137
|
+
bitrate = MIN(bitrate, 500 * 1000 * 1000);
|
|
140
138
|
|
|
141
139
|
NSLog(@"🎬 ULTRA QUALITY AVFoundation: %dx%d, bitrate=%.2fMbps",
|
|
142
140
|
(int)recordingSize.width, (int)recordingSize.height, bitrate / (1000.0 * 1000.0));
|
|
@@ -155,7 +153,7 @@ extern "C" bool startAVFoundationRecording(const std::string& outputPath,
|
|
|
155
153
|
AVVideoMaxKeyFrameIntervalKey: @((int)fps),
|
|
156
154
|
AVVideoAllowFrameReorderingKey: @YES,
|
|
157
155
|
AVVideoExpectedSourceFrameRateKey: @((int)fps),
|
|
158
|
-
AVVideoQualityKey: @(
|
|
156
|
+
AVVideoQualityKey: @(1.0),
|
|
159
157
|
AVVideoProfileLevelKey: AVVideoProfileLevelH264HighAutoLevel,
|
|
160
158
|
AVVideoH264EntropyModeKey: AVVideoH264EntropyModeCABAC
|
|
161
159
|
}
|
package/src/cursor_tracker.mm
CHANGED
|
@@ -360,10 +360,10 @@ static void InitializeCursorFingerprintMap(void) {
|
|
|
360
360
|
AddCursorIfAvailable(@selector(dragLinkCursor), @"alias");
|
|
361
361
|
AddCursorIfAvailable(@selector(resizeLeftRightCursor), @"col-resize");
|
|
362
362
|
AddCursorIfAvailable(@selector(resizeUpDownCursor), @"row-resize");
|
|
363
|
-
AddCursorIfAvailableByName(@"resizeLeftCursor", @"
|
|
364
|
-
AddCursorIfAvailableByName(@"resizeRightCursor", @"
|
|
365
|
-
AddCursorIfAvailableByName(@"resizeUpCursor", @"
|
|
366
|
-
AddCursorIfAvailableByName(@"resizeDownCursor", @"
|
|
363
|
+
AddCursorIfAvailableByName(@"resizeLeftCursor", @"col-resize");
|
|
364
|
+
AddCursorIfAvailableByName(@"resizeRightCursor", @"col-resize");
|
|
365
|
+
AddCursorIfAvailableByName(@"resizeUpCursor", @"ns-resize");
|
|
366
|
+
AddCursorIfAvailableByName(@"resizeDownCursor", @"ns-resize");
|
|
367
367
|
AddCursorIfAvailableByName(@"resizeNorthWestSouthEastCursor", @"nwse-resize");
|
|
368
368
|
AddCursorIfAvailableByName(@"resizeNorthEastSouthWestCursor", @"nesw-resize");
|
|
369
369
|
AddCursorIfAvailable(@selector(zoomInCursor), @"zoom-in");
|
|
@@ -994,10 +994,10 @@ static NSString* cursorTypeFromCursorName(NSString *value) {
|
|
|
994
994
|
return @"nwse-resize";
|
|
995
995
|
}
|
|
996
996
|
if (horizontal) {
|
|
997
|
-
return @"
|
|
997
|
+
return @"col-resize"; // Desktop SVG var: col-resize
|
|
998
998
|
}
|
|
999
999
|
if (vertical) {
|
|
1000
|
-
return @"ns-resize"; //
|
|
1000
|
+
return @"ns-resize"; // Desktop SVG var: ns-resize
|
|
1001
1001
|
}
|
|
1002
1002
|
|
|
1003
1003
|
// If contains "resize" but no specific direction, return generic resize
|
|
@@ -1332,34 +1332,35 @@ static NSString* cursorTypeFromSeed(int seed) {
|
|
|
1332
1332
|
}
|
|
1333
1333
|
}
|
|
1334
1334
|
switch(seed) {
|
|
1335
|
-
|
|
1336
|
-
case
|
|
1337
|
-
case
|
|
1335
|
+
// Desktop'ta SVG karşılığı olan tiplere normalize edilmiş seed map
|
|
1336
|
+
case 741324: return @"default"; // auto → default
|
|
1337
|
+
case 741336: return @"default"; // none → default (gizli cursor kayıtta default gösterilir)
|
|
1338
|
+
case 741338: return @"default"; // context-menu → default (SVG yok)
|
|
1338
1339
|
case 741339: return @"pointer";
|
|
1339
1340
|
case 741341: return @"progress";
|
|
1340
|
-
case 741343: return @"
|
|
1341
|
-
case 741345: return @"
|
|
1341
|
+
case 741343: return @"progress"; // wait → progress
|
|
1342
|
+
case 741345: return @"crosshair"; // cell → crosshair (en yakın SVG)
|
|
1342
1343
|
case 741347: return @"crosshair";
|
|
1343
1344
|
case 741357: return @"text";
|
|
1344
|
-
case 741359: return @"vertical-text
|
|
1345
|
+
case 741359: return @"text"; // vertical-text → text
|
|
1345
1346
|
case 741361: return @"alias";
|
|
1346
1347
|
case 741362: return @"copy";
|
|
1347
|
-
case 741364: return @"
|
|
1348
|
-
case 741368: return @"no-drop
|
|
1348
|
+
case 741364: return @"all-scroll"; // move → all-scroll
|
|
1349
|
+
case 741368: return @"not-allowed"; // no-drop → not-allowed
|
|
1349
1350
|
case 741370: return @"not-allowed";
|
|
1350
1351
|
case 741381: return @"grab";
|
|
1351
1352
|
case 741385: return @"grabbing";
|
|
1352
1353
|
case 741389: return @"col-resize";
|
|
1353
1354
|
case 741393: return @"row-resize";
|
|
1354
|
-
case 741397: return @"
|
|
1355
|
-
case 741398: return @"
|
|
1356
|
-
case 741409: return @"
|
|
1357
|
-
case 741413: return @"
|
|
1358
|
-
case 741417: return @"
|
|
1359
|
-
case 741418: return @"
|
|
1360
|
-
case 741420: return @"
|
|
1361
|
-
case 741424: return @"
|
|
1362
|
-
case 741426: return @"
|
|
1355
|
+
case 741397: return @"ns-resize"; // n-resize → ns-resize
|
|
1356
|
+
case 741398: return @"col-resize"; // e-resize → col-resize
|
|
1357
|
+
case 741409: return @"ns-resize"; // s-resize → ns-resize
|
|
1358
|
+
case 741413: return @"col-resize"; // w-resize → col-resize
|
|
1359
|
+
case 741417: return @"nesw-resize"; // ne-resize → nesw-resize
|
|
1360
|
+
case 741418: return @"nwse-resize"; // nw-resize → nwse-resize
|
|
1361
|
+
case 741420: return @"nwse-resize"; // se-resize → nwse-resize
|
|
1362
|
+
case 741424: return @"nesw-resize"; // sw-resize → nesw-resize
|
|
1363
|
+
case 741426: return @"col-resize"; // ew-resize → col-resize
|
|
1363
1364
|
case 741436: return @"ns-resize";
|
|
1364
1365
|
case 741438: return @"nesw-resize";
|
|
1365
1366
|
case 741442: return @"nwse-resize";
|
|
@@ -1787,6 +1788,71 @@ static NSString* detectSystemCursorType(void) {
|
|
|
1787
1788
|
return cursorType;
|
|
1788
1789
|
}
|
|
1789
1790
|
|
|
1791
|
+
// Desktop'ta SVG karşılığı olmayan cursor tiplerini desteklenen tiplere normalize et
|
|
1792
|
+
static NSString* normalizeCursorTypeForDesktop(NSString *cursorType) {
|
|
1793
|
+
if (!cursorType || [cursorType length] == 0) {
|
|
1794
|
+
return @"default";
|
|
1795
|
+
}
|
|
1796
|
+
|
|
1797
|
+
// Desteklenen tipler — desktop/public/cursor/default/ dizinindeki SVG'lere karşılık gelir
|
|
1798
|
+
static NSSet *supportedTypes = nil;
|
|
1799
|
+
static dispatch_once_t onceToken;
|
|
1800
|
+
dispatch_once(&onceToken, ^{
|
|
1801
|
+
supportedTypes = [[NSSet alloc] initWithArray:@[
|
|
1802
|
+
@"default", @"pointer", @"grabbing", @"text", @"grab",
|
|
1803
|
+
@"alias", @"copy", @"not-allowed", @"help", @"progress",
|
|
1804
|
+
@"crosshair", @"all-scroll", @"zoom-in", @"zoom-out",
|
|
1805
|
+
@"row-resize", @"col-resize", @"ns-resize",
|
|
1806
|
+
@"nwse-resize", @"nesw-resize"
|
|
1807
|
+
]];
|
|
1808
|
+
});
|
|
1809
|
+
|
|
1810
|
+
if ([supportedTypes containsObject:cursorType]) {
|
|
1811
|
+
return cursorType;
|
|
1812
|
+
}
|
|
1813
|
+
|
|
1814
|
+
// Normalize edilmemiş tipleri en yakın desteklenen tipe eşle
|
|
1815
|
+
if ([cursorType isEqualToString:@"auto"] || [cursorType isEqualToString:@"none"] ||
|
|
1816
|
+
[cursorType isEqualToString:@"context-menu"]) {
|
|
1817
|
+
return @"default";
|
|
1818
|
+
}
|
|
1819
|
+
if ([cursorType isEqualToString:@"wait"]) {
|
|
1820
|
+
return @"progress";
|
|
1821
|
+
}
|
|
1822
|
+
if ([cursorType isEqualToString:@"cell"]) {
|
|
1823
|
+
return @"crosshair";
|
|
1824
|
+
}
|
|
1825
|
+
if ([cursorType isEqualToString:@"vertical-text"]) {
|
|
1826
|
+
return @"text";
|
|
1827
|
+
}
|
|
1828
|
+
if ([cursorType isEqualToString:@"move"]) {
|
|
1829
|
+
return @"all-scroll";
|
|
1830
|
+
}
|
|
1831
|
+
if ([cursorType isEqualToString:@"no-drop"]) {
|
|
1832
|
+
return @"not-allowed";
|
|
1833
|
+
}
|
|
1834
|
+
// Yönlü resize → iki yönlü resize
|
|
1835
|
+
if ([cursorType isEqualToString:@"ew-resize"] ||
|
|
1836
|
+
[cursorType isEqualToString:@"e-resize"] ||
|
|
1837
|
+
[cursorType isEqualToString:@"w-resize"]) {
|
|
1838
|
+
return @"col-resize";
|
|
1839
|
+
}
|
|
1840
|
+
if ([cursorType isEqualToString:@"n-resize"] ||
|
|
1841
|
+
[cursorType isEqualToString:@"s-resize"]) {
|
|
1842
|
+
return @"ns-resize";
|
|
1843
|
+
}
|
|
1844
|
+
if ([cursorType isEqualToString:@"ne-resize"] ||
|
|
1845
|
+
[cursorType isEqualToString:@"sw-resize"]) {
|
|
1846
|
+
return @"nesw-resize";
|
|
1847
|
+
}
|
|
1848
|
+
if ([cursorType isEqualToString:@"nw-resize"] ||
|
|
1849
|
+
[cursorType isEqualToString:@"se-resize"]) {
|
|
1850
|
+
return @"nwse-resize";
|
|
1851
|
+
}
|
|
1852
|
+
|
|
1853
|
+
return @"default";
|
|
1854
|
+
}
|
|
1855
|
+
|
|
1790
1856
|
NSString* getCursorType() {
|
|
1791
1857
|
@autoreleasepool {
|
|
1792
1858
|
g_cursorTypeCounter++;
|
|
@@ -1823,7 +1889,10 @@ NSString* getCursorType() {
|
|
|
1823
1889
|
// Use cursorTypeFromNSCursor for detection (pointer equality + image-based)
|
|
1824
1890
|
// DO NOT use accessibility detection as it's unreliable and causes false positives
|
|
1825
1891
|
NSString *systemCursorType = detectSystemCursorType();
|
|
1826
|
-
NSString *
|
|
1892
|
+
NSString *rawType = systemCursorType && [systemCursorType length] > 0 ? systemCursorType : @"default";
|
|
1893
|
+
|
|
1894
|
+
// Desktop SVG'lerine uyumlu tipe normalize et
|
|
1895
|
+
NSString *finalType = normalizeCursorTypeForDesktop(rawType);
|
|
1827
1896
|
|
|
1828
1897
|
// Only log when cursor type changes
|
|
1829
1898
|
static NSString *lastLoggedType = nil;
|
|
@@ -1967,12 +2036,43 @@ void cursorTimerCallback() {
|
|
|
1967
2036
|
if (!cursorType) {
|
|
1968
2037
|
cursorType = @"default";
|
|
1969
2038
|
}
|
|
2039
|
+
|
|
2040
|
+
// Mouse button state polling — event tap olmadığında click/drag tespiti
|
|
2041
|
+
bool currentLeftMouseDown = CGEventSourceButtonState(kCGEventSourceStateHIDSystemState, kCGMouseButtonLeft);
|
|
2042
|
+
bool currentRightMouseDown = CGEventSourceButtonState(kCGEventSourceStateHIDSystemState, kCGMouseButtonRight);
|
|
2043
|
+
|
|
1970
2044
|
NSString *eventType = @"move";
|
|
1971
2045
|
|
|
2046
|
+
if (currentLeftMouseDown && !g_leftMouseDown) {
|
|
2047
|
+
eventType = @"mousedown";
|
|
2048
|
+
g_lastEventType = @"mousedown";
|
|
2049
|
+
} else if (!currentLeftMouseDown && g_leftMouseDown) {
|
|
2050
|
+
eventType = @"mouseup";
|
|
2051
|
+
g_lastEventType = @"mouseup";
|
|
2052
|
+
} else if (currentRightMouseDown && !g_rightMouseDown) {
|
|
2053
|
+
eventType = @"rightmousedown";
|
|
2054
|
+
g_lastEventType = @"rightmousedown";
|
|
2055
|
+
} else if (!currentRightMouseDown && g_rightMouseDown) {
|
|
2056
|
+
eventType = @"rightmouseup";
|
|
2057
|
+
g_lastEventType = @"rightmouseup";
|
|
2058
|
+
} else if (currentLeftMouseDown) {
|
|
2059
|
+
eventType = @"drag";
|
|
2060
|
+
g_lastEventType = @"drag";
|
|
2061
|
+
} else if (currentRightMouseDown) {
|
|
2062
|
+
eventType = @"rightdrag";
|
|
2063
|
+
g_lastEventType = @"rightdrag";
|
|
2064
|
+
} else {
|
|
2065
|
+
eventType = @"move";
|
|
2066
|
+
g_lastEventType = @"move";
|
|
2067
|
+
}
|
|
2068
|
+
|
|
2069
|
+
g_leftMouseDown = currentLeftMouseDown;
|
|
2070
|
+
g_rightMouseDown = currentRightMouseDown;
|
|
2071
|
+
|
|
1972
2072
|
if (!ShouldEmitCursorEvent(location, cursorType, eventType)) {
|
|
1973
2073
|
return;
|
|
1974
2074
|
}
|
|
1975
|
-
|
|
2075
|
+
|
|
1976
2076
|
// Cursor data oluştur
|
|
1977
2077
|
NSDictionary *cursorInfo = @{
|
|
1978
2078
|
@"x": @((int)location.x),
|
|
@@ -1982,7 +2082,7 @@ void cursorTimerCallback() {
|
|
|
1982
2082
|
@"cursorType": cursorType,
|
|
1983
2083
|
@"type": eventType
|
|
1984
2084
|
};
|
|
1985
|
-
|
|
2085
|
+
|
|
1986
2086
|
// Direkt dosyaya yaz
|
|
1987
2087
|
writeToFile(cursorInfo);
|
|
1988
2088
|
RememberCursorEvent(location, cursorType, eventType);
|
|
@@ -2237,20 +2337,32 @@ Napi::Value GetCursorPosition(const Napi::CallbackInfo& info) {
|
|
|
2237
2337
|
bool currentRightMouseDown = CGEventSourceButtonState(kCGEventSourceStateHIDSystemState, kCGMouseButtonRight);
|
|
2238
2338
|
|
|
2239
2339
|
NSString *eventType = @"move";
|
|
2240
|
-
|
|
2340
|
+
|
|
2241
2341
|
// Mouse button state değişikliklerini tespit et
|
|
2242
2342
|
if (currentLeftMouseDown && !g_leftMouseDown) {
|
|
2343
|
+
// Sol tuş basıldı (geçiş: up → down)
|
|
2243
2344
|
eventType = @"mousedown";
|
|
2244
2345
|
g_lastEventType = @"mousedown";
|
|
2245
2346
|
} else if (!currentLeftMouseDown && g_leftMouseDown) {
|
|
2347
|
+
// Sol tuş bırakıldı (geçiş: down → up)
|
|
2246
2348
|
eventType = @"mouseup";
|
|
2247
2349
|
g_lastEventType = @"mouseup";
|
|
2248
2350
|
} else if (currentRightMouseDown && !g_rightMouseDown) {
|
|
2351
|
+
// Sağ tuş basıldı
|
|
2249
2352
|
eventType = @"rightmousedown";
|
|
2250
2353
|
g_lastEventType = @"rightmousedown";
|
|
2251
2354
|
} else if (!currentRightMouseDown && g_rightMouseDown) {
|
|
2355
|
+
// Sağ tuş bırakıldı
|
|
2252
2356
|
eventType = @"rightmouseup";
|
|
2253
2357
|
g_lastEventType = @"rightmouseup";
|
|
2358
|
+
} else if (currentLeftMouseDown) {
|
|
2359
|
+
// Sol tuş basılı tutuluyor — sürükleme
|
|
2360
|
+
eventType = @"drag";
|
|
2361
|
+
g_lastEventType = @"drag";
|
|
2362
|
+
} else if (currentRightMouseDown) {
|
|
2363
|
+
// Sağ tuş basılı tutuluyor
|
|
2364
|
+
eventType = @"rightdrag";
|
|
2365
|
+
g_lastEventType = @"rightdrag";
|
|
2254
2366
|
} else {
|
|
2255
2367
|
eventType = @"move";
|
|
2256
2368
|
g_lastEventType = @"move";
|
|
@@ -214,9 +214,9 @@ static void SCKQualityBitrateForDimensions(NSString *preset,
|
|
|
214
214
|
minBitrate = 18 * 1000 * 1000;
|
|
215
215
|
maxBitrate = 80 * 1000 * 1000;
|
|
216
216
|
} else { // high/default - ULTRA quality
|
|
217
|
-
multiplier =
|
|
218
|
-
minBitrate =
|
|
219
|
-
maxBitrate =
|
|
217
|
+
multiplier = 120;
|
|
218
|
+
minBitrate = 120 * 1000 * 1000;
|
|
219
|
+
maxBitrate = 600 * 1000 * 1000;
|
|
220
220
|
}
|
|
221
221
|
|
|
222
222
|
double base = ((double)MAX(1, width)) * ((double)MAX(1, height)) * (double)multiplier;
|
|
@@ -783,7 +783,7 @@ extern "C" NSString *ScreenCaptureKitCurrentAudioPath(void) {
|
|
|
783
783
|
NSString *normalizedQuality = SCKNormalizeQualityPreset(g_qualityPreset);
|
|
784
784
|
SCKQualityBitrateForDimensions(normalizedQuality, width, height, &bitrate, &bitrateMultiplier, &minBitrate, &maxBitrate);
|
|
785
785
|
|
|
786
|
-
NSNumber *qualityHint = @0
|
|
786
|
+
NSNumber *qualityHint = @1.0;
|
|
787
787
|
if ([normalizedQuality isEqualToString:@"medium"]) {
|
|
788
788
|
qualityHint = @0.9;
|
|
789
789
|
} else if ([normalizedQuality isEqualToString:@"low"]) {
|
|
@@ -1349,14 +1349,6 @@ static void SCKPerformRecordingSetup(NSDictionary *config, SCShareableContent *c
|
|
|
1349
1349
|
g_targetFPS = 60;
|
|
1350
1350
|
}
|
|
1351
1351
|
|
|
1352
|
-
// CRITICAL ELECTRON FIX: Lower FPS to 30 when recording with camera
|
|
1353
|
-
// This prevents resource conflicts and crashes when running both simultaneously
|
|
1354
|
-
BOOL isCameraEnabled = captureCamera && [captureCamera boolValue];
|
|
1355
|
-
if (isCameraEnabled && g_targetFPS > 30) {
|
|
1356
|
-
MRLog(@"📹 Camera recording detected - lowering ScreenCaptureKit FPS from %ld to 30 for stability", (long)g_targetFPS);
|
|
1357
|
-
g_targetFPS = 30;
|
|
1358
|
-
}
|
|
1359
|
-
|
|
1360
1352
|
MRLog(@"🎬 Starting PURE ScreenCaptureKit recording (NO AVFoundation)");
|
|
1361
1353
|
MRLog(@"🔧 Config: cursor=%@ mic=%@ system=%@ display=%@ window=%@ crop=%@",
|
|
1362
1354
|
captureCursor, includeMicrophone, includeSystemAudio, displayId, windowId, captureRect);
|