node-mac-recorder 2.21.13 → 2.21.14

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