node-mac-recorder 2.21.13 → 2.21.15

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,7 +1,9 @@
1
1
  {
2
2
  "permissions": {
3
3
  "allow": [
4
- "Bash(ffmpeg:*)"
4
+ "Bash(ffmpeg:*)",
5
+ "Bash(chmod:*)",
6
+ "Bash(node test-sync.js:*)"
5
7
  ],
6
8
  "deny": [],
7
9
  "ask": []
package/index.js CHANGED
@@ -437,9 +437,9 @@ class MacRecorder extends EventEmitter {
437
437
 
438
438
  this.outputPath = outputPath;
439
439
 
440
- return new Promise((resolve, reject) => {
440
+ return new Promise(async (resolve, reject) => {
441
441
  try {
442
- // Create cursor file path with timestamp in the same directory as video
442
+ // SYNC FIX: Create unified session timestamp FIRST for all components
443
443
  const sessionTimestamp = Date.now();
444
444
  this.sessionTimestamp = sessionTimestamp;
445
445
  const outputDir = path.dirname(outputPath);
@@ -507,15 +507,45 @@ class MacRecorder extends EventEmitter {
507
507
  };
508
508
  }
509
509
 
510
+ // CRITICAL SYNC FIX: Start native recording FIRST (video/audio/camera)
511
+ // Then IMMEDIATELY start cursor tracking with the SAME timestamp
512
+ // This ensures ALL components capture their first frame at the same time
513
+
510
514
  let success;
511
515
  try {
516
+ console.log('🎯 SYNC: Starting native recording (screen/audio/camera) at timestamp:', sessionTimestamp);
512
517
  success = nativeBinding.startRecording(
513
518
  outputPath,
514
519
  recordingOptions
515
520
  );
521
+ if (success) {
522
+ console.log('✅ SYNC: Native recording started successfully');
523
+ }
516
524
  } catch (error) {
517
- // console.log('Native recording failed, trying alternative method');
518
525
  success = false;
526
+ console.warn('❌ Native recording failed to start:', error.message);
527
+ }
528
+
529
+ // Only start cursor if native recording started successfully
530
+ if (success) {
531
+ const standardCursorOptions = {
532
+ videoRelative: true,
533
+ displayInfo: this.recordingDisplayInfo,
534
+ recordingType: this.options.windowId ? 'window' :
535
+ this.options.captureArea ? 'area' : 'display',
536
+ captureArea: this.options.captureArea,
537
+ windowId: this.options.windowId,
538
+ startTimestamp: sessionTimestamp // Use the same timestamp base
539
+ };
540
+
541
+ try {
542
+ console.log('🎯 SYNC: Starting cursor tracking at timestamp:', sessionTimestamp);
543
+ await this.startCursorCapture(cursorFilePath, standardCursorOptions);
544
+ console.log('✅ SYNC: Cursor tracking started successfully');
545
+ } catch (cursorError) {
546
+ console.warn('⚠️ Cursor tracking failed to start:', cursorError.message);
547
+ // Continue with recording even if cursor fails - don't stop native recording
548
+ }
519
549
  }
520
550
 
521
551
  if (success) {
@@ -546,10 +576,12 @@ class MacRecorder extends EventEmitter {
546
576
  }
547
577
  }
548
578
  this.isRecording = true;
549
- this.recordingStartTime = Date.now();
579
+ // SYNC FIX: Use session timestamp for consistent timing across all components
580
+ this.recordingStartTime = sessionTimestamp;
550
581
 
551
582
  if (this.options.captureCamera === true && cameraFilePath) {
552
583
  this.cameraCaptureActive = true;
584
+ console.log('📹 SYNC: Camera recording started at timestamp:', sessionTimestamp);
553
585
  this.emit("cameraCaptureStarted", {
554
586
  outputPath: cameraFilePath,
555
587
  deviceId: this.options.cameraDeviceId || null,
@@ -560,6 +592,7 @@ class MacRecorder extends EventEmitter {
560
592
 
561
593
  if (captureAudio && audioFilePath) {
562
594
  this.audioCaptureActive = true;
595
+ console.log('🎙️ SYNC: Audio recording started at timestamp:', sessionTimestamp);
563
596
  this.emit("audioCaptureStarted", {
564
597
  outputPath: audioFilePath,
565
598
  deviceIds: {
@@ -571,20 +604,17 @@ class MacRecorder extends EventEmitter {
571
604
  });
572
605
  }
573
606
 
574
- // Start unified cursor tracking with video-relative coordinates
575
- // This ensures cursor positions match exactly with video frames
576
- const standardCursorOptions = {
577
- videoRelative: true,
578
- displayInfo: this.recordingDisplayInfo,
579
- recordingType: this.options.windowId ? 'window' :
580
- this.options.captureArea ? 'area' : 'display',
581
- captureArea: this.options.captureArea,
582
- windowId: this.options.windowId
583
- };
607
+ // SYNC FIX: Cursor tracking already started BEFORE recording for perfect sync
608
+ // (Removed duplicate cursor start code)
584
609
 
585
- this.startCursorCapture(cursorFilePath, standardCursorOptions).catch(cursorError => {
586
- console.warn('Unified cursor tracking failed:', cursorError.message);
587
- });
610
+ // Log synchronized recording summary
611
+ const activeComponents = [];
612
+ activeComponents.push('Screen');
613
+ if (this.cursorCaptureInterval) activeComponents.push('Cursor');
614
+ if (this.cameraCaptureActive) activeComponents.push('Camera');
615
+ if (this.audioCaptureActive) activeComponents.push('Audio');
616
+ console.log(`✅ SYNC COMPLETE: All components synchronized at timestamp ${sessionTimestamp}`);
617
+ console.log(` Active components: ${activeComponents.join(', ')}`);
588
618
 
589
619
  // Timer başlat (progress tracking için)
590
620
  this.recordingTimer = setInterval(() => {
@@ -696,20 +726,38 @@ class MacRecorder extends EventEmitter {
696
726
 
697
727
 
698
728
  /**
699
- * Ekran kaydını durdurur
729
+ * Ekran kaydını durdurur - SYNCHRONIZED stop for all components
700
730
  */
701
731
  async stopRecording() {
702
732
  if (!this.isRecording) {
703
733
  throw new Error("No recording in progress");
704
734
  }
705
735
 
706
- return new Promise((resolve, reject) => {
736
+ return new Promise(async (resolve, reject) => {
707
737
  try {
738
+ console.log('🛑 SYNC: Stopping all recording components simultaneously');
739
+
740
+ // SYNC FIX: Stop ALL components at the same time for perfect sync
741
+ // 1. Stop cursor tracking FIRST (it's instant)
742
+ if (this.cursorCaptureInterval) {
743
+ try {
744
+ console.log('🛑 SYNC: Stopping cursor tracking');
745
+ await this.stopCursorCapture();
746
+ console.log('✅ SYNC: Cursor tracking stopped');
747
+ } catch (cursorError) {
748
+ console.warn('⚠️ Cursor tracking failed to stop:', cursorError.message);
749
+ }
750
+ }
751
+
708
752
  let success = false;
709
-
710
- // Use native ScreenCaptureKit stop only
753
+
754
+ // 2. Stop native screen recording
711
755
  try {
756
+ console.log('🛑 SYNC: Stopping screen recording');
712
757
  success = nativeBinding.stopRecording();
758
+ if (success) {
759
+ console.log('✅ SYNC: Screen recording stopped');
760
+ }
713
761
  } catch (nativeError) {
714
762
  // console.log('Native stop failed:', nativeError.message);
715
763
  success = true; // Assume success to avoid throwing
@@ -744,6 +792,7 @@ class MacRecorder extends EventEmitter {
744
792
 
745
793
  if (this.cameraCaptureActive) {
746
794
  this.cameraCaptureActive = false;
795
+ console.log('📹 SYNC: Camera recording stopped');
747
796
  this.emit("cameraCaptureStopped", {
748
797
  outputPath: this.cameraCaptureFile || null,
749
798
  success: success === true,
@@ -753,6 +802,7 @@ class MacRecorder extends EventEmitter {
753
802
 
754
803
  if (this.audioCaptureActive) {
755
804
  this.audioCaptureActive = false;
805
+ console.log('🎙️ SYNC: Audio recording stopped');
756
806
  this.emit("audioCaptureStopped", {
757
807
  outputPath: this.audioCaptureFile || null,
758
808
  success: success === true,
@@ -760,12 +810,11 @@ class MacRecorder extends EventEmitter {
760
810
  });
761
811
  }
762
812
 
763
- // Stop cursor tracking automatically
764
- if (this.cursorCaptureInterval) {
765
- this.stopCursorCapture().catch(cursorError => {
766
- console.warn('Cursor tracking failed to stop:', cursorError.message);
767
- });
768
- }
813
+ // SYNC FIX: Cursor tracking already stopped at the beginning for sync
814
+ // (Removed duplicate cursor stop code)
815
+
816
+ // Log synchronized stop summary
817
+ console.log('✅ SYNC STOP COMPLETE: All recording components stopped simultaneously');
769
818
 
770
819
  // Timer durdur
771
820
  if (this.recordingTimer) {
@@ -964,6 +1013,7 @@ class MacRecorder extends EventEmitter {
964
1013
  * @param {string} options.recordingType - Type of recording: 'display', 'window', 'area'
965
1014
  * @param {Object} options.captureArea - Capture area for area recording coordinate transformation
966
1015
  * @param {number} options.windowId - Window ID for window recording coordinate transformation
1016
+ * @param {number} options.startTimestamp - Pre-defined start timestamp for synchronization (optional)
967
1017
  */
968
1018
  async startCursorCapture(intervalOrFilepath = 100, options = {}) {
969
1019
  let filepath;
@@ -985,6 +1035,9 @@ class MacRecorder extends EventEmitter {
985
1035
  throw new Error("Cursor capture is already running");
986
1036
  }
987
1037
 
1038
+ // SYNC FIX: Use pre-defined timestamp if provided for synchronization
1039
+ const syncStartTime = options.startTimestamp || Date.now();
1040
+
988
1041
  // Use video-relative coordinate system for all recording types
989
1042
  if (options.videoRelative && options.displayInfo) {
990
1043
  // Calculate video offset based on recording type
@@ -1068,7 +1121,8 @@ class MacRecorder extends EventEmitter {
1068
1121
  fs.writeFileSync(filepath, "[");
1069
1122
 
1070
1123
  this.cursorCaptureFile = filepath;
1071
- this.cursorCaptureStartTime = Date.now();
1124
+ // SYNC FIX: Use synchronized start time for accurate timestamp calculation
1125
+ this.cursorCaptureStartTime = syncStartTime;
1072
1126
  this.cursorCaptureFirstWrite = true;
1073
1127
  this.lastCapturedData = null;
1074
1128
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-mac-recorder",
3
- "version": "2.21.13",
3
+ "version": "2.21.15",
4
4
  "description": "Native macOS screen recording package for Node.js applications",
5
5
  "main": "index.js",
6
6
  "keywords": [