node-mac-recorder 2.16.7 โ†’ 2.16.9

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,57 +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\")",
53
- "Bash(FORCE_AVFOUNDATION=1 node -e \"\nconsole.log(''๐Ÿ”ฅ FINAL TEST: macOS 14 Direct AVFoundation (NO ScreenCaptureKit)'');\nconst MacRecorder = require(''./index'');\nconst recorder = new MacRecorder();\n\nasync function finalTest() {\n try {\n const outputPath = ''./test-output/final-macos14-test.mov'';\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: macOS 14 AVFoundation works!'');\n await new Promise(resolve => setTimeout(resolve, 2000));\n await recorder.stopRecording();\n console.log(''โœ… Test completed - macOS 14 is FIXED!'');\n } else {\n console.log(''โŒ Still failed'');\n }\n } catch (error) {\n console.log(''โŒ Error:'', error.message);\n }\n}\n\nfinalTest();\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))\")"
54
5
  ],
55
- "deny": []
6
+ "deny": [],
7
+ "ask": []
56
8
  }
57
9
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-mac-recorder",
3
- "version": "2.16.7",
3
+ "version": "2.16.9",
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
  }
@@ -74,7 +74,13 @@ Napi::Value StartRecording(const Napi::CallbackInfo& info) {
74
74
  return env.Null();
75
75
  }
76
76
 
77
+ // IMPORTANT: Clean up any stale recording state before starting
78
+ // This fixes the issue where macOS 14/13 users get "recording already in progress"
79
+ NSLog(@"๐Ÿงน Cleaning up any previous recording state...");
80
+ cleanupRecording();
81
+
77
82
  if (g_isRecording) {
83
+ NSLog(@"โš ๏ธ Still recording after cleanup - forcing stop");
78
84
  return Napi::Boolean::New(env, false);
79
85
  }
80
86
 
@@ -270,24 +276,27 @@ Napi::Value StartRecording(const Napi::CallbackInfo& info) {
270
276
  NSLog(@"โŒ Exception during ScreenCaptureKit startup: %@", sckException.reason);
271
277
  }
272
278
 
273
- NSLog(@"โŒ ScreenCaptureKit failed or unsafe");
279
+ NSLog(@"โŒ ScreenCaptureKit failed or unsafe - will fallback to AVFoundation");
274
280
 
275
281
  } else {
276
- NSLog(@"โŒ ScreenCaptureKit availability check failed");
277
- NSLog(@"โŒ ScreenCaptureKit not available");
282
+ NSLog(@"โŒ ScreenCaptureKit availability check failed - will fallback to AVFoundation");
278
283
  }
279
284
  } @catch (NSException *availabilityException) {
280
- NSLog(@"โŒ Exception during ScreenCaptureKit availability check: %@", availabilityException.reason);
281
- return Napi::Boolean::New(env, false);
285
+ NSLog(@"โŒ Exception during ScreenCaptureKit availability check - will fallback to AVFoundation: %@", availabilityException.reason);
282
286
  }
