node-mac-recorder 2.19.4 → 2.20.0

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.
@@ -1,8 +1,15 @@
1
1
  {
2
2
  "permissions": {
3
3
  "allow": [
4
- "Bash(node-gyp:*)",
5
- "Bash(node:*)"
4
+ "Bash(npm test)",
5
+ "Bash(node:*)",
6
+ "Bash(npm install)",
7
+ "Bash(npm run clean:*)",
8
+ "Bash(npm run build:*)",
9
+ "Bash(npm run rebuild:*)",
10
+ "Bash(system_profiler:*)",
11
+ "Bash(sw_vers)",
12
+ "Bash(find:*)"
6
13
  ],
7
14
  "deny": [],
8
15
  "ask": []
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # node-mac-recorder
2
2
 
3
+ *This package was developed for [https://creavit.studio](https://creavit.studio)*
4
+
3
5
  A powerful native macOS screen recording Node.js package with advanced window selection, multi-display support, and automatic overlay window exclusion. Built with ScreenCaptureKit for modern macOS with intelligent window filtering and Electron compatibility.
4
6
 
5
7
  ## Features
package/binding.gyp CHANGED
@@ -30,6 +30,7 @@
30
30
  "libraries": [
31
31
  "-framework Foundation",
32
32
  "-framework AppKit",
33
+ "-framework ScreenCaptureKit",
33
34
  "-framework AVFoundation",
34
35
  "-framework CoreMedia",
35
36
  "-framework CoreVideo",
@@ -40,15 +41,6 @@
40
41
  "-framework CoreAudio"
41
42
  ]
42
43
  },
43
- "conditions": [
44
- ['OS=="mac"', {
45
- "link_settings": {
46
- "libraries": [
47
- "-weak_framework ScreenCaptureKit"
48
- ]
49
- }
50
- }]
51
- ],
52
44
  "defines": [ "NAPI_DISABLE_CPP_EXCEPTIONS" ]
53
45
  }
54
46
  ]
package/index.js CHANGED
@@ -87,7 +87,11 @@ class MacRecorder extends EventEmitter {
87
87
  x: display.x,
88
88
  y: display.y,
89
89
  isPrimary: display.isPrimary,
90
+ scaleFactor: display.scaleFactor || 1.0,
91
+ physicalWidth: display.physicalWidth || display.width,
92
+ physicalHeight: display.physicalHeight || display.height,
90
93
  resolution: `${display.width}x${display.height}`,
94
+ physicalResolution: `${display.physicalWidth || display.width}x${display.physicalHeight || display.height}`,
91
95
  }));
92
96
  }
93
97
 
