node-mac-recorder 2.16.6 โ†’ 2.16.8

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,56 +1,9 @@
1
1
  {
2
2
  "permissions": {
3
3
  "allow": [
4
- "Bash(node:*)",
5
- "Bash(timeout:*)",
6
- "Bash(rm:*)",
7
- "Bash(python3:*)",
8
- "Bash(grep:*)",
9
- "Bash(cat:*)",
10
- "Bash(brew install:*)",
11
- "Bash(open:*)",
12
- "Bash(curl:*)",
13
- "Bash(system_profiler:*)",
14
- "Bash(mkdir:*)",
15
- "Bash(/dev/null)",
16
- "Bash(ls:*)",
17
- "Bash(touch:*)",
18
- "Bash(git add:*)",
19
- "Bash(git commit:*)",
20
- "Bash(find:*)",
21
- "WebFetch(domain:developer.apple.com)",
22
- "WebFetch(domain:github.com)",
23
- "WebFetch(domain:nonstrict.eu)",
24
- "Bash(cp:*)",
25
- "Bash(git checkout:*)",
26
- "Bash(ELECTRON_VERSION=25.0.0 node -e \"\nconsole.log(''ELECTRON_VERSION env:'', process.env.ELECTRON_VERSION);\nconsole.log(''getenv result would be:'', process.env.ELECTRON_VERSION || ''null'');\n\")",
27
- "Bash(ELECTRON_VERSION=25.0.0 node test-env-detection.js)",
28
- "Bash(ELECTRON_VERSION=25.0.0 node test-native-call.js)",
29
- "Bash(chmod:*)",
30
- "Bash(ffprobe:*)",
31
- "Bash(ffmpeg:*)",
32
- "WebSearch",
33
- "Bash(ELECTRON_RUN_AS_NODE=1 node -e \"\nconsole.log(''๐Ÿ” Testing with proper permissions and Electron env'');\nconst MacRecorder = require(''./index'');\nconst recorder = new MacRecorder();\n\nasync function test() {\n try {\n const outputPath = ''./test-output/proper-test.mov'';\n console.log(''๐Ÿ“น Starting recording...'');\n const success = await recorder.startRecording(outputPath, {\n captureCursor: true,\n includeMicrophone: false,\n includeSystemAudio: false\n });\n \n if (success) {\n console.log(''โœ… Recording started - waiting 2 seconds'');\n await new Promise(resolve => setTimeout(resolve, 2000));\n console.log(''๐Ÿ›‘ Stopping recording...'');\n await recorder.stopRecording();\n console.log(''โœ… Test completed'');\n } else {\n console.log(''โŒ Recording start failed'');\n }\n } catch (error) {\n console.log(''โŒ Error:'', error.message);\n }\n}\n\ntest();\n\")",
34
- "Bash(ELECTRON_RUN_AS_NODE=1 node -e \"\nconsole.log(''''๐Ÿ” Debugging frame writing...'''');\nconst MacRecorder = require(''''./index'''');\nconst recorder = new MacRecorder();\n\nasync function debugFrameWriting() {\n try {\n const outputPath = ''''./test-output/frame-debug.mov'''';\n console.log(''''๐Ÿ“น Starting debug test...'''');\n \n const success = await recorder.startRecording(outputPath);\n \n if (success) {\n console.log(''''โฑ๏ธ Recording for 2 seconds...'''');\n await new Promise(resolve => setTimeout(resolve, 2000));\n \n console.log(''''๐Ÿ›‘ Stopping...'''');\n await recorder.stopRecording();\n \n // Wait for finalization\n await new Promise(resolve => setTimeout(resolve, 1000));\n \n } else {\n console.log(''''โŒ Failed to start'''');\n }\n } catch (error) {\n console.log(''''โŒ Error:'''', error);\n }\n}\n\ndebugFrameWriting();\n\")",
35
- "Bash(ELECTRON_RUN_AS_NODE=1 node -e \"\nconsole.log(''๐Ÿ” Testing with proper permissions and Electron env'');\nconst MacRecorder = require(''./index'');\nconst recorder = new MacRecorder();\n\nasync function test() {\n try {\n const outputPath = ''./test-output/crash-test.mov'';\n console.log(''๐Ÿ“น Starting recording...'');\n const success = await recorder.startRecording(outputPath, {\n captureCursor: true,\n includeMicrophone: false,\n includeSystemAudio: false\n });\n \n if (success) {\n console.log(''โœ… Recording started - waiting 3 seconds'');\n await new Promise(resolve => setTimeout(resolve, 3000));\n console.log(''๐Ÿ›‘ Stopping recording...'');\n await recorder.stopRecording();\n console.log(''โœ… Test completed without crash'');\n } else {\n console.log(''โŒ Recording start failed'');\n }\n } catch (error) {\n console.log(''โŒ Error:'', error.message);\n console.log(''Stack:'', error.stack);\n }\n}\n\ntest();\n\")",
36
- "Bash(ELECTRON_RUN_AS_NODE=1 node -e \"\nconsole.log(''๐Ÿ” Debugging frame writing...'');\nconst MacRecorder = require(''./index'');\nconst recorder = new MacRecorder();\n\nasync function debugFrameWriting() {\n try {\n const outputPath = ''./test-output/frame-debug.mov'';\n console.log(''๐Ÿ“น Starting debug test...'');\n \n const success = await recorder.startRecording(outputPath);\n \n if (success) {\n console.log(''โฑ๏ธ Recording for 2 seconds...'');\n await new Promise(resolve => setTimeout(resolve, 2000));\n \n console.log(''๐Ÿ›‘ Stopping...'');\n await recorder.stopRecording();\n \n // Wait for finalization\n await new Promise(resolve => setTimeout(resolve, 1000));\n \n } else {\n console.log(''โŒ Failed to start'');\n }\n } catch (error) {\n console.log(''โŒ Error:'', error);\n }\n}\n\ndebugFrameWriting();\n\")",
37
- "Bash(ELECTRON_RUN_AS_NODE=1 node -e \"\nconsole.log(''๐Ÿงช Testing without AVFoundation - Pure ScreenCaptureKit'');\nconst MacRecorder = require(''./index'');\nconst recorder = new MacRecorder();\n\nasync function testPureScreenCaptureKit() {\n try {\n const outputPath = ''./test-output/pure-sck.mov'';\n console.log(''๐Ÿ“น Starting pure ScreenCaptureKit test...'');\n \n const success = await recorder.startRecording(outputPath, {\n captureCursor: true,\n includeMicrophone: false,\n includeSystemAudio: false\n });\n \n if (success) {\n console.log(''โœ… Recording started - waiting 2 seconds'');\n await new Promise(resolve => setTimeout(resolve, 2000));\n \n console.log(''๐Ÿ›‘ Stopping recording...'');\n await recorder.stopRecording();\n console.log(''โœ… Pure ScreenCaptureKit test completed successfully!'');\n } else {\n console.log(''โŒ Recording failed to start'');\n }\n } catch (error) {\n console.log(''โŒ Error:'', error.message);\n console.log(''Stack:'', error.stack);\n }\n}\n\ntestPureScreenCaptureKit();\n\")",
38
- "Bash(ELECTRON_RUN_AS_NODE=1 node -e \"\nconsole.log(''๐ŸŽต Testing audio + crop area recording'');\nconst MacRecorder = require(''./index'');\nconst recorder = new MacRecorder();\n\nasync function testAudioCropRecording() {\n try {\n const outputPath = ''./test-output/audio-crop-test.mov'';\n console.log(''๐Ÿ“น Starting recording with audio and crop area...'');\n \n const success = await recorder.startRecording(outputPath, {\n captureCursor: false,\n includeMicrophone: true,\n includeSystemAudio: true,\n captureArea: {\n x: 100, \n y: 100,\n width: 800,\n height: 600\n }\n });\n \n if (success) {\n console.log(''โœ… Recording started - waiting 3 seconds'');\n await new Promise(resolve => setTimeout(resolve, 3000));\n \n console.log(''๐Ÿ›‘ Stopping recording...'');\n await recorder.stopRecording();\n console.log(''โœ… Audio + crop test completed successfully!'');\n } else {\n console.log(''โŒ Recording failed to start'');\n }\n } catch (error) {\n console.log(''โŒ Error:'', error.message);\n }\n}\n\ntestAudioCropRecording();\n\")",
39
- "Bash(ELECTRON_RUN_AS_NODE=1 node -e \"\nconsole.log(''๐Ÿงช Testing crash fix with null path protection'');\nconst MacRecorder = require(''./index'');\nconst recorder = new MacRecorder();\n\nasync function testCrashFix() {\n try {\n const outputPath = ''./test-output/crash-fix-test.mov'';\n console.log(''๐Ÿ“น Starting crash fix test...'');\n \n const success = await recorder.startRecording(outputPath, {\n captureCursor: true,\n includeMicrophone: true,\n includeSystemAudio: true,\n captureArea: {\n x: 100,\n y: 100,\n width: 800,\n height: 600\n }\n });\n \n if (success) {\n console.log(''โœ… Recording started - waiting 3 seconds'');\n await new Promise(resolve => setTimeout(resolve, 3000));\n \n console.log(''๐Ÿ›‘ Stopping recording...'');\n await recorder.stopRecording();\n console.log(''โœ… Crash fix test completed successfully!'');\n } else {\n console.log(''โŒ Recording failed to start'');\n }\n } catch (error) {\n console.log(''โŒ Error:'', error.message);\n console.log(''Stack:'', error.stack);\n }\n}\n\ntestCrashFix();\n\")",
40
- "Bash(ELECTRON_RUN_AS_NODE=1 node -e \"\nconsole.log(''๐Ÿ Final stability test with all features'');\nconst MacRecorder = require(''./index'');\nconst recorder = new MacRecorder();\n\nasync function finalTest() {\n try {\n const outputPath = ''./test-output/final-stability-test.mov'';\n console.log(''๐Ÿ“น Starting final stability test...'');\n \n const success = await recorder.startRecording(outputPath, {\n captureCursor: true,\n includeMicrophone: true,\n includeSystemAudio: true,\n captureArea: {\n x: 200,\n y: 200,\n width: 600,\n height: 400\n }\n });\n \n if (success) {\n console.log(''โœ… Recording started - waiting 4 seconds'');\n await new Promise(resolve => setTimeout(resolve, 4000));\n \n console.log(''๐Ÿ›‘ Stopping recording...'');\n await recorder.stopRecording();\n console.log(''๐ŸŽ‰ FINAL TEST COMPLETED SUCCESSFULLY - NO CRASH!'');\n } else {\n console.log(''โŒ Recording failed to start'');\n }\n } catch (error) {\n console.log(''โŒ Error:'', error.message);\n }\n}\n\nfinalTest();\n\")",
41
- "Bash(ELECTRON_RUN_AS_NODE=1 node -e \"\nconsole.log(''๐ŸŽต Testing system audio only recording'');\nconst MacRecorder = require(''./index'');\nconst recorder = new MacRecorder();\n\nasync function testSystemAudio() {\n try {\n const outputPath = ''./test-output/system-audio-only.mov'';\n console.log(''๐Ÿ“น Starting system audio only recording...'');\n \n const success = await recorder.startRecording(outputPath, {\n captureCursor: false,\n includeMicrophone: false, // Mikrophone DEVRE DIลžI \n includeSystemAudio: true, // Sadece sistem sesi Aร‡IK\n });\n \n if (success) {\n console.log(''โœ… System audio recording started - waiting 4 seconds'');\n await new Promise(resolve => setTimeout(resolve, 4000));\n \n console.log(''๐Ÿ›‘ Stopping recording...'');\n await recorder.stopRecording();\n console.log(''โœ… System audio test completed!'');\n } else {\n console.log(''โŒ Recording failed to start'');\n }\n } catch (error) {\n console.log(''โŒ Error:'', error.message);\n }\n}\n\ntestSystemAudio();\n\")",
42
- "Bash(ELECTRON_RUN_AS_NODE=1 node -e \"\nconsole.log(''๐Ÿ Final stability test with all features'');\nconst MacRecorder = require(''./index'');\nconst recorder = new MacRecorder();\n\nasync function finalTest() {\n try {\n const outputPath = ''./test-output/final-stability-test.mov'';\n console.log(''๐Ÿ“น Starting final stability test...'');\n \n const success = await recorder.startRecording(outputPath, {\n captureCursor: true,\n includeMicrophone: false,\n includeSystemAudio: true,\n captureArea: {\n x: 200,\n y: 200,\n width: 600,\n height: 400\n }\n });\n \n if (success) {\n console.log(''โœ… Recording started - waiting 4 seconds'');\n await new Promise(resolve => setTimeout(resolve, 4000));\n \n console.log(''๐Ÿ›‘ Stopping recording...'');\n await recorder.stopRecording();\n console.log(''๐ŸŽ‰ FINAL TEST COMPLETED SUCCESSFULLY - NO CRASH!'');\n } else {\n console.log(''โŒ Recording failed to start'');\n }\n } catch (error) {\n console.log(''โŒ Error:'', error.message);\n }\n}\n\nfinalTest();\n\")",
43
- "Bash(ELECTRON_RUN_AS_NODE=1 node -e \"\nconsole.log(''๐ŸŽต Testing both microphone and system audio'');\nconst MacRecorder = require(''./index'');\nconst recorder = new MacRecorder();\n\nasync function testBothAudio() {\n try {\n const outputPath = ''./test-output/both-audio-test.mov'';\n console.log(''๐Ÿ“น Starting recording with both audio sources...'');\n \n const success = await recorder.startRecording(outputPath, {\n captureCursor: true,\n includeMicrophone: true, // Mikrophone Aร‡IK\n includeSystemAudio: true // Sistem sesi Aร‡IK\n });\n \n if (success) {\n console.log(''โœ… Recording started - waiting 5 seconds'');\n await new Promise(resolve => setTimeout(resolve, 5000));\n \n console.log(''๐Ÿ›‘ Stopping recording...'');\n await recorder.stopRecording();\n console.log(''โœ… Both audio test completed!'');\n } else {\n console.log(''โŒ Recording failed to start'');\n }\n } catch (error) {\n console.log(''โŒ Error:'', error.message);\n console.log(''Stack:'', error.stack);\n }\n}\n\ntestBothAudio();\n\")",
44
- "Bash(ELECTRON_RUN_AS_NODE=1 node -e \"\nconsole.log(''๐ŸŽค Testing microphone only'');\nconst MacRecorder = require(''./index'');\nconst recorder = new MacRecorder();\n\nasync function testMicrophoneOnly() {\n try {\n const outputPath = ''./test-output/microphone-only-test.mov'';\n console.log(''๐Ÿ“น Starting microphone only recording...'');\n \n const success = await recorder.startRecording(outputPath, {\n captureCursor: false,\n includeMicrophone: true, // Sadece mikrofon Aร‡IK\n includeSystemAudio: false // Sistem sesi KAPALI\n });\n \n if (success) {\n console.log(''โœ… Microphone recording started - waiting 3 seconds'');\n await new Promise(resolve => setTimeout(resolve, 3000));\n \n console.log(''๐Ÿ›‘ Stopping microphone recording...'');\n await recorder.stopRecording();\n console.log(''โœ… Microphone only test completed!'');\n } else {\n console.log(''โŒ Recording failed to start'');\n }\n } catch (error) {\n console.log(''โŒ Error:'', error.message);\n }\n}\n\ntestMicrophoneOnly();\n\")",
45
- "Bash(ELECTRON_RUN_AS_NODE=1 node -e \"\nconsole.log(''๐Ÿš€ Full stress test - All features + Both audio sources'');\nconst MacRecorder = require(''./index'');\nconst recorder = new MacRecorder();\n\nasync function fullStressTest() {\n try {\n const outputPath = ''./test-output/full-stress-test.mov'';\n console.log(''๐Ÿ“น Starting full featured recording (stress test)...'');\n \n const success = await recorder.startRecording(outputPath, {\n captureCursor: true, // Cursor kayฤฑt\n includeMicrophone: true, // Mikrofon kayฤฑt\n includeSystemAudio: true, // Sistem sesi kayฤฑt\n captureArea: { // Crop area\n x: 300,\n y: 300,\n width: 800,\n height: 500\n }\n });\n \n if (success) {\n console.log(''โœ… Full stress test started - waiting 6 seconds'');\n await new Promise(resolve => setTimeout(resolve, 6000));\n \n console.log(''๐Ÿ›‘ Stopping full stress test...'');\n await recorder.stopRecording();\n console.log(''๐ŸŽ‰ FULL STRESS TEST COMPLETED WITHOUT CRASH!'');\n } else {\n console.log(''โŒ Recording failed to start'');\n }\n } catch (error) {\n console.log(''โŒ Error:'', error.message);\n console.log(''Stack:'', error.stack);\n }\n}\n\nfullStressTest();\n\")",
46
- "Bash(ELECTRON_RUN_AS_NODE=1 node -e \"\nconsole.log(''๐Ÿงช Testing version detection and fallback system'');\nconst MacRecorder = require(''./index'');\nconst recorder = new MacRecorder();\n\nasync function testVersionDetection() {\n try {\n const outputPath = ''./test-output/version-detection-test.mov'';\n console.log(''๐Ÿ“น Starting recording with version detection...'');\n \n const success = await recorder.startRecording(outputPath, {\n captureCursor: true,\n includeMicrophone: false,\n includeSystemAudio: true\n });\n \n if (success) {\n console.log(''โœ… Recording started - waiting 3 seconds'');\n await new Promise(resolve => setTimeout(resolve, 3000));\n \n console.log(''๐Ÿ›‘ Stopping recording...'');\n await recorder.stopRecording();\n console.log(''โœ… Version detection test completed successfully!'');\n } else {\n console.log(''โŒ Recording failed to start'');\n }\n } catch (error) {\n console.log(''โŒ Error:'', error.message);\n }\n}\n\ntestVersionDetection();\n\")",
47
- "Bash(ELECTRON_RUN_AS_NODE=1 node -e \"\nconsole.log(''๐Ÿงช Testing Electron safety measures'');\nconst MacRecorder = require(''./index'');\nconst recorder = new MacRecorder();\n\nasync function testElectronSafety() {\n try {\n const outputPath = ''./test-output/electron-safe-test.mov'';\n console.log(''๐Ÿ“น Starting recording in Electron environment...'');\n \n const success = await recorder.startRecording(outputPath, {\n captureCursor: true,\n includeMicrophone: false,\n includeSystemAudio: true\n });\n \n if (success) {\n console.log(''โœ… Recording started safely in Electron - waiting 3 seconds'');\n await new Promise(resolve => setTimeout(resolve, 3000));\n \n console.log(''๐Ÿ›‘ Stopping recording...'');\n await recorder.stopRecording();\n console.log(''โœ… Electron safety test completed successfully!'');\n } else {\n console.log(''โš ๏ธ Recording failed to start (expected in some cases)'');\n }\n } catch (error) {\n console.log(''โŒ Error:'', error.message);\n }\n}\n\ntestElectronSafety();\n\")",
48
- "Bash(FORCE_AVFOUNDATION=1 node -e \"\nconsole.log(''๐Ÿงช Testing AVFoundation fallback path'');\nconst MacRecorder = require(''./index'');\nconst recorder = new MacRecorder();\n\nasync function testAVFoundationPath() {\n try {\n const outputPath = ''./test-output/force-avfoundation-test.mov'';\n console.log(''๐Ÿ“น Testing AVFoundation (force mode)...'');\n \n const success = await recorder.startRecording(outputPath, {\n captureCursor: true,\n includeMicrophone: false,\n includeSystemAudio: true\n });\n \n if (success) {\n console.log(''โœ… Recording started'');\n await new Promise(resolve => setTimeout(resolve, 2000));\n \n await recorder.stopRecording();\n console.log(''โœ… AVFoundation test completed!'');\n } else {\n console.log(''โŒ Recording failed'');\n }\n } catch (error) {\n console.log(''โŒ Error:'', error.message);\n }\n}\n\ntestAVFoundationPath();\n\")",
49
- "Bash(FORCE_AVFOUNDATION=1 node -e \"\nconsole.log(''๐Ÿงช Testing AVFoundation fallback path (macOS 14 simulation)'');\nconst MacRecorder = require(''./index'');\nconst recorder = new MacRecorder();\n\nasync function testAVFoundationPath() {\n try {\n const outputPath = ''./test-output/force-avfoundation-test.mov'';\n console.log(''๐Ÿ“น Testing AVFoundation (simulating macOS 14)...'');\n \n const success = await recorder.startRecording(outputPath, {\n captureCursor: true,\n includeMicrophone: false,\n includeSystemAudio: true\n });\n \n if (success) {\n console.log(''โœ… Recording started with AVFoundation'');\n await new Promise(resolve => setTimeout(resolve, 3000));\n \n await recorder.stopRecording();\n console.log(''โœ… AVFoundation test completed successfully!'');\n console.log(''This is how it should work on macOS 14'');\n } else {\n console.log(''โŒ Recording failed to start'');\n }\n } catch (error) {\n console.log(''โŒ Error:'', error.message);\n }\n}\n\ntestAVFoundationPath();\n\")",
50
- "Bash(FORCE_AVFOUNDATION=1 node -e \"\nconsole.log(''๐Ÿงช Testing AVFoundation with detailed debugging'');\nconst MacRecorder = require(''./index'');\nconst recorder = new MacRecorder();\n\nasync function testWithDebugging() {\n try {\n const outputPath = ''./test-output/debug-test.mov'';\n console.log(''๐Ÿ“น Starting AVFoundation test with debugging...'');\n console.log(''Expected to see detailed AVFoundation logs in console'');\n \n const success = await recorder.startRecording(outputPath, {\n captureCursor: true,\n includeMicrophone: false,\n includeSystemAudio: true\n });\n \n if (success) {\n console.log(''โœ… Recording started'');\n await new Promise(resolve => setTimeout(resolve, 2000));\n \n await recorder.stopRecording();\n console.log(''โœ… Test completed'');\n } else {\n console.log(''โŒ Recording failed - check console logs for AVFoundation details'');\n }\n } catch (error) {\n console.log(''โŒ Error:'', error.message);\n }\n}\n\ntestWithDebugging();\n\")",
51
- "Bash(FORCE_AVFOUNDATION=1 node -e \"\nconsole.log(''๐Ÿงช Testing macOS 13/14 path (forced AVFoundation)'');\nconst MacRecorder = require(''./index'');\nconst recorder = new MacRecorder();\n\nasync function testMacOS1314Path() {\n try {\n const outputPath = ''./test-output/macos1314-test.mov'';\n console.log(''๐Ÿ“น Starting test for macOS 13/14 path...'');\n console.log(''This simulates the exact path macOS 13/14 users will take'');\n console.log('''');\n \n const success = await recorder.startRecording(outputPath, {\n captureCursor: true,\n includeMicrophone: false,\n includeSystemAudio: true\n });\n \n if (success) {\n console.log(''โœ… SUCCESS: Recording started on macOS 13/14 path!'');\n await new Promise(resolve => setTimeout(resolve, 3000));\n \n await recorder.stopRecording();\n console.log(''โœ… Recording completed successfully!'');\n console.log(''๐Ÿ“ Output:'', outputPath);\n } else {\n console.log(''โŒ Recording failed to start'');\n }\n } catch (error) {\n console.log(''โŒ Error:'', error.message);\n console.log(''This error would be what macOS 13/14 users see'');\n }\n}\n\ntestMacOS1314Path();\n\")",
52
- "Bash(FORCE_AVFOUNDATION=1 node -e \"\nconsole.log(''๐Ÿงช Testing macOS 14 Direct AVFoundation Path'');\nconst MacRecorder = require(''./index'');\nconst recorder = new MacRecorder();\n\nasync function testDirectAVFoundation() {\n try {\n const outputPath = ''./test-output/direct-avfoundation-test.mov'';\n console.log(''๐Ÿ“น Testing direct AVFoundation path (macOS 14 simulation)...'');\n console.log(''Expected logs:'');\n console.log('' ๐ŸŽฏ macOS 14 detected - directly using AVFoundation'');\n console.log('' โญ๏ธ Skipping ScreenCaptureKit completely, jumping to AVFoundation'');\n console.log('' ๐ŸŽฌ AVFoundation: Starting recording initialization'');\n console.log('''');\n \n const success = await recorder.startRecording(outputPath, {\n captureCursor: true,\n includeMicrophone: false,\n includeSystemAudio: true\n });\n \n if (success) {\n console.log(''โœ… SUCCESS: Direct AVFoundation path works!'');\n await new Promise(resolve => setTimeout(resolve, 3000));\n \n await recorder.stopRecording();\n console.log(''โœ… Recording completed successfully!'');\n console.log('''');\n console.log(''๐ŸŽ‰ This is exactly how macOS 14 will work:'');\n console.log(''โœ… No ScreenCaptureKit attempts'');\n console.log(''โœ… Direct AVFoundation usage'');\n console.log(''โœ… No permission errors'');\n } else {\n console.log(''โŒ Recording failed'');\n }\n } catch (error) {\n console.log(''โŒ Error:'', error.message);\n }\n}\n\ntestDirectAVFoundation();\n\")"
4
+ "Bash(FORCE_AVFOUNDATION=1 node -e \"const MacRecorder = require(''./index.js''); const recorder = new MacRecorder(); recorder.checkPermissions().then(result => console.log(''Permission result (macOS 14 simulation):'', result))\")"
53
5
  ],
54
- "deny": []
6
+ "deny": [],
7
+ "ask": []
55
8
  }
56
9
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-mac-recorder",
3
- "version": "2.16.6",
3
+ "version": "2.16.8",
4
4
  "description": "Native macOS screen recording package for Node.js applications",
5
5
  "main": "index.js",
6
6
  "keywords": [
@@ -17,14 +17,14 @@ static int64_t g_avFrameNumber = 0;
17
17
  static CMTime g_avStartTime;
18
18
 
19
19
  // AVFoundation screen recording implementation
20
- bool startAVFoundationRecording(const std::string& outputPath,
21
- CGDirectDisplayID displayID,
22
- uint32_t windowID,
23
- CGRect captureRect,
24
- bool captureCursor,
25
- bool includeMicrophone,
26
- bool includeSystemAudio,
27
- NSString* audioDeviceId) {
20
+ extern "C" bool startAVFoundationRecording(const std::string& outputPath,
21
+ CGDirectDisplayID displayID,
22
+ uint32_t windowID,
23
+ CGRect captureRect,
24
+ bool captureCursor,
25
+ bool includeMicrophone,
26
+ bool includeSystemAudio,
27
+ NSString* audioDeviceId) {
28
28
 
29
29
  if (g_avIsRecording) {
30
30
  NSLog(@"โŒ AVFoundation recording already in progress");
@@ -36,10 +36,7 @@ bool startAVFoundationRecording(const std::string& outputPath,
36
36
 
37
37
  // Create output URL
38
38
  NSString *outputPathStr = [NSString stringWithUTF8String:outputPath.c_str()];
39
- NSLog(@"๐ŸŽฌ AVFoundation: Output path string: %@", outputPathStr);
40
-
41
39
  NSURL *outputURL = [NSURL fileURLWithPath:outputPathStr];
42
- NSLog(@"๐ŸŽฌ AVFoundation: Output URL: %@", outputURL);
43
40
 
44
41
  // Remove existing file
45
42
  NSError *removeError = nil;
@@ -49,23 +46,16 @@ bool startAVFoundationRecording(const std::string& outputPath,
49
46
  }
50
47
 
51
48
  // Create asset writer
52
- NSLog(@"๐ŸŽฌ AVFoundation: Creating AVAssetWriter...");
53
49
  NSError *error = nil;
54
50
  g_avWriter = [[AVAssetWriter alloc] initWithURL:outputURL fileType:AVFileTypeQuickTimeMovie error:&error];
55
51
  if (!g_avWriter || error) {
56
52
  NSLog(@"โŒ AVFoundation: Failed to create AVAssetWriter: %@", error);
57
- NSLog(@"โŒ AVFoundation: Output URL was: %@", outputURL);
58
53
  return false;
59
54
  }
60
- NSLog(@"โœ… AVFoundation: AVAssetWriter created successfully");
61
55
 
62
56
  // Get display dimensions
63
- NSLog(@"๐ŸŽฌ AVFoundation: Getting display dimensions for display ID %u", displayID);
64
57
  CGRect displayBounds = CGDisplayBounds(displayID);
65
- NSLog(@"๐ŸŽฌ AVFoundation: Display bounds: %.0f x %.0f", displayBounds.size.width, displayBounds.size.height);
66
-
67
58
  CGSize recordingSize = captureRect.size.width > 0 ? captureRect.size : displayBounds.size;
68
- NSLog(@"๐ŸŽฌ AVFoundation: Recording size: %.0f x %.0f", recordingSize.width, recordingSize.height);
69
59
 
70
60
  // Video settings
71
61
  NSDictionary *videoSettings = @{
@@ -118,6 +108,11 @@ bool startAVFoundationRecording(const std::string& outputPath,
118
108
  dispatch_queue_t captureQueue = dispatch_queue_create("AVFoundationCaptureQueue", DISPATCH_QUEUE_SERIAL);
119
109
  g_avTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, captureQueue);
120
110
 
111
+ if (!g_avTimer) {
112
+ NSLog(@"โŒ Failed to create dispatch timer");
113
+ return false;
114
+ }
115
+
121
116
  uint64_t interval = NSEC_PER_SEC / 15; // 15 FPS
122
117
  dispatch_source_set_timer(g_avTimer, dispatch_time(DISPATCH_TIME_NOW, 0), interval, interval / 10);
123
118
 
@@ -191,7 +186,7 @@ bool startAVFoundationRecording(const std::string& outputPath,
191
186
  }
192
187
  }
193
188
 
194
- bool stopAVFoundationRecording() {
189
+ extern "C" bool stopAVFoundationRecording() {
195
190
  if (!g_avIsRecording) {
196
191
  return true;
197
192
  }
@@ -233,6 +228,6 @@ bool stopAVFoundationRecording() {
233
228
  }
234
229
  }
235
230
 
236
- bool isAVFoundationRecording() {
231
+ extern "C" bool isAVFoundationRecording() {
237
232
  return g_avIsRecording;
238
233
  }
@@ -205,9 +205,9 @@ Napi::Value StartRecording(const Napi::CallbackInfo& info) {
205
205
  NSLog(@"๐Ÿ”ง FORCE_AVFOUNDATION environment variable detected - skipping ScreenCaptureKit");
206
206
  }
207
207
 
208
- // Use ScreenCaptureKit only on macOS 15+ for maximum stability
209
- // macOS 14/13 should skip ScreenCaptureKit completely and use AVFoundation
210
- if (@available(macOS 12.3, *) && isM15Plus && !forceAVFoundation) {
208
+ // ONLY use ScreenCaptureKit on macOS 15+
209
+ // macOS 14/13 ALWAYS use AVFoundation - NO ScreenCaptureKit attempts
210
+ if (isM15Plus && !forceAVFoundation && !isElectron) {
211
211
  NSLog(@"โœ… macOS 15+ detected - ScreenCaptureKit available with full compatibility");
212
212
 
213
213
  // Try ScreenCaptureKit with extensive safety measures
@@ -270,60 +270,43 @@ Napi::Value StartRecording(const Napi::CallbackInfo& info) {
270
270
  NSLog(@"โŒ Exception during ScreenCaptureKit startup: %@", sckException.reason);
271
271
  }
272
272
 
273
- NSLog(@"โŒ ScreenCaptureKit failed or unsafe");
273
+ NSLog(@"โŒ ScreenCaptureKit failed or unsafe - will fallback to AVFoundation");
274
274
 
275
275
  } else {
276
- NSLog(@"โŒ ScreenCaptureKit availability check failed");
277
- NSLog(@"โŒ ScreenCaptureKit not available");
276
+ NSLog(@"โŒ ScreenCaptureKit availability check failed - will fallback to AVFoundation");
278
277
  }
279
278
  } @catch (NSException *availabilityException) {
280
- NSLog(@"โŒ Exception during ScreenCaptureKit availability check: %@", availabilityException.reason);
281
- return Napi::Boolean::New(env, false);
279
+ NSLog(@"โŒ Exception during ScreenCaptureKit availability check - will fallback to AVFoundation: %@", availabilityException.reason);
282
280
  }
283
- } else if (isM14Plus && !isElectron) {
284
- // macOS 14 - directly use AVFoundation, skip all ScreenCaptureKit logic
285
- NSLog(@"๐ŸŽฏ macOS 14 detected - directly using AVFoundation for better compatibility");
286
- NSLog(@"โญ๏ธ Skipping ScreenCaptureKit completely, jumping to AVFoundation");
287
- goto useAVFoundation;
288
- } else if (isM13Plus && !isElectron) {
289
- // macOS 13 - directly use AVFoundation, skip all ScreenCaptureKit logic
290
- NSLog(@"๐ŸŽฏ macOS 13 detected - directly using AVFoundation (limited features)");
291
- NSLog(@"โญ๏ธ Skipping ScreenCaptureKit completely, jumping to AVFoundation");
292
- goto useAVFoundation;
281
+
282
+ // If we reach here, ScreenCaptureKit failed, so fall through to AVFoundation
283
+ NSLog(@"โญ๏ธ ScreenCaptureKit failed - falling back to AVFoundation");
293
284
  } else {
294
- NSLog(@"โŒ macOS version too old (< 13.0) or Electron environment - Recording not supported");
295
- return Napi::Boolean::New(env, false);
296
- }
297
-
298
- // AVFoundation fallback logic
299
- useAVFoundation:
300
- if (isElectron) {
301
- NSLog(@"โŒ ScreenCaptureKit failed in Electron - AVFoundation disabled for stability");
302
- NSLog(@"โŒ Recording not available in Electron when ScreenCaptureKit fails");
303
- return Napi::Boolean::New(env, false);
285
+ // macOS 14/13 or forced AVFoundation - ALWAYS use AVFoundation
286
+ if (isElectron) {
287
+ NSLog(@"โŒ Electron environment - Recording not supported");
288
+ return Napi::Boolean::New(env, false);
289
+ }
290
+
291
+ if (isM15Plus) {
292
+ NSLog(@"๐ŸŽฏ macOS 15+ with FORCE_AVFOUNDATION - using AVFoundation");
293
+ } else if (isM14Plus) {
294
+ NSLog(@"๐ŸŽฏ macOS 14 detected - using AVFoundation (primary method)");
295
+ } else if (isM13Plus) {
296
+ NSLog(@"๐ŸŽฏ macOS 13 detected - using AVFoundation (limited features)");
297
+ } else {
298
+ NSLog(@"โŒ macOS version too old (< 13.0) - Not supported");
299
+ return Napi::Boolean::New(env, false);
300
+ }
301
+
302
+ // DIRECT AVFoundation - NO fallback logic needed
303
+ NSLog(@"โญ๏ธ Using AVFoundation directly - no ScreenCaptureKit attempts");
304
304
  }
305
305
 
306
- // Try AVFoundation fallback (ScreenCaptureKit failed or macOS 13/14)
307
- if (isM15Plus) {
308
- NSLog(@"๐Ÿ”„ ScreenCaptureKit failed on macOS 15+ - attempting AVFoundation fallback");
309
- } else if (isM14Plus) {
310
- NSLog(@"๐ŸŽฅ Using AVFoundation for macOS 14 compatibility (primary method)");
311
- } else if (isM13Plus) {
312
- NSLog(@"๐ŸŽฅ Using AVFoundation for macOS 13 compatibility (primary method, limited features)");
313
- } else {
314
- NSLog(@"โŒ Unsupported macOS version for AVFoundation");
315
- return Napi::Boolean::New(env, false);
316
- }
306
+ // AVFoundation recording (either fallback from ScreenCaptureKit or direct)
307
+ NSLog(@"๐ŸŽฅ Starting AVFoundation recording...");
317
308
 
318
309
  @try {
319
- NSLog(@"๐Ÿ”ง Attempting AVFoundation recording...");
320
- NSLog(@"๐Ÿ”ง Output path: %s", outputPath.c_str());
321
- NSLog(@"๐Ÿ”ง Display ID: %u", displayID);
322
- NSLog(@"๐Ÿ”ง Cursor: %s, Mic: %s, System Audio: %s",
323
- captureCursor ? "YES" : "NO",
324
- includeMicrophone ? "YES" : "NO",
325
- includeSystemAudio ? "YES" : "NO");
326
-
327
310
  // Import AVFoundation recording functions (if available)
328
311
  extern bool startAVFoundationRecording(const std::string& outputPath,
329
312
  CGDirectDisplayID displayID,
@@ -334,13 +317,11 @@ Napi::Value StartRecording(const Napi::CallbackInfo& info) {
334
317
  bool includeSystemAudio,
335
318
  NSString* audioDeviceId);
336
319
 
337
- NSLog(@"๐Ÿ”ง Calling startAVFoundationRecording...");
338
320
  bool avResult = startAVFoundationRecording(outputPath, displayID, windowID, captureRect,
339
321
  captureCursor, includeMicrophone, includeSystemAudio, audioDeviceId);
340
- NSLog(@"๐Ÿ”ง AVFoundation result: %s", avResult ? "SUCCESS" : "FAILED");
341
322
 
342
323
  if (avResult) {
343
- NSLog(@"๐ŸŽฅ RECORDING METHOD: AVFoundation (Fallback)");
324
+ NSLog(@"๐ŸŽฅ RECORDING METHOD: AVFoundation");
344
325
  NSLog(@"โœ… AVFoundation recording started successfully");
345
326
  g_isRecording = true;
346
327
  return Napi::Boolean::New(env, true);
@@ -929,40 +910,93 @@ Napi::Value CheckPermissions(const Napi::CallbackInfo& info) {
929
910
  Napi::Env env = info.Env();
930
911
 
931
912
  @try {
913
+ // Determine which framework will be used (same logic as startRecording)
914
+ NSOperatingSystemVersion osVersion = [[NSProcessInfo processInfo] operatingSystemVersion];
915
+ BOOL isM15Plus = (osVersion.majorVersion >= 15);
916
+ BOOL isM14Plus = (osVersion.majorVersion >= 14);
917
+ BOOL isM13Plus = (osVersion.majorVersion >= 13);
918
+
919
+ // Check for force AVFoundation flag
920
+ BOOL forceAVFoundation = (getenv("FORCE_AVFOUNDATION") != NULL);
921
+
922
+ // Electron detection
923
+ BOOL isElectron = (NSBundle.mainBundle.bundleIdentifier &&
924
+ [NSBundle.mainBundle.bundleIdentifier containsString:@"electron"]) ||
925
+ (NSProcessInfo.processInfo.processName &&
926
+ [NSProcessInfo.processInfo.processName containsString:@"Electron"]) ||
927
+ (NSProcessInfo.processInfo.environment[@"ELECTRON_RUN_AS_NODE"] != nil);
928
+
929
+ NSLog(@"๐Ÿ”’ Permission check for macOS %ld.%ld.%ld",
930
+ (long)osVersion.majorVersion, (long)osVersion.minorVersion, (long)osVersion.patchVersion);
931
+
932
+ // Determine which framework will be used
933
+ BOOL willUseScreenCaptureKit = (isM15Plus && !forceAVFoundation && !isElectron);
934
+ BOOL willUseAVFoundation = (!willUseScreenCaptureKit && (isM13Plus || isM14Plus));
935
+
936
+ if (willUseScreenCaptureKit) {
937
+ NSLog(@"๐ŸŽฏ Will use ScreenCaptureKit - checking ScreenCaptureKit permissions");
938
+ } else if (willUseAVFoundation) {
939
+ NSLog(@"๐ŸŽฏ Will use AVFoundation - checking AVFoundation permissions");
940
+ } else {
941
+ NSLog(@"โŒ No compatible recording framework available");
942
+ return Napi::Boolean::New(env, false);
943
+ }
944
+
932
945
  // Check screen recording permission
933
- bool hasScreenPermission = true;
934
-
935
- if (@available(macOS 10.15, *)) {
936
- // Try to create a display stream to test permissions
937
- CGDisplayStreamRef stream = CGDisplayStreamCreate(
938
- CGMainDisplayID(),
939
- 1, 1,
940
- kCVPixelFormatType_32BGRA,
941
- nil,
942
- ^(CGDisplayStreamFrameStatus status, uint64_t displayTime, IOSurfaceRef frameSurface, CGDisplayStreamUpdateRef updateRef) {
943
- // Empty handler
944
- }
945
- );
946
+ bool hasScreenPermission = false;
947
+
948
+ if (@available(macOS 14.0, *)) {
949
+ // macOS 14+ - Use CGRequestScreenCaptureAccess for both frameworks
950
+ hasScreenPermission = CGRequestScreenCaptureAccess();
951
+ NSLog(@"๐Ÿ”’ Screen recording permission: %s", hasScreenPermission ? "GRANTED" : "DENIED");
946
952
 
947
- if (stream) {
948
- CFRelease(stream);
953
+ if (!hasScreenPermission) {
954
+ NSLog(@"โŒ Screen recording permission DENIED");
955
+ NSLog(@"๐Ÿ“ Please go to System Settings > Privacy & Security > Screen Recording");
956
+ NSLog(@"๐Ÿ“ and enable permission for your application");
957
+ }
958
+ } else if (@available(macOS 10.15, *)) {
959
+ // macOS 10.15-13 - Try to create a minimal display image to test permissions
960
+ CGImageRef testImage = CGDisplayCreateImage(CGMainDisplayID());
961
+ if (testImage) {
949
962
  hasScreenPermission = true;
963
+ CGImageRelease(testImage);
964
+ NSLog(@"๐Ÿ”’ Screen recording permission: GRANTED");
950
965
  } else {
951
966
  hasScreenPermission = false;
967
+ NSLog(@"โŒ Screen recording permission: DENIED");
968
+ NSLog(@"๐Ÿ“ Please grant screen recording permission in System Preferences");
952
969
  }
970
+ } else {
971
+ // macOS < 10.15 - No permission system for screen recording
972
+ hasScreenPermission = true;
973
+ NSLog(@"๐Ÿ”’ Screen recording: No permission system (macOS < 10.15)");
953
974
  }
954
975
 
955
- // Check audio permission
976
+ // Check audio permission based on framework
956
977
  bool hasAudioPermission = true;
957
- if (@available(macOS 10.14, *)) {
958
- // Audio permissions handled by ScreenCaptureKit internally
959
- BOOL audioAuthorized = YES; // Assume authorized since SCK handles it
960
- hasAudioPermission = audioAuthorized;
978
+
979
+ if (willUseScreenCaptureKit) {
980
+ // ScreenCaptureKit handles audio permissions internally
981
+ hasAudioPermission = true;
982
+ NSLog(@"๐Ÿ”’ Audio permission: Handled internally by ScreenCaptureKit");
983
+
984
+ } else if (willUseAVFoundation) {
985
+ // For AVFoundation, we don't enforce audio permissions
986
+ // Recording can continue without audio if needed
987
+ hasAudioPermission = true;
988
+ NSLog(@"๐Ÿ”’ Audio permission: Will be requested during recording by AVFoundation");
961
989
  }
962
990
 
991
+ NSLog(@"๐Ÿ”’ Final permission status:");
992
+ NSLog(@" Framework: %s", willUseScreenCaptureKit ? "ScreenCaptureKit" : "AVFoundation");
993
+ NSLog(@" Screen Recording: %s", hasScreenPermission ? "GRANTED" : "DENIED");
994
+ NSLog(@" Audio: %s", hasAudioPermission ? "READY" : "NOT READY");
995
+
963
996
  return Napi::Boolean::New(env, hasScreenPermission && hasAudioPermission);
964
997
 
965
998
  } @catch (NSException *exception) {
999
+ NSLog(@"โŒ Exception in permission check: %@", exception.reason);
966
1000
  return Napi::Boolean::New(env, false);
967
1001
  }
968
1002
  }
package/simple-test.js DELETED
@@ -1,38 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- const WindowSelector = require('./window-selector');
4
-
5
- async function simpleTest() {
6
- const selector = new WindowSelector();
7
-
8
- console.log('๐Ÿ” Starting simple window selector test...');
9
- console.log('Move your cursor around - you should see overlay highlighting windows');
10
- console.log('Press Ctrl+C to exit\n');
11
-
12
- try {
13
- selector.on('windowEntered', (window) => {
14
- console.log(`โžก๏ธ Entered: ${window.appName} - "${window.title}"`);
15
- });
16
-
17
- selector.on('windowLeft', (window) => {
18
- console.log(`โฌ…๏ธ Left: ${window.appName} - "${window.title}"`);
19
- });
20
-
21
- await selector.startSelection();
22
-
23
- // Keep running until Ctrl+C
24
- process.on('SIGINT', async () => {
25
- console.log('\n๐Ÿ›‘ Stopping...');
26
- await selector.cleanup();
27
- process.exit(0);
28
- });
29
-
30
- // Prevent the process from exiting
31
- setInterval(() => {}, 1000);
32
-
33
- } catch (error) {
34
- console.error('โŒ Error:', error);
35
- }
36
- }
37
-
38
- simpleTest();