287
+
288
+ // If we reach here, ScreenCaptureKit failed, so fall through to AVFoundation
289
+ NSLog(@"โญ๏ธ ScreenCaptureKit failed - falling back to AVFoundation");
283
290
  } else {
284
- // macOS 14/13 or not macOS 15+ - ALWAYS use AVFoundation
291
+ // macOS 14/13 or forced AVFoundation - ALWAYS use AVFoundation
285
292
  if (isElectron) {
286
293
  NSLog(@"โŒ Electron environment - Recording not supported");
287
294
  return Napi::Boolean::New(env, false);
288
295
  }
289
296
 
290
- if (isM14Plus) {
297
+ if (isM15Plus) {
298
+ NSLog(@"๐ŸŽฏ macOS 15+ with FORCE_AVFOUNDATION - using AVFoundation");
299
+ } else if (isM14Plus) {
291
300
  NSLog(@"๐ŸŽฏ macOS 14 detected - using AVFoundation (primary method)");
292
301
  } else if (isM13Plus) {
293
302
  NSLog(@"๐ŸŽฏ macOS 13 detected - using AVFoundation (limited features)");
@@ -296,23 +305,14 @@ Napi::Value StartRecording(const Napi::CallbackInfo& info) {
296
305
  return Napi::Boolean::New(env, false);
297
306
  }
298
307
 
299
- // DIRECT AVFoundation - NO fallback logic
308
+ // DIRECT AVFoundation - NO fallback logic needed
300
309
  NSLog(@"โญ๏ธ Using AVFoundation directly - no ScreenCaptureKit attempts");
301
310
  }
302
311
 
303
312
  // AVFoundation recording (either fallback from ScreenCaptureKit or direct)
304
- useAVFoundation:
305
313
  NSLog(@"๐ŸŽฅ Starting AVFoundation recording...");
306
314
 
307
315
  @try {
308
- NSLog(@"๐Ÿ”ง Attempting AVFoundation recording...");
309
- NSLog(@"๐Ÿ”ง Output path: %s", outputPath.c_str());
310
- NSLog(@"๐Ÿ”ง Display ID: %u", displayID);
311
- NSLog(@"๐Ÿ”ง Cursor: %s, Mic: %s, System Audio: %s",
312
- captureCursor ? "YES" : "NO",
313
- includeMicrophone ? "YES" : "NO",
314
- includeSystemAudio ? "YES" : "NO");
315
-
316
316
  // Import AVFoundation recording functions (if available)
317
317
  extern bool startAVFoundationRecording(const std::string& outputPath,
318
318
  CGDirectDisplayID displayID,
@@ -323,13 +323,11 @@ Napi::Value StartRecording(const Napi::CallbackInfo& info) {
323
323
  bool includeSystemAudio,
324
324
  NSString* audioDeviceId);
325
325
 
326
- NSLog(@"๐Ÿ”ง Calling startAVFoundationRecording...");
327
326
  bool avResult = startAVFoundationRecording(outputPath, displayID, windowID, captureRect,
328
327
  captureCursor, includeMicrophone, includeSystemAudio, audioDeviceId);
329
- NSLog(@"๐Ÿ”ง AVFoundation result: %s", avResult ? "SUCCESS" : "FAILED");
330
328
 
331
329
  if (avResult) {
332
- NSLog(@"๐ŸŽฅ RECORDING METHOD: AVFoundation (Fallback)");
330
+ NSLog(@"๐ŸŽฅ RECORDING METHOD: AVFoundation");
333
331
  NSLog(@"โœ… AVFoundation recording started successfully");
334
332
  g_isRecording = true;
335
333
  return Napi::Boolean::New(env, true);
@@ -918,40 +916,93 @@ Napi::Value CheckPermissions(const Napi::CallbackInfo& info) {
918
916
  Napi::Env env = info.Env();
919
917
 
920
918
  @try {
919
+ // Determine which framework will be used (same logic as startRecording)
920
+ NSOperatingSystemVersion osVersion = [[NSProcessInfo processInfo] operatingSystemVersion];
921
+ BOOL isM15Plus = (osVersion.majorVersion >= 15);
922
+ BOOL isM14Plus = (osVersion.majorVersion >= 14);
923
+ BOOL isM13Plus = (osVersion.majorVersion >= 13);
924
+
925
+ // Check for force AVFoundation flag
926
+ BOOL forceAVFoundation = (getenv("FORCE_AVFOUNDATION") != NULL);
927
+
928
+ // Electron detection
929
+ BOOL isElectron = (NSBundle.mainBundle.bundleIdentifier &&
930
+ [NSBundle.mainBundle.bundleIdentifier containsString:@"electron"]) ||
931
+ (NSProcessInfo.processInfo.processName &&
932
+ [NSProcessInfo.processInfo.processName containsString:@"Electron"]) ||
933
+ (NSProcessInfo.processInfo.environment[@"ELECTRON_RUN_AS_NODE"] != nil);
934
+
935
+ NSLog(@"๐Ÿ”’ Permission check for macOS %ld.%ld.%ld",
936
+ (long)osVersion.majorVersion, (long)osVersion.minorVersion, (long)osVersion.patchVersion);
937
+
938
+ // Determine which framework will be used
939
+ BOOL willUseScreenCaptureKit = (isM15Plus && !forceAVFoundation && !isElectron);
940
+ BOOL willUseAVFoundation = (!willUseScreenCaptureKit && (isM13Plus || isM14Plus));
941
+
942
+ if (willUseScreenCaptureKit) {
943
+ NSLog(@"๐ŸŽฏ Will use ScreenCaptureKit - checking ScreenCaptureKit permissions");
944
+ } else if (willUseAVFoundation) {
945
+ NSLog(@"๐ŸŽฏ Will use AVFoundation - checking AVFoundation permissions");
946
+ } else {
947
+ NSLog(@"โŒ No compatible recording framework available");
948
+ return Napi::Boolean::New(env, false);
949
+ }
950
+
921
951
  // Check screen recording permission
922
- bool hasScreenPermission = true;
923
-
924
- if (@available(macOS 10.15, *)) {
925
- // Try to create a display stream to test permissions
926
- CGDisplayStreamRef stream = CGDisplayStreamCreate(
927
- CGMainDisplayID(),
928
- 1, 1,
929
- kCVPixelFormatType_32BGRA,
930
- nil,
931
- ^(CGDisplayStreamFrameStatus status, uint64_t displayTime, IOSurfaceRef frameSurface, CGDisplayStreamUpdateRef updateRef) {
932
- // Empty handler
933
- }
934
- );
952
+ bool hasScreenPermission = false;
953
+
954
+ if (@available(macOS 14.0, *)) {
955
+ // macOS 14+ - Use CGRequestScreenCaptureAccess for both frameworks
956
+ hasScreenPermission = CGRequestScreenCaptureAccess();
957
+ NSLog(@"๐Ÿ”’ Screen recording permission: %s", hasScreenPermission ? "GRANTED" : "DENIED");
935
958
 
936
- if (stream) {
937
- CFRelease(stream);
959
+ if (!hasScreenPermission) {
960
+ NSLog(@"โŒ Screen recording permission DENIED");
961
+ NSLog(@"๐Ÿ“ Please go to System Settings > Privacy & Security > Screen Recording");
962
+ NSLog(@"๐Ÿ“ and enable permission for your application");
963
+ }
964
+ } else if (@available(macOS 10.15, *)) {
965
+ // macOS 10.15-13 - Try to create a minimal display image to test permissions
966
+ CGImageRef testImage = CGDisplayCreateImage(CGMainDisplayID());
967
+ if (testImage) {
938
968
  hasScreenPermission = true;
969
+ CGImageRelease(testImage);
970
+ NSLog(@"๐Ÿ”’ Screen recording permission: GRANTED");
939
971
  } else {
940
972
  hasScreenPermission = false;
973
+ NSLog(@"โŒ Screen recording permission: DENIED");
974
+ NSLog(@"๐Ÿ“ Please grant screen recording permission in System Preferences");
941
975
  }
976
+ } else {
977
+ // macOS < 10.15 - No permission system for screen recording
978
+ hasScreenPermission = true;
979
+ NSLog(@"๐Ÿ”’ Screen recording: No permission system (macOS < 10.15)");
942
980
  }
943
981
 
944
- // Check audio permission
982
+ // Check audio permission based on framework
945
983
  bool hasAudioPermission = true;
946
- if (@available(macOS 10.14, *)) {
947
- // Audio permissions handled by ScreenCaptureKit internally
948
- BOOL audioAuthorized = YES; // Assume authorized since SCK handles it
949
- hasAudioPermission = audioAuthorized;
984
+
985
+ if (willUseScreenCaptureKit) {
986
+ // ScreenCaptureKit handles audio permissions internally
987
+ hasAudioPermission = true;
988
+ NSLog(@"๐Ÿ”’ Audio permission: Handled internally by ScreenCaptureKit");
989
+
990
+ } else if (willUseAVFoundation) {
991
+ // For AVFoundation, we don't enforce audio permissions
992
+ // Recording can continue without audio if needed
993
+ hasAudioPermission = true;
994
+ NSLog(@"๐Ÿ”’ Audio permission: Will be requested during recording by AVFoundation");
950
995
  }
951
996
 
997
+ NSLog(@"๐Ÿ”’ Final permission status:");
998
+ NSLog(@" Framework: %s", willUseScreenCaptureKit ? "ScreenCaptureKit" : "AVFoundation");
999
+ NSLog(@" Screen Recording: %s", hasScreenPermission ? "GRANTED" : "DENIED");
1000
+ NSLog(@" Audio: %s", hasAudioPermission ? "READY" : "NOT READY");
1001
+
952
1002
  return Napi::Boolean::New(env, hasScreenPermission && hasAudioPermission);
953
1003
 
954
1004
  } @catch (NSException *exception) {
1005
+ NSLog(@"โŒ Exception in permission check: %@", exception.reason);
955
1006
  return Napi::Boolean::New(env, false);
956
1007
  }
957
1008
  }
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();