node-mac-recorder 2.24.3 → 2.24.4

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.
Files changed (2) hide show
  1. package/index.js +85 -37
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -2,6 +2,12 @@ const { EventEmitter } = require("events");
2
2
  const path = require("path");
3
3
  const fs = require("fs");
4
4
 
5
+ // Videonun ilk karesi yakalanana kadar beklenecek ust sinir.
6
+ // Normalde birkac yuz ms; clamshell'de ekran envanteri yeniden kuruldugu icin
7
+ // saniyelere cikabiliyor. Beklemek, cursor'i yanlis referansa baglamaktan iyidir.
8
+ const VIDEO_START_TIMEOUT_MS = 12000;
9
+ const VIDEO_START_POLL_MS = 10;
10
+
5
11
  // Auto-switch to Electron-safe implementation when running under Electron and binary exists
6
12
  let USE_ELECTRON_SAFE = false;
7
13
  let ElectronSafeMacRecorder = null;
@@ -860,54 +866,89 @@ class MacRecorder extends EventEmitter {
860
866
 
861
867
  // Only start cursor if native recording started successfully
862
868
  if (success) {
863
- // For ScreenCaptureKit (async startup), wait briefly until native fully initialized
864
- // ScreenCaptureKit needs ~150-300ms to start + ~150ms for first 10 frames
869
+ // ==========================================================
870
+ // VIDEONUN ILK KARESINI BEKLE SENKRONUN TEMELI
871
+ // ==========================================================
872
+ // getRecordingStatus() BU AMAC ICIN KULLANILAMAZ:
873
+ // ScreenCaptureKit stream'i henuz baslamadiysa native taraf
874
+ // `g_isRecording` fallback'ine dusup TRUE donuyor, yani
875
+ // "kayit komutu verildi"yi "kayit basladi" saniyor.
876
+ // Olculdu: "fully ready after 0ms" + videoStartTimestamp = 0.
877
+ // Sonucu: cursor timeline'i video'dan ONCE basliyor. Normal
878
+ // kosulda fark ~150-300ms, clamshell'de (ekran envanteri
879
+ // yeniden kuruldugu icin) SANIYELER — editordeki 1sn'lik
880
+ // telafi limiti bunu kapatamiyor ve cursor gorunur sekilde kayiyor.
881
+ //
882
+ // Dogru sinyal getVideoStartTimestamp(): native ilk kareyi
883
+ // yakalayip g_videoStartTime'i set ettiginde gecerli bir
884
+ // wall-clock degeri doner. Timeline'i buna baglayinca
885
+ // baslatma hizi (prewarm vb.) senkronu ETKILEMEZ.
886
+ const usesScreenCaptureKit =
887
+ this.options.preferScreenCaptureKit === true;
888
+
889
+ const readVideoStart = () => {
890
+ try {
891
+ const value = Number(
892
+ typeof nativeBinding.getVideoStartTimestamp === 'function'
893
+ ? nativeBinding.getVideoStartTimestamp()
894
+ : 0
895
+ );
896
+ return Number.isFinite(value) && value > 0 ? value : 0;
897
+ } catch (_) {
898
+ return 0;
899
+ }
900
+ };
901
+
902
+ let videoStartTimestamp = 0;
865
903
  const waitStart = Date.now();
866
- try {
904
+
905
+ if (usesScreenCaptureKit) {
906
+ while (Date.now() - waitStart < VIDEO_START_TIMEOUT_MS) {
907
+ videoStartTimestamp = readVideoStart();
908
+ if (videoStartTimestamp > 0) {
909
+ console.log(
910
+ `✅ SYNC: Video ilk karesi ${Date.now() - waitStart}ms'de yakalandi`
911
+ );
912
+ break;
913
+ }
914
+ await new Promise(r => setTimeout(r, VIDEO_START_POLL_MS));
915
+ }
916
+
917
+ if (!videoStartTimestamp) {
918
+ console.warn(
919
+ `⚠️ SYNC: Video baslangici ${VIDEO_START_TIMEOUT_MS}ms icinde okunamadi — heuristik hizalamaya dusuluyor`
920
+ );
921
+ }
922
+ } else {
923
+ // AVFoundation yolunda video-start damgasi yok; eski
924
+ // hazir-olma beklemesi korunuyor.
867
925
  while (Date.now() - waitStart < 600) {
868
926
  try {
869
- const nativeStatus = nativeBinding && nativeBinding.getRecordingStatus ? nativeBinding.getRecordingStatus() : true;
870
- if (nativeStatus) {
871
- console.log(`✅ SYNC: Native recording fully ready after ${Date.now() - waitStart}ms`);
927
+ if (
928
+ nativeBinding &&
929
+ nativeBinding.getRecordingStatus &&
930
+ nativeBinding.getRecordingStatus()
931
+ ) {
872
932
  break;
873
933
  }
874
934
  } catch (_) {}
875
935
  await new Promise(r => setTimeout(r, 30));
876
936
  }
877
- } catch (_) {}
937
+ }
938
+
878
939
  this.sessionTimestamp = sessionTimestamp;
940
+ this.videoStartTimestamp = videoStartTimestamp;
941
+ // Onceki kayittan kalan referans sizmasin
942
+ this.timelineStartTimestamp = 0;
879
943
 
880
- // Native sync_timeline handles A/V alignment - no JS-level delay needed
881
944
  const syncTimestamp = Date.now();
882
945
  this.syncTimestamp = syncTimestamp;
883
946
  this.recordingStartTime = syncTimestamp;
884
- console.log(`🎯 CURSOR SYNC: Cursor tracking will use timestamp: ${syncTimestamp}`);
885
-
886
- // CURSOR/VIDEO TIME ALIGNMENT:
887
- // Cursor timeline'in t=0'i bu andir (syncTimestamp), ama videonun
888
- // ILK KARESI daha once yakalanmis olabilir (ScreenCaptureKit
889
- // baslatma gecikmesi + yukaridaki hazir-olma beklemesi). Editor
890
- // "cursor'un ilk ornegi = video t=0" varsayarsa aradaki fark sabit
891
- // bir zaman kaymasi olarak kalir. Native gercek video baslangicini
892
- // biliyor; okuyup sakla ki stop'ta cursor JSON'una yazabilelim.
893
- this.videoStartTimestamp = 0;
894
- // Onceki kayittan kalan referans sizmasin
895
- this.timelineStartTimestamp = 0;
896
- try {
897
- const nativeVideoStart =
898
- typeof nativeBinding.getVideoStartTimestamp === 'function'
899
- ? Number(nativeBinding.getVideoStartTimestamp())
900
- : 0;
901
- if (Number.isFinite(nativeVideoStart) && nativeVideoStart > 0) {
902
- this.videoStartTimestamp = nativeVideoStart;
903
- console.log(
904
- `🎯 SYNC: Video first-frame timestamp: ${nativeVideoStart} (cursor starts ${(syncTimestamp - nativeVideoStart).toFixed(0)}ms later)`
905
- );
906
- } else {
907
- console.warn('⚠️ SYNC: Video start timestamp unavailable — cursor sync metadata will be skipped');
908
- }
909
- } catch (videoStartError) {
910
- console.warn('⚠️ SYNC: Video start timestamp read failed:', videoStartError.message);
947
+
948
+ if (videoStartTimestamp > 0) {
949
+ console.log(
950
+ `🎯 SYNC: Video first-frame timestamp: ${videoStartTimestamp} (JS bunu ${(syncTimestamp - videoStartTimestamp).toFixed(0)}ms sonra fark etti)`
951
+ );
911
952
  }
912
953
 
913
954
  // ZAMAN REFERANSI: cursor/klavye timeline'i VIDEONUN ilk karesine
@@ -1174,9 +1215,16 @@ class MacRecorder extends EventEmitter {
1174
1215
 
1175
1216
  return new Promise(async (resolve, reject) => {
1176
1217
  const stopRequestedAt = Date.now();
1218
+ // Sure, VIDEONUN baslangicindan olculur. recordingStartTime (JS'in
1219
+ // hazir oldugunu fark ettigi an) videodan birkac on ms sonra oldugu
1220
+ // icin buradan hesaplanan stopLimit videoyu kuyrugundan kirpiyordu.
1221
+ const durationReference =
1222
+ this.timelineStartTimestamp && this.timelineStartTimestamp > 0
1223
+ ? this.timelineStartTimestamp
1224
+ : this.recordingStartTime;
1177
1225
  const elapsedSeconds =
1178
- this.recordingStartTime && this.recordingStartTime > 0
1179
- ? (stopRequestedAt - this.recordingStartTime) / 1000
1226
+ durationReference && durationReference > 0
1227
+ ? (stopRequestedAt - durationReference) / 1000
1180
1228
  : -1;
1181
1229
  try {
1182
1230
  console.log('🛑 SYNC: Stopping all recording components simultaneously');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-mac-recorder",
3
- "version": "2.24.3",
3
+ "version": "2.24.4",
4
4
  "description": "Native macOS screen recording package for Node.js applications",
5
5
  "main": "index.js",
6
6
  "keywords": [