@@ -771,24 +775,31 @@ class MacRecorder extends EventEmitter {
771
775
  // JavaScript interval ile polling yap (daha sık - mouse event'leri yakalamak için)
772
776
  this.cursorCaptureInterval = setInterval(() => {
773
777
  try {
774
- const position = nativeBinding.getCursorPosition();
778
+ // Get cursor position with recording coordinates
779
+ const cursorData = this.getCursorPosition();
775
780
  const timestamp = Date.now() - this.cursorCaptureStartTime;
776
781
 
777
- // Video-relative coordinate transformation for all recording types
778
- let x = position.x;
779
- let y = position.y;
780
- let coordinateSystem = "global";
782
+ // Use recording coordinates for video overlay alignment
783
+ let x = cursorData.recordingX || cursorData.x;
784
+ let y = cursorData.recordingY || cursorData.y;
785
+ let coordinateSystem = "recording-coordinates";
781
786
 
782
- // Apply video-relative transformation for all recording types
787
+ // Apply video-relative transformation for window/area recording
783
788
  if (this.cursorDisplayInfo && this.cursorDisplayInfo.videoRelative) {
784
- // Step 1: Transform global → display-relative coordinates
785
- const displayRelativeX = position.x - this.cursorDisplayInfo.displayX;
786
- const displayRelativeY = position.y - this.cursorDisplayInfo.displayY;
787
-
788
- // Step 2: Transform display-relative → video-relative coordinates
789
- x = displayRelativeX - this.cursorDisplayInfo.videoOffsetX;
790
- y = displayRelativeY - this.cursorDisplayInfo.videoOffsetY;
791
- coordinateSystem = "video-relative";
789
+ // For window/area recording: adjust coordinates relative to capture area
790
+ if (this.cursorDisplayInfo.videoOffsetX || this.cursorDisplayInfo.videoOffsetY) {
791
+ // Convert from full recording resolution to capture area coordinates
792
+ const recordingScaleX = cursorData.scaleX || 1;
793
+ const recordingScaleY = cursorData.scaleY || 1;
794
+
795
+ // Scale offsets to recording resolution
796
+ const scaledOffsetX = this.cursorDisplayInfo.videoOffsetX * recordingScaleX;
797
+ const scaledOffsetY = this.cursorDisplayInfo.videoOffsetY * recordingScaleY;
798
+
799
+ x = x - scaledOffsetX;
800
+ y = y - scaledOffsetY;
801
+ coordinateSystem = "video-relative";
802
+ }
792
803
 
793
804
  // Bounds check for video area (don't skip, just note if outside)
794
805
  const outsideVideo = x < 0 || y < 0 ||
@@ -801,16 +812,30 @@ class MacRecorder extends EventEmitter {
801
812
  }
802
813
  }
803
814
 
804
- const cursorData = {
815
+ const captureData = {
805
816
  x: x,
806
817
  y: y,
807
818
  timestamp: timestamp,
808
819
  unixTimeMs: Date.now(),
809
- cursorType: position.cursorType,
810
- type: position.eventType || "move",
820
+ cursorType: cursorData.cursorType,
821
+ type: cursorData.eventType || "move",
811
822
  coordinateSystem: coordinateSystem,
812
- // Video-relative metadata for all recording types
823
+ // Recording metadata for video overlay alignment
813
824
  recordingType: this.cursorDisplayInfo?.recordingType || "display",
825
+ recordingInfo: {
826
+ // Logical coordinates for backward compatibility
827
+ logicalX: cursorData.x,
828
+ logicalY: cursorData.y,
829
+ // Recording coordinates for video overlay
830
+ recordingX: x,
831
+ recordingY: y,
832
+ // Recording resolution
833
+ recordingWidth: cursorData.recordingWidth,
834
+ recordingHeight: cursorData.recordingHeight,
835
+ // Scale factors
836
+ scaleX: cursorData.scaleX || 1,
837
+ scaleY: cursorData.scaleY || 1
838
+ },
814
839
  videoInfo: this.cursorDisplayInfo ? {
815
840
  width: this.cursorDisplayInfo.videoWidth,
816
841
  height: this.cursorDisplayInfo.videoHeight,
@@ -825,9 +850,9 @@ class MacRecorder extends EventEmitter {
825
850
  };
826
851
 
827
852
  // Sadece eventType değiştiğinde veya pozisyon değiştiğinde kaydet
828
- if (this.shouldCaptureEvent(cursorData)) {
853
+ if (this.shouldCaptureEvent(captureData)) {
829
854
  // Dosyaya ekle
830
- const jsonString = JSON.stringify(cursorData);
855
+ const jsonString = JSON.stringify(captureData);
831
856
 
832
857
  if (this.cursorCaptureFirstWrite) {
833
858
  fs.appendFileSync(filepath, jsonString);
@@ -837,7 +862,7 @@ class MacRecorder extends EventEmitter {
837
862
  }
838
863
 
839
864
  // Son pozisyonu sakla
840
- this.lastCapturedData = { ...cursorData };
865
+ this.lastCapturedData = { ...captureData };
841
866
  }
842
867
  } catch (error) {
843
868
  console.error("Cursor capture error:", error);
@@ -896,12 +921,123 @@ class MacRecorder extends EventEmitter {
896
921
  const position = nativeBinding.getCursorPosition();
897
922
 
898
923
  // Cursor hangi display'de ise o display'e relative döndür
899
- return this.getDisplayRelativePositionSync(position);
924
+ const displayRelativePosition = this.getDisplayRelativePositionSync(position);
925
+
926
+ // Add recording coordinates for video overlay alignment
927
+ const recordingCoordinates = this.calculateRecordingCoordinates(displayRelativePosition);
928
+
929
+ return {
930
+ ...displayRelativePosition,
931
+ // Recording coordinates for video overlay (matches actual video resolution)
932
+ recordingX: recordingCoordinates.x,
933
+ recordingY: recordingCoordinates.y,
934
+ recordingWidth: recordingCoordinates.width,
935
+ recordingHeight: recordingCoordinates.height,
936
+ // Scale factors for reference
937
+ scaleX: recordingCoordinates.scaleX,
938
+ scaleY: recordingCoordinates.scaleY,
939
+ // Original coordinates for backward compatibility
940
+ x: displayRelativePosition.x,
941
+ y: displayRelativePosition.y
942
+ };
900
943
  } catch (error) {
901
944
  throw new Error("Failed to get cursor position: " + error.message);
902
945
  }
903
946
  }
904
947
 
948
+ /**
949
+ * Calculate cursor coordinates for recording video overlay alignment
950
+ * Converts logical display coordinates to actual video recording coordinates
951
+ */
952
+ calculateRecordingCoordinates(displayPosition) {
953
+ try {
954
+ // Get current display information
955
+ if (!this.cachedDisplays) {
956
+ this.refreshDisplayCache();
957
+ }
958
+
959
+ // If still null, use native binding directly
960
+ if (!this.cachedDisplays) {
961
+ try {
962
+ const nativeDisplays = nativeBinding.getDisplays();
963
+ this.cachedDisplays = nativeDisplays.map((display, index) => ({
964
+ id: display.id,
965
+ name: display.name,
966
+ width: display.width,
967
+ height: display.height,
968
+ x: display.x,
969
+ y: display.y,
970
+ isPrimary: display.isPrimary,
971
+ scaleFactor: display.scaleFactor || 1.0,
972
+ physicalWidth: display.physicalWidth || display.width,
973
+ physicalHeight: display.physicalHeight || display.height,
974
+ resolution: `${display.width}x${display.height}`,
975
+ }));
976
+ } catch (e) {
977
+ console.warn('Failed to get displays from native binding:', e.message);
978
+ }
979
+ }
980
+
981
+ // Find the target display
982
+ let targetDisplay = null;
983
+ if (displayPosition.displayId) {
984
+ targetDisplay = this.cachedDisplays.find(d => d.id === displayPosition.displayId);
985
+ }
986
+ if (!targetDisplay) {
987
+ targetDisplay = this.cachedDisplays.find(d => d.isPrimary) || this.cachedDisplays[0];
988
+ }
989
+
990
+ if (!targetDisplay) {
991
+ // Fallback: assume 1:1 mapping
992
+ return {
993
+ x: displayPosition.x,
994
+ y: displayPosition.y,
995
+ width: displayPosition.x,
996
+ height: displayPosition.y
997
+ };
998
+ }
999
+
1000
+ // Parse display dimensions
1001
+ const logicalWidth = parseInt(targetDisplay.resolution.split("x")[0]);
1002
+ const logicalHeight = parseInt(targetDisplay.resolution.split("x")[1]);
1003
+
1004
+ // Get physical recording dimensions
1005
+ // On Retina displays, recording is done at physical resolution (2x logical)
1006
+ const physicalWidth = targetDisplay.physicalWidth || (logicalWidth * (targetDisplay.scaleFactor || 1));
1007
+ const physicalHeight = targetDisplay.physicalHeight || (logicalHeight * (targetDisplay.scaleFactor || 1));
1008
+
1009
+ // Calculate scale factors for coordinate conversion
1010
+ const scaleX = physicalWidth / logicalWidth;
1011
+ const scaleY = physicalHeight / logicalHeight;
1012
+
1013
+ // Convert logical cursor coordinates to recording coordinates
1014
+ const recordingX = Math.round(displayPosition.x * scaleX);
1015
+ const recordingY = Math.round(displayPosition.y * scaleY);
1016
+
1017
+ return {
1018
+ x: recordingX,
1019
+ y: recordingY,
1020
+ width: physicalWidth,
1021
+ height: physicalHeight,
1022
+ scaleX: scaleX,
1023
+ scaleY: scaleY,
1024
+ logicalWidth: logicalWidth,
1025
+ logicalHeight: logicalHeight
1026
+ };
1027
+ } catch (error) {
1028
+ console.warn('Failed to calculate recording coordinates:', error.message);
1029
+ // Fallback: 1:1 mapping
1030
+ return {
1031
+ x: displayPosition.x,
1032
+ y: displayPosition.y,
1033
+ width: displayPosition.x,
1034
+ height: displayPosition.y,
1035
+ scaleX: 1,
1036
+ scaleY: 1
1037
+ };
1038
+ }
1039
+ }
1040
+
905
1041
  /**
906
1042
  * Global koordinatları en uygun display'e relative çevirir (sync version)
907
1043
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-mac-recorder",
3
- "version": "2.19.4",
3
+ "version": "2.20.0",
4
4
  "description": "Native macOS screen recording package for Node.js applications",
5
5
  "main": "index.js",
6
6
  "keywords": [
@@ -58,7 +58,25 @@ NSString* getCursorType() {
58
58
  // ACCESSIBILITY API BASED CURSOR DETECTION
59
59
  // Determine cursor type based on the UI element under the cursor
60
60
 
61
- CGPoint cursorPos = CGEventGetLocation(CGEventCreate(NULL));
61
+ // MACOS 14 FIX: Get cursor position for accessibility API
62
+ CGPoint cursorPos;
63
+
64
+ if (@available(macOS 10.15, *)) {
65
+ // Use NSEvent method for macOS 14 compatibility
66
+ NSPoint mouseLocationInWindow = [NSEvent mouseLocation];
67
+
68
+ // Convert NSEvent bottom-left coordinates to top-left
69
+ CGDirectDisplayID primaryDisplay = CGMainDisplayID();
70
+ CGRect primaryBounds = CGDisplayBounds(primaryDisplay);
71
+
72
+ cursorPos = CGPointMake(
73
+ mouseLocationInWindow.x,
74
+ primaryBounds.size.height - mouseLocationInWindow.y
75
+ );
76
+ } else {
77
+ // Fallback for older macOS versions
78
+ cursorPos = CGEventGetLocation(CGEventCreate(NULL));
79
+ }
62
80
  AXUIElementRef systemWide = AXUIElementCreateSystemWide();
63
81
  AXUIElementRef elementAtPosition = NULL;
64
82
  AXError error = AXUIElementCopyElementAtPosition(systemWide, cursorPos.x, cursorPos.y, &elementAtPosition);
@@ -345,7 +363,31 @@ CGEventRef eventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef eve
345
363
  return event;
346
364
  }
347
365
 
348
- CGPoint rawLocation = CGEventGetLocation(event);
366
+ // MACOS 14 FIX: Use alternative coordinate method if available
367
+ CGPoint rawLocation;
368
+
369
+ if (@available(macOS 10.15, *)) {
370
+ // For event callbacks, we can still use CGEventGetLocation
371
+ // but we'll apply coordinate correction if needed
372
+ rawLocation = CGEventGetLocation(event);
373
+
374
+ // Additional validation for macOS 14 scaled displays
375
+ if (rawLocation.x > 5000 || rawLocation.y > 5000) {
376
+ // Coordinates seem unreasonable, try NSEvent method as backup
377
+ NSPoint mouseLocationInWindow = [NSEvent mouseLocation];
378
+
379
+ // Convert NSEvent bottom-left coordinates to top-left
380
+ CGDirectDisplayID primaryDisplay = CGMainDisplayID();
381
+ CGRect primaryBounds = CGDisplayBounds(primaryDisplay);
382
+
383
+ rawLocation = CGPointMake(
384
+ mouseLocationInWindow.x,
385
+ primaryBounds.size.height - mouseLocationInWindow.y
386
+ );
387
+ }
388
+ } else {
389
+ rawLocation = CGEventGetLocation(event);
390
+ }
349
391
 
350
392
  // Apply DPR scaling correction for Retina displays
351
393
  NSDictionary *scalingInfo = getDisplayScalingInfo(rawLocation);
@@ -413,11 +455,28 @@ void cursorTimerCallback() {
413
455
  return;
414
456
  }
415
457
 
416
- // Get cursor position with DPR scaling correction
417
- CGEventRef event = CGEventCreate(NULL);
418
- CGPoint rawLocation = CGEventGetLocation(event);
419
- if (event) {
420
- CFRelease(event);
458
+ // MACOS 14 FIX: Get cursor position with DPR scaling correction
459
+ CGPoint rawLocation;
460
+
461
+ if (@available(macOS 10.15, *)) {
462
+ // Use NSEvent method for better macOS 14 compatibility
463
+ NSPoint mouseLocationInWindow = [NSEvent mouseLocation];
464
+
465
+ // Convert NSEvent bottom-left coordinates to top-left
466
+ CGDirectDisplayID primaryDisplay = CGMainDisplayID();
467
+ CGRect primaryBounds = CGDisplayBounds(primaryDisplay);
468
+
469
+ rawLocation = CGPointMake(
470
+ mouseLocationInWindow.x,
471
+ primaryBounds.size.height - mouseLocationInWindow.y
472
+ );
473
+ } else {
474
+ // Fallback for older macOS versions
475
+ CGEventRef event = CGEventCreate(NULL);
476
+ rawLocation = CGEventGetLocation(event);
477
+ if (event) {
478
+ CFRelease(event);
479
+ }
421
480
  }
422
481
 
423
482
  // Apply DPR scaling correction for Retina displays
@@ -750,23 +809,48 @@ Napi::Value GetCursorPosition(const Napi::CallbackInfo& info) {
750
809
  Napi::Env env = info.Env();
751
810
 
752
811
  @try {
753
- // Get raw cursor position (may be scaled on Retina displays)
754
- CGEventRef event = CGEventCreate(NULL);
755
- CGPoint rawLocation = CGEventGetLocation(event);
756
- if (event) {
757
- CFRelease(event);
812
+ // MACOS 14 FIX: Use NSEvent mouseLocation instead of CGEventGetLocation
813
+ // CGEventGetLocation has coordinate system issues on macOS 14 with scaled displays
814
+ CGPoint rawLocation;
815
+
816
+ if (@available(macOS 10.15, *)) {
817
+ // Try NSEvent method first (more reliable on macOS 14)
818
+ NSPoint mouseLocationInWindow = [NSEvent mouseLocation];
819
+
820
+ // NSEvent.mouseLocation returns coordinates in the global coordinate space
821
+ // where (0,0) is at bottom-left of PRIMARY SCREEN
822
+ // CGDisplayBounds uses (0,0) at top-left
823
+ // We need to convert Y coordinate from bottom-left to top-left
824
+
825
+ CGDirectDisplayID primaryDisplay = CGMainDisplayID();
826
+ CGRect primaryBounds = CGDisplayBounds(primaryDisplay);
827
+
828
+ rawLocation = CGPointMake(
829
+ mouseLocationInWindow.x,
830
+ primaryBounds.size.height - mouseLocationInWindow.y
831
+ );
832
+
833
+ NSLog(@"🔧 macOS 14 cursor fix: NSEvent(%.0f,%.0f) -> CGPoint(%.0f,%.0f)",
834
+ mouseLocationInWindow.x, mouseLocationInWindow.y,
835
+ rawLocation.x, rawLocation.y);
836
+ } else {
837
+ // Fallback for older macOS versions
838
+ CGEventRef event = CGEventCreate(NULL);
839
+ rawLocation = CGEventGetLocation(event);
840
+ if (event) {
841
+ CFRelease(event);
842
+ }
758
843
  }
759
-
844
+
760
845
  // Get display scaling information
761
846
  NSDictionary *scalingInfo = getDisplayScalingInfo(rawLocation);
762
847
  CGPoint logicalLocation = rawLocation;
763
-
848
+
764
849
  if (scalingInfo) {
765
850
  CGFloat scaleFactor = [[scalingInfo objectForKey:@"scaleFactor"] doubleValue];
766
851
  NSRect displayBounds = [[scalingInfo objectForKey:@"displayBounds"] rectValue];
767
-
768
- // CGEventGetLocation returns LOGICAL coordinates (correct for JS layer)
769
- // Keep logical coordinates - transformation happens in JS layer
852
+
853
+ // Use the corrected coordinates
770
854
  logicalLocation = rawLocation;
771
855
  }
772
856
 
@@ -577,15 +577,60 @@ Napi::Value GetDisplays(const Napi::CallbackInfo& info) {
577
577
  CGDirectDisplayID displayID = activeDisplays[i];
578
578
  CGRect displayBounds = CGDisplayBounds(displayID);
579
579
  bool isPrimary = (displayID == CGMainDisplayID());
580
-
580
+
581
+ // Calculate scale factor and physical dimensions
582
+ CGSize logicalSize = displayBounds.size;
583
+ CGFloat scaleFactor = 1.0;
584
+ CGSize physicalSize = logicalSize;
585
+ CGSize actualPhysicalSize = logicalSize;
586
+
587
+ // Get actual display mode info first (most accurate)
588
+ CGDisplayModeRef mode = CGDisplayCopyDisplayMode(displayID);
589
+ if (mode) {
590
+ size_t modePixelWidth = CGDisplayModeGetPixelWidth(mode);
591
+ size_t modePixelHeight = CGDisplayModeGetPixelHeight(mode);
592
+ actualPhysicalSize = CGSizeMake(modePixelWidth, modePixelHeight);
593
+ CGDisplayModeRelease(mode);
594
+ }
595
+
596
+ // Use NSScreen for backing scale factor (this is the UI scaling)
597
+ NSArray *screens = [NSScreen screens];
598
+ for (NSScreen *screen in screens) {
599
+ NSDictionary *screenDescription = [screen deviceDescription];
600
+ NSNumber *screenNumber = [screenDescription objectForKey:@"NSScreenNumber"];
601
+ if (screenNumber && [screenNumber unsignedIntValue] == displayID) {
602
+ scaleFactor = [screen backingScaleFactor];
603
+
604
+ // For cursor tracking, we want to use the logical coordinates that macOS reports
605
+ // Physical size should be what the system actually uses for display output
606
+ physicalSize = actualPhysicalSize;
607
+
608
+ NSLog(@"🔍 Display %u scale analysis:", displayID);
609
+ NSLog(@" Logical bounds: %.0fx%.0f", logicalSize.width, logicalSize.height);
610
+ NSLog(@" NSScreen backing scale: %.1fx", scaleFactor);
611
+ NSLog(@" CGDisplayMode pixels: %.0fx%.0f", actualPhysicalSize.width, actualPhysicalSize.height);
612
+ NSLog(@" Calculated scale: %.2fx", actualPhysicalSize.width / logicalSize.width);
613
+ break;
614
+ }
615
+ }
616
+
617
+ // Fallback if NSScreen method didn't work
618
+ if (scaleFactor == 1.0 && actualPhysicalSize.width > logicalSize.width) {
619
+ scaleFactor = actualPhysicalSize.width / logicalSize.width;
620
+ physicalSize = actualPhysicalSize;
621
+ }
622
+
581
623
  NSDictionary *displayInfo = @{
582
624
  @"id": @(displayID), // Direct CGDirectDisplayID
583
625
  @"name": [NSString stringWithFormat:@"Display %u", (unsigned int)(i + 1)],
584
- @"width": @((int)displayBounds.size.width),
585
- @"height": @((int)displayBounds.size.height),
626
+ @"width": @((int)logicalSize.width),
627
+ @"height": @((int)logicalSize.height),
586
628
  @"x": @((int)displayBounds.origin.x),
587
629
  @"y": @((int)displayBounds.origin.y),
588
- @"isPrimary": @(isPrimary)
630
+ @"isPrimary": @(isPrimary),
631
+ @"scaleFactor": @(scaleFactor),
632
+ @"physicalWidth": @((int)physicalSize.width),
633
+ @"physicalHeight": @((int)physicalSize.height)
589
634
  };
590
635
  [displays addObject:displayInfo];
591
636
  }
@@ -595,13 +640,16 @@ Napi::Value GetDisplays(const Napi::CallbackInfo& info) {
595
640
 
596
641
  for (NSUInteger i = 0; i < displays.count; i++) {
597
642
  NSDictionary *display = displays[i];
598
- NSLog(@"Display %lu: ID=%u, Name=%@, Size=%@x%@",
643
+ NSLog(@"Display %lu: ID=%u, Name=%@, Logical=%@x%@, Physical=%@x%@, Scale=%.1fx",
599
644
  (unsigned long)i,
600
645
  [display[@"id"] unsignedIntValue],
601
646
  display[@"name"],
602
647
  display[@"width"],
603
- display[@"height"]);
604
-
648
+ display[@"height"],
649
+ display[@"physicalWidth"],
650
+ display[@"physicalHeight"],
651
+ [display[@"scaleFactor"] doubleValue]);
652
+
605
653
  Napi::Object displayObj = Napi::Object::New(env);
606
654
  displayObj.Set("id", Napi::Number::New(env, [display[@"id"] unsignedIntValue]));
607
655
  displayObj.Set("name", Napi::String::New(env, [display[@"name"] UTF8String]));
@@ -610,6 +658,9 @@ Napi::Value GetDisplays(const Napi::CallbackInfo& info) {
610
658
  displayObj.Set("x", Napi::Number::New(env, [display[@"x"] doubleValue]));
611
659
  displayObj.Set("y", Napi::Number::New(env, [display[@"y"] doubleValue]));
612
660
  displayObj.Set("isPrimary", Napi::Boolean::New(env, [display[@"isPrimary"] boolValue]));
661
+ displayObj.Set("scaleFactor", Napi::Number::New(env, [display[@"scaleFactor"] doubleValue]));
662
+ displayObj.Set("physicalWidth", Napi::Number::New(env, [display[@"physicalWidth"] doubleValue]));
663
+ displayObj.Set("physicalHeight", Napi::Number::New(env, [display[@"physicalHeight"] doubleValue]));
613
664
  result[i] = displayObj;
614
665
  }
615
666
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  // Pure ScreenCaptureKit implementation - NO AVFoundation
4
4
  static SCStream * API_AVAILABLE(macos(12.3)) g_stream = nil;
5
- static SCRecordingOutput * API_AVAILABLE(macos(15.0)) g_recordingOutput = nil;
5
+ static id g_recordingOutput API_AVAILABLE(macos(15.0)) = nil;
6
6
  static id<SCStreamDelegate> API_AVAILABLE(macos(12.3)) g_streamDelegate = nil;
7
7
  static BOOL g_isRecording = NO;
8
8
  static BOOL g_isCleaningUp = NO; // Prevent recursive cleanup
@@ -42,7 +42,8 @@ static NSString *g_outputPath = nil;
42
42
 
43
43
  + (BOOL)isScreenCaptureKitAvailable {
44
44
  if (@available(macOS 15.0, *)) {
45
- return [SCShareableContent class] != nil && [SCStream class] != nil && [SCRecordingOutput class] != nil;
45
+ Class recordingOutputClass = NSClassFromString(@"SCRecordingOutput");
46
+ return [SCShareableContent class] != nil && [SCStream class] != nil && recordingOutputClass != nil;
46
47
  }
47
48
  return NO;
48
49
  }
@@ -275,17 +276,23 @@ static NSString *g_outputPath = nil;
275
276
  }
276
277
 
277
278
  if (@available(macOS 15.0, *)) {
278
- // Create recording output configuration
279
- SCRecordingOutputConfiguration *recordingConfig = [[SCRecordingOutputConfiguration alloc] init];
280
- recordingConfig.outputURL = outputURL;
281
- recordingConfig.videoCodecType = AVVideoCodecTypeH264;
282
-
283
- // Audio configuration - using available properties
284
- // Note: Specific audio routing handled by ScreenCaptureKit automatically
285
-
286
- // Create recording output with correct initializer
287
- g_recordingOutput = [[SCRecordingOutput alloc] initWithConfiguration:recordingConfig
288
- delegate:nil];
279
+ // Create recording output configuration using runtime class lookup
280
+ Class recordingConfigClass = NSClassFromString(@"SCRecordingOutputConfiguration");
281
+ Class recordingOutputClass = NSClassFromString(@"SCRecordingOutput");
282
+
283
+ if (recordingConfigClass && recordingOutputClass) {
284
+ id recordingConfig = [[recordingConfigClass alloc] init];
285
+ [recordingConfig setValue:outputURL forKey:@"outputURL"];
286
+ // Use string constant for video codec
287
+ [recordingConfig setValue:@"avc1" forKey:@"videoCodecType"];
288
+
289
+ // Audio configuration - using available properties
290
+ // Note: Specific audio routing handled by ScreenCaptureKit automatically
291
+
292
+ // Create recording output with correct initializer
293
+ g_recordingOutput = [[recordingOutputClass alloc] initWithConfiguration:recordingConfig
294
+ delegate:nil];
295
+ }
289
296
  if (shouldCaptureMic && shouldCaptureSystemAudio) {
290
297
  NSLog(@"🔧 Created SCRecordingOutput with microphone and system audio");
291
298
  } else if (shouldCaptureMic) {
@@ -320,7 +327,9 @@ static NSString *g_outputPath = nil;
320
327
  BOOL outputAdded = NO;
321
328
 
322
329
  if (@available(macOS 15.0, *)) {
323
- outputAdded = [g_stream addRecordingOutput:g_recordingOutput error:&outputError];
330
+ if (g_recordingOutput && [g_stream respondsToSelector:@selector(addRecordingOutput:error:)]) {
331
+ outputAdded = [g_stream addRecordingOutput:g_recordingOutput error:&outputError];
332
+ }
324
333
  }
325
334
 
326
335
  if (!outputAdded || outputError) {
@@ -0,0 +1 @@
1
+ [{"x":661,"y":888,"timestamp":75,"unixTimeMs":1758662417142,"cursorType":"text","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}}]
@@ -0,0 +1 @@
1
+ [{"x":661,"y":888,"timestamp":28,"unixTimeMs":1758662420161,"cursorType":"text","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":691,"y":857,"timestamp":468,"unixTimeMs":1758662420601,"cursorType":"text","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":796,"y":749,"timestamp":490,"unixTimeMs":1758662420623,"cursorType":"text","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":971,"y":567,"timestamp":515,"unixTimeMs":1758662420648,"cursorType":"text","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1262,"y":304,"timestamp":525,"unixTimeMs":1758662420658,"cursorType":"text","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1519,"y":95,"timestamp":583,"unixTimeMs":1758662420716,"cursorType":"pointer","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1709,"y":0,"timestamp":621,"unixTimeMs":1758662420754,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1707,"y":0,"timestamp":1510,"unixTimeMs":1758662421643,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1704,"y":0,"timestamp":1526,"unixTimeMs":1758662421659,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1702,"y":0,"timestamp":1549,"unixTimeMs":1758662421682,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1705,"y":0,"timestamp":1633,"unixTimeMs":1758662421766,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1709,"y":0,"timestamp":1653,"unixTimeMs":1758662421786,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1707,"y":0,"timestamp":1989,"unixTimeMs":1758662422122,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}}]
@@ -0,0 +1 @@
1
+ [{"x":1709,"y":0,"timestamp":33,"unixTimeMs":1758662423222,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1707,"y":3,"timestamp":654,"unixTimeMs":1758662423843,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1704,"y":8,"timestamp":677,"unixTimeMs":1758662423866,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1704,"y":10,"timestamp":697,"unixTimeMs":1758662423886,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1703,"y":13,"timestamp":718,"unixTimeMs":1758662423907,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1709,"y":2,"timestamp":780,"unixTimeMs":1758662423969,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1709,"y":0,"timestamp":802,"unixTimeMs":1758662423991,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1706,"y":3,"timestamp":968,"unixTimeMs":1758662424157,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1698,"y":15,"timestamp":989,"unixTimeMs":1758662424178,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1691,"y":29,"timestamp":1016,"unixTimeMs":1758662424205,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1688,"y":36,"timestamp":1029,"unixTimeMs":1758662424218,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1687,"y":41,"timestamp":1056,"unixTimeMs":1758662424245,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1687,"y":42,"timestamp":1072,"unixTimeMs":1758662424261,"cursorType":"ns-resize","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1689,"y":38,"timestamp":1094,"unixTimeMs":1758662424283,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1700,"y":23,"timestamp":1116,"unixTimeMs":1758662424305,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1709,"y":9,"timestamp":1134,"unixTimeMs":1758662424323,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1709,"y":1,"timestamp":1157,"unixTimeMs":1758662424346,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1707,"y":1,"timestamp":1554,"unixTimeMs":1758662424743,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1702,"y":5,"timestamp":1578,"unixTimeMs":1758662424767,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1694,"y":13,"timestamp":1594,"unixTimeMs":1758662424783,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1690,"y":19,"timestamp":1618,"unixTimeMs":1758662424807,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1685,"y":25,"timestamp":1634,"unixTimeMs":1758662424823,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1684,"y":28,"timestamp":1658,"unixTimeMs":1758662424847,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1686,"y":30,"timestamp":1700,"unixTimeMs":1758662424889,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1691,"y":30,"timestamp":1720,"unixTimeMs":1758662424909,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1699,"y":30,"timestamp":1746,"unixTimeMs":1758662424935,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1709,"y":25,"timestamp":1764,"unixTimeMs":1758662424953,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1709,"y":16,"timestamp":1783,"unixTimeMs":1758662424972,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1709,"y":10,"timestamp":1808,"unixTimeMs":1758662424997,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1709,"y":0,"timestamp":1825,"unixTimeMs":1758662425014,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1702,"y":0,"timestamp":1890,"unixTimeMs":1758662425079,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1690,"y":1,"timestamp":1919,"unixTimeMs":1758662425108,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1682,"y":4,"timestamp":1930,"unixTimeMs":1758662425119,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1672,"y":12,"timestamp":1949,"unixTimeMs":1758662425138,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1669,"y":16,"timestamp":1976,"unixTimeMs":1758662425165,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1665,"y":22,"timestamp":1991,"unixTimeMs":1758662425180,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}}]
@@ -0,0 +1 @@
1
+ [{"x":1698,"y":14,"timestamp":31,"unixTimeMs":1758662426275,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1701,"y":11,"timestamp":51,"unixTimeMs":1758662426295,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1705,"y":6,"timestamp":76,"unixTimeMs":1758662426320,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1708,"y":1,"timestamp":95,"unixTimeMs":1758662426339,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1706,"y":8,"timestamp":426,"unixTimeMs":1758662426670,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1703,"y":13,"timestamp":449,"unixTimeMs":1758662426693,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1701,"y":16,"timestamp":467,"unixTimeMs":1758662426711,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1699,"y":19,"timestamp":489,"unixTimeMs":1758662426733,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1701,"y":17,"timestamp":573,"unixTimeMs":1758662426817,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1702,"y":15,"timestamp":595,"unixTimeMs":1758662426839,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1704,"y":12,"timestamp":623,"unixTimeMs":1758662426867,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1705,"y":9,"timestamp":635,"unixTimeMs":1758662426879,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1707,"y":6,"timestamp":660,"unixTimeMs":1758662426904,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1708,"y":4,"timestamp":678,"unixTimeMs":1758662426922,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1708,"y":2,"timestamp":702,"unixTimeMs":1758662426946,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1709,"y":0,"timestamp":744,"unixTimeMs":1758662426988,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1708,"y":3,"timestamp":890,"unixTimeMs":1758662427134,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1705,"y":8,"timestamp":917,"unixTimeMs":1758662427161,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1702,"y":12,"timestamp":928,"unixTimeMs":1758662427172,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1701,"y":14,"timestamp":951,"unixTimeMs":1758662427195,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1705,"y":10,"timestamp":1036,"unixTimeMs":1758662427280,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1709,"y":5,"timestamp":1058,"unixTimeMs":1758662427302,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1709,"y":2,"timestamp":1079,"unixTimeMs":1758662427323,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1709,"y":0,"timestamp":1100,"unixTimeMs":1758662427344,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1706,"y":5,"timestamp":1222,"unixTimeMs":1758662427466,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1704,"y":8,"timestamp":1244,"unixTimeMs":1758662427488,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1702,"y":13,"timestamp":1266,"unixTimeMs":1758662427510,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1701,"y":16,"timestamp":1288,"unixTimeMs":1758662427532,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1702,"y":14,"timestamp":1371,"unixTimeMs":1758662427615,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1703,"y":12,"timestamp":1395,"unixTimeMs":1758662427639,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1705,"y":9,"timestamp":1432,"unixTimeMs":1758662427676,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1706,"y":6,"timestamp":1454,"unixTimeMs":1758662427698,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1706,"y":4,"timestamp":1494,"unixTimeMs":1758662427738,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1704,"y":7,"timestamp":1561,"unixTimeMs":1758662427805,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1702,"y":10,"timestamp":1578,"unixTimeMs":1758662427822,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1702,"y":12,"timestamp":1617,"unixTimeMs":1758662427861,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1704,"y":12,"timestamp":1661,"unixTimeMs":1758662427905,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1706,"y":12,"timestamp":1685,"unixTimeMs":1758662427929,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1708,"y":11,"timestamp":1726,"unixTimeMs":1758662427970,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1709,"y":9,"timestamp":1748,"unixTimeMs":1758662427992,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1709,"y":7,"timestamp":1769,"unixTimeMs":1758662428013,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1709,"y":5,"timestamp":1791,"unixTimeMs":1758662428035,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1707,"y":5,"timestamp":1850,"unixTimeMs":1758662428094,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}},{"x":1708,"y":3,"timestamp":1978,"unixTimeMs":1758662428222,"cursorType":"default","type":"move","coordinateSystem":"video-relative","recordingType":"display","videoInfo":{"width":1710,"height":1112,"offsetX":0,"offsetY":0},"displayInfo":{"displayId":1,"width":1710,"height":1112}}]