node-mac-recorder 1.2.9 → 1.2.11
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.
- package/.claude/settings.local.json +5 -1
- package/README.md +50 -6
- package/debug-audio.js +79 -0
- package/index.js +9 -7
- package/package.json +1 -1
- package/quick-test.js +50 -0
- package/simple-api-example.js +182 -0
- package/src/mac_recorder.mm +87 -6
- package/src/screen_capture_kit.h +15 -0
- package/system-sound-test.js +46 -0
- package/test-system-audio.js +104 -0
- package/usage-examples.js +202 -0
package/README.md
CHANGED
|
@@ -93,6 +93,7 @@ await recorder.startRecording("./recording.mov", {
|
|
|
93
93
|
includeMicrophone: false, // Enable microphone (default: false)
|
|
94
94
|
includeSystemAudio: true, // Enable system audio (default: true)
|
|
95
95
|
audioDeviceId: "device-id", // Specific audio input device (default: system default)
|
|
96
|
+
systemAudioDeviceId: "system-device-id", // Specific system audio device (auto-detected by default)
|
|
96
97
|
|
|
97
98
|
// Display & Window Selection
|
|
98
99
|
displayId: 0, // Display index (null = main display)
|
|
@@ -348,19 +349,62 @@ await new Promise((resolve) => setTimeout(resolve, 8000));
|
|
|
348
349
|
await recorder.stopRecording();
|
|
349
350
|
```
|
|
350
351
|
|
|
351
|
-
###
|
|
352
|
+
### Advanced System Audio Recording
|
|
352
353
|
|
|
353
354
|
```javascript
|
|
354
355
|
const recorder = new MacRecorder();
|
|
355
356
|
|
|
356
|
-
//
|
|
357
|
-
await recorder.
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
357
|
+
// List available audio devices to find system audio devices
|
|
358
|
+
const audioDevices = await recorder.getAudioDevices();
|
|
359
|
+
console.log("Available audio devices:");
|
|
360
|
+
audioDevices.forEach((device, i) => {
|
|
361
|
+
console.log(`${i + 1}. ${device.name} (ID: ${device.id})`);
|
|
361
362
|
});
|
|
363
|
+
|
|
364
|
+
// Find system audio device (like BlackHole, Soundflower, etc.)
|
|
365
|
+
const systemAudioDevice = audioDevices.find(device =>
|
|
366
|
+
device.name.toLowerCase().includes('blackhole') ||
|
|
367
|
+
device.name.toLowerCase().includes('soundflower') ||
|
|
368
|
+
device.name.toLowerCase().includes('loopback') ||
|
|
369
|
+
device.name.toLowerCase().includes('aggregate')
|
|
370
|
+
);
|
|
371
|
+
|
|
372
|
+
if (systemAudioDevice) {
|
|
373
|
+
console.log(`Using system audio device: ${systemAudioDevice.name}`);
|
|
374
|
+
|
|
375
|
+
// Record with specific system audio device
|
|
376
|
+
await recorder.startRecording("./system-audio-specific.mov", {
|
|
377
|
+
includeMicrophone: false,
|
|
378
|
+
includeSystemAudio: true,
|
|
379
|
+
systemAudioDeviceId: systemAudioDevice.id, // Specify exact device
|
|
380
|
+
captureArea: { x: 0, y: 0, width: 1, height: 1 }, // Minimal video
|
|
381
|
+
});
|
|
382
|
+
} else {
|
|
383
|
+
console.log("No system audio device found. Installing BlackHole or Soundflower recommended.");
|
|
384
|
+
|
|
385
|
+
// Record with default system audio capture (may not work without virtual audio device)
|
|
386
|
+
await recorder.startRecording("./system-audio-default.mov", {
|
|
387
|
+
includeMicrophone: false,
|
|
388
|
+
includeSystemAudio: true, // Auto-detect system audio device
|
|
389
|
+
captureArea: { x: 0, y: 0, width: 1, height: 1 },
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// Record for 10 seconds
|
|
394
|
+
await new Promise(resolve => setTimeout(resolve, 10000));
|
|
395
|
+
await recorder.stopRecording();
|
|
362
396
|
```
|
|
363
397
|
|
|
398
|
+
**System Audio Setup:**
|
|
399
|
+
|
|
400
|
+
For reliable system audio capture, install a virtual audio device:
|
|
401
|
+
|
|
402
|
+
1. **BlackHole** (Free): https://github.com/ExistentialAudio/BlackHole
|
|
403
|
+
2. **Soundflower** (Free): https://github.com/mattingalls/Soundflower
|
|
404
|
+
3. **Loopback** (Paid): https://rogueamoeba.com/loopback/
|
|
405
|
+
|
|
406
|
+
These create aggregate audio devices that the package can detect and use for system audio capture.
|
|
407
|
+
|
|
364
408
|
### Event-Driven Recording
|
|
365
409
|
|
|
366
410
|
```javascript
|
package/debug-audio.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
const MacRecorder = require('./index');
|
|
2
|
+
|
|
3
|
+
async function debugAudio() {
|
|
4
|
+
const recorder = new MacRecorder();
|
|
5
|
+
|
|
6
|
+
console.log('🔍 SES CİHAZI DEBUG RAPORU\n');
|
|
7
|
+
|
|
8
|
+
try {
|
|
9
|
+
// Tüm ses cihazlarını detayları ile listele
|
|
10
|
+
const devices = await recorder.getAudioDevices();
|
|
11
|
+
console.log('📋 TÜM SES CİHAZLARI:');
|
|
12
|
+
devices.forEach((device, index) => {
|
|
13
|
+
console.log(`${index + 1}. ${device.name}`);
|
|
14
|
+
console.log(` ID: ${device.id || 'N/A'}`);
|
|
15
|
+
console.log(` Type: ${device.type || 'N/A'}`);
|
|
16
|
+
console.log(` Manufacturer: ${device.manufacturer || 'N/A'}`);
|
|
17
|
+
console.log(` Default: ${device.isDefault ? 'Yes' : 'No'}`);
|
|
18
|
+
console.log('');
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// Sistem sesi için uygun cihazları bul
|
|
22
|
+
const systemDevices = devices.filter(device => {
|
|
23
|
+
const name = device.name.toLowerCase();
|
|
24
|
+
return name.includes('aggregate') ||
|
|
25
|
+
name.includes('blackhole') ||
|
|
26
|
+
name.includes('soundflower') ||
|
|
27
|
+
name.includes('loopback') ||
|
|
28
|
+
name.includes('system') ||
|
|
29
|
+
name.includes('imobie');
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
console.log('🎵 SİSTEM SESİ İÇİN UYGUN CİHAZLAR:');
|
|
33
|
+
if (systemDevices.length > 0) {
|
|
34
|
+
systemDevices.forEach((device, index) => {
|
|
35
|
+
console.log(`${index + 1}. ${device.name} (ID: ${device.id})`);
|
|
36
|
+
});
|
|
37
|
+
} else {
|
|
38
|
+
console.log('❌ Sistem sesi cihazı bulunamadı!');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
console.log('\n💡 ÇÖZÜMLERİ:');
|
|
42
|
+
console.log('1. BlackHole kur: https://github.com/ExistentialAudio/BlackHole/releases');
|
|
43
|
+
console.log('2. Audio MIDI Setup ile Aggregate Device oluştur');
|
|
44
|
+
console.log('3. Sistem sesini aggregate device\'a yönlendir');
|
|
45
|
+
|
|
46
|
+
console.log('\n🔧 MANUAL AGGREGATE DEVICE OLUŞTURMA:');
|
|
47
|
+
console.log('1. Spotlight\'ta "Audio MIDI Setup" ara ve aç');
|
|
48
|
+
console.log('2. Sol alt köşedeki "+" butonuna tıkla');
|
|
49
|
+
console.log('3. "Create Aggregate Device" seç');
|
|
50
|
+
console.log('4. Hem built-in output hem de built-in input\'u seç');
|
|
51
|
+
console.log('5. İsim ver (örn: "System Audio Capture")');
|
|
52
|
+
console.log('6. System Preferences > Sound > Output\'ta yeni cihazı seç');
|
|
53
|
+
|
|
54
|
+
// Test kayıt yap
|
|
55
|
+
console.log('\n🧪 TEST KAYIT YAPILIYOR...');
|
|
56
|
+
console.log('🎵 Şimdi müzik çal veya YouTube video aç!');
|
|
57
|
+
|
|
58
|
+
const testDevice = systemDevices[0]; // İlk sistem ses cihazını kullan
|
|
59
|
+
|
|
60
|
+
await recorder.startRecording('./test-output/debug-audio.mov', {
|
|
61
|
+
includeSystemAudio: true,
|
|
62
|
+
includeMicrophone: false,
|
|
63
|
+
systemAudioDeviceId: testDevice?.id,
|
|
64
|
+
captureArea: { x: 0, y: 0, width: 300, height: 200 }
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// 3 saniye kayıt
|
|
68
|
+
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
69
|
+
await recorder.stopRecording();
|
|
70
|
+
|
|
71
|
+
console.log('✅ Test kayıt tamamlandı: ./test-output/debug-audio.mov');
|
|
72
|
+
console.log('🔍 Dosyayı QuickTime Player ile açıp ses kontrolü yap');
|
|
73
|
+
|
|
74
|
+
} catch (error) {
|
|
75
|
+
console.error('❌ Debug hatası:', error.message);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
debugAudio();
|
package/index.js
CHANGED
|
@@ -111,11 +111,12 @@ class MacRecorder extends EventEmitter {
|
|
|
111
111
|
setOptions(options = {}) {
|
|
112
112
|
this.options = {
|
|
113
113
|
includeMicrophone: options.includeMicrophone || false,
|
|
114
|
-
includeSystemAudio: options.includeSystemAudio !== false, // Default true
|
|
114
|
+
includeSystemAudio: options.includeSystemAudio !== false, // Default true unless explicitly disabled
|
|
115
115
|
captureCursor: options.captureCursor || false,
|
|
116
116
|
displayId: options.displayId || null, // null = ana ekran
|
|
117
117
|
windowId: options.windowId || null, // null = tam ekran
|
|
118
118
|
audioDeviceId: options.audioDeviceId || null, // null = default device
|
|
119
|
+
systemAudioDeviceId: options.systemAudioDeviceId || null, // null = auto-detect system audio device
|
|
119
120
|
captureArea: options.captureArea || null,
|
|
120
121
|
};
|
|
121
122
|
}
|
|
@@ -163,7 +164,7 @@ class MacRecorder extends EventEmitter {
|
|
|
163
164
|
targetWindow.y >= display.y &&
|
|
164
165
|
targetWindow.y < display.y + displayHeight
|
|
165
166
|
) {
|
|
166
|
-
targetDisplayId =
|
|
167
|
+
targetDisplayId = display.id; // Use actual display ID, not array index
|
|
167
168
|
// Koordinatları display'e göre normalize et
|
|
168
169
|
adjustedX = targetWindow.x - display.x;
|
|
169
170
|
adjustedY = targetWindow.y - display.y;
|
|
@@ -175,7 +176,7 @@ class MacRecorder extends EventEmitter {
|
|
|
175
176
|
if (targetDisplayId === null) {
|
|
176
177
|
const mainDisplay = displays.find((d) => d.x === 0 && d.y === 0);
|
|
177
178
|
if (mainDisplay) {
|
|
178
|
-
targetDisplayId =
|
|
179
|
+
targetDisplayId = mainDisplay.id; // Use actual display ID, not array index
|
|
179
180
|
adjustedX = Math.max(
|
|
180
181
|
0,
|
|
181
182
|
Math.min(
|
|
@@ -200,7 +201,7 @@ class MacRecorder extends EventEmitter {
|
|
|
200
201
|
this.options.displayId = targetDisplayId;
|
|
201
202
|
|
|
202
203
|
// Recording için display bilgisini sakla (cursor capture için)
|
|
203
|
-
const targetDisplay = displays
|
|
204
|
+
const targetDisplay = displays.find(d => d.id === targetDisplayId);
|
|
204
205
|
this.recordingDisplayInfo = {
|
|
205
206
|
displayId: targetDisplayId,
|
|
206
207
|
x: targetDisplay.x,
|
|
@@ -233,8 +234,8 @@ class MacRecorder extends EventEmitter {
|
|
|
233
234
|
if (this.options.displayId !== null && !this.recordingDisplayInfo) {
|
|
234
235
|
try {
|
|
235
236
|
const displays = await this.getDisplays();
|
|
236
|
-
|
|
237
|
-
|
|
237
|
+
const targetDisplay = displays.find(d => d.id === this.options.displayId);
|
|
238
|
+
if (targetDisplay) {
|
|
238
239
|
this.recordingDisplayInfo = {
|
|
239
240
|
displayId: this.options.displayId,
|
|
240
241
|
x: targetDisplay.x,
|
|
@@ -261,11 +262,12 @@ class MacRecorder extends EventEmitter {
|
|
|
261
262
|
// Native kayıt başlat
|
|
262
263
|
const recordingOptions = {
|
|
263
264
|
includeMicrophone: this.options.includeMicrophone || false,
|
|
264
|
-
includeSystemAudio: this.options.includeSystemAudio !== false, // Default true
|
|
265
|
+
includeSystemAudio: this.options.includeSystemAudio !== false, // Default true unless explicitly disabled
|
|
265
266
|
captureCursor: this.options.captureCursor || false,
|
|
266
267
|
displayId: this.options.displayId || null, // null = ana ekran
|
|
267
268
|
windowId: this.options.windowId || null, // null = tam ekran
|
|
268
269
|
audioDeviceId: this.options.audioDeviceId || null, // null = default device
|
|
270
|
+
systemAudioDeviceId: this.options.systemAudioDeviceId || null, // null = auto-detect system audio device
|
|
269
271
|
};
|
|
270
272
|
|
|
271
273
|
// Manuel captureArea varsa onu kullan
|
package/package.json
CHANGED
package/quick-test.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
const MacRecorder = require('./index');
|
|
2
|
+
|
|
3
|
+
async function quickTest() {
|
|
4
|
+
const recorder = new MacRecorder();
|
|
5
|
+
|
|
6
|
+
console.log('🚀 Hızlı Sistem Sesi Testi\n');
|
|
7
|
+
|
|
8
|
+
try {
|
|
9
|
+
// Ses cihazlarını listele
|
|
10
|
+
const devices = await recorder.getAudioDevices();
|
|
11
|
+
console.log('🎤 Mevcut ses cihazları:');
|
|
12
|
+
devices.forEach((d, i) => console.log(`${i+1}. ${d.name}`));
|
|
13
|
+
|
|
14
|
+
// Sistem sesi cihazı var mı?
|
|
15
|
+
const sysDevice = devices.find(d =>
|
|
16
|
+
d.name.toLowerCase().includes('aggregate') ||
|
|
17
|
+
d.name.toLowerCase().includes('blackhole') ||
|
|
18
|
+
d.name.toLowerCase().includes('soundflower')
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
if (sysDevice) {
|
|
22
|
+
console.log(`\n✅ Sistem sesi cihazı bulundu: ${sysDevice.name}`);
|
|
23
|
+
console.log('🎵 Sistem sesi yakalama çalışmalı');
|
|
24
|
+
} else {
|
|
25
|
+
console.log('\n⚠️ Sistem sesi cihazı yok');
|
|
26
|
+
console.log('💡 BlackHole veya Soundflower yüklemen gerekiyor');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Kısa test kayıt
|
|
30
|
+
console.log('\n🔴 2 saniyelik test kayıt başlıyor...');
|
|
31
|
+
console.log('🎵 Şimdi müzik çal!');
|
|
32
|
+
|
|
33
|
+
await recorder.startRecording('./test-output/quick-test.mov', {
|
|
34
|
+
includeSystemAudio: true,
|
|
35
|
+
includeMicrophone: false,
|
|
36
|
+
systemAudioDeviceId: sysDevice?.id,
|
|
37
|
+
captureArea: { x: 0, y: 0, width: 200, height: 150 }
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
41
|
+
await recorder.stopRecording();
|
|
42
|
+
|
|
43
|
+
console.log('✅ Test tamamlandı! ./test-output/quick-test.mov dosyasını kontrol et');
|
|
44
|
+
|
|
45
|
+
} catch (error) {
|
|
46
|
+
console.error('❌ Hata:', error.message);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
quickTest();
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
// ===== SENİN UYGULAMANDA NASIL KULLANACAĞIN =====
|
|
2
|
+
|
|
3
|
+
const MacRecorder = require('node-mac-recorder'); // npm install node-mac-recorder
|
|
4
|
+
|
|
5
|
+
async function myAppRecording() {
|
|
6
|
+
const recorder = new MacRecorder();
|
|
7
|
+
|
|
8
|
+
// === 1. KULLANICIYA SES CİHAZLARINI GÖSTER ===
|
|
9
|
+
const devices = await recorder.getAudioDevices();
|
|
10
|
+
console.log('🎤 Ses cihazları:');
|
|
11
|
+
devices.forEach((d, i) => console.log(`${i+1}. ${d.name}`));
|
|
12
|
+
|
|
13
|
+
// === 2. SİSTEM SESİ CİHAZINI BUL ===
|
|
14
|
+
const systemDevice = devices.find(d =>
|
|
15
|
+
d.name.includes('Aggregate') ||
|
|
16
|
+
d.name.includes('iMobie') ||
|
|
17
|
+
d.name.includes('BlackHole')
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
if (!systemDevice) {
|
|
21
|
+
console.log('⚠️ Sistem ses cihazı yok. Sadece mikrofon kullanılacak.');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// === 3. KULLANICI TERCİHLERİ ===
|
|
25
|
+
const userPrefs = {
|
|
26
|
+
recordSystemAudio: true, // Kullanıcının sistem sesi tercihi
|
|
27
|
+
recordMicrophone: false, // Kullanıcının mikrofon tercihi
|
|
28
|
+
outputPath: './my-recording.mov'
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// === 4. KAYIT BAŞLAT ===
|
|
32
|
+
const options = {
|
|
33
|
+
// Sistem sesi
|
|
34
|
+
includeSystemAudio: userPrefs.recordSystemAudio,
|
|
35
|
+
systemAudioDeviceId: systemDevice?.id, // Bulunan cihazı kullan
|
|
36
|
+
|
|
37
|
+
// Mikrofon
|
|
38
|
+
includeMicrophone: userPrefs.recordMicrophone,
|
|
39
|
+
|
|
40
|
+
// Diğer seçenekler
|
|
41
|
+
captureCursor: true,
|
|
42
|
+
quality: 'high'
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
console.log('🔴 Kayıt başlıyor...', options);
|
|
46
|
+
await recorder.startRecording(userPrefs.outputPath, options);
|
|
47
|
+
|
|
48
|
+
// === 5. KAYDI DURDUR ===
|
|
49
|
+
setTimeout(async () => {
|
|
50
|
+
await recorder.stopRecording();
|
|
51
|
+
console.log('✅ Kayıt bitti:', userPrefs.outputPath);
|
|
52
|
+
}, 5000);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// === ELECTRONʼDA KULLANIM ===
|
|
56
|
+
// main.js
|
|
57
|
+
const { ipcMain } = require('electron');
|
|
58
|
+
const MacRecorder = require('node-mac-recorder');
|
|
59
|
+
|
|
60
|
+
const recorder = new MacRecorder();
|
|
61
|
+
|
|
62
|
+
// Renderer'dan gelen istekleri dinle
|
|
63
|
+
ipcMain.handle('get-audio-devices', async () => {
|
|
64
|
+
return await recorder.getAudioDevices();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
ipcMain.handle('start-recording', async (event, options) => {
|
|
68
|
+
try {
|
|
69
|
+
await recorder.startRecording('./recording.mov', {
|
|
70
|
+
includeSystemAudio: options.systemAudio, // true/false
|
|
71
|
+
includeMicrophone: options.microphone, // true/false
|
|
72
|
+
systemAudioDeviceId: options.systemDeviceId || null
|
|
73
|
+
});
|
|
74
|
+
return { success: true };
|
|
75
|
+
} catch (error) {
|
|
76
|
+
return { success: false, error: error.message };
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
ipcMain.handle('stop-recording', async () => {
|
|
81
|
+
return await recorder.stopRecording();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// === REACTʼTE KULLANIM ===
|
|
85
|
+
// renderer.js veya React component
|
|
86
|
+
const { ipcRenderer } = require('electron');
|
|
87
|
+
|
|
88
|
+
class RecordingComponent {
|
|
89
|
+
async componentDidMount() {
|
|
90
|
+
// Ses cihazlarını al
|
|
91
|
+
this.audioDevices = await ipcRenderer.invoke('get-audio-devices');
|
|
92
|
+
this.setState({ audioDevices: this.audioDevices });
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async startRecording() {
|
|
96
|
+
const options = {
|
|
97
|
+
systemAudio: this.state.enableSystemAudio, // checkbox değeri
|
|
98
|
+
microphone: this.state.enableMicrophone, // checkbox değeri
|
|
99
|
+
systemDeviceId: this.state.selectedSystemDevice // dropdown değeri
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const result = await ipcRenderer.invoke('start-recording', options);
|
|
103
|
+
if (result.success) {
|
|
104
|
+
this.setState({ recording: true });
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async stopRecording() {
|
|
109
|
+
await ipcRenderer.invoke('stop-recording');
|
|
110
|
+
this.setState({ recording: false });
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
render() {
|
|
114
|
+
return (
|
|
115
|
+
<div>
|
|
116
|
+
<label>
|
|
117
|
+
<input
|
|
118
|
+
type="checkbox"
|
|
119
|
+
checked={this.state.enableSystemAudio}
|
|
120
|
+
onChange={e => this.setState({enableSystemAudio: e.target.checked})}
|
|
121
|
+
/>
|
|
122
|
+
Sistem Sesini Kaydet
|
|
123
|
+
</label>
|
|
124
|
+
|
|
125
|
+
<select onChange={e => this.setState({selectedSystemDevice: e.target.value})}>
|
|
126
|
+
{this.state.audioDevices?.map(device => (
|
|
127
|
+
<option key={device.id} value={device.id}>
|
|
128
|
+
{device.name}
|
|
129
|
+
</option>
|
|
130
|
+
))}
|
|
131
|
+
</select>
|
|
132
|
+
|
|
133
|
+
<button onClick={() => this.startRecording()}>
|
|
134
|
+
Kayıt Başlat
|
|
135
|
+
</button>
|
|
136
|
+
</div>
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// === EXPRESS API ===
|
|
142
|
+
const express = require('express');
|
|
143
|
+
const MacRecorder = require('node-mac-recorder');
|
|
144
|
+
|
|
145
|
+
const app = express();
|
|
146
|
+
const recorder = new MacRecorder();
|
|
147
|
+
|
|
148
|
+
app.use(express.json());
|
|
149
|
+
|
|
150
|
+
// Ses cihazlarını listele
|
|
151
|
+
app.get('/api/audio-devices', async (req, res) => {
|
|
152
|
+
const devices = await recorder.getAudioDevices();
|
|
153
|
+
res.json(devices);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// Kayıt başlat
|
|
157
|
+
app.post('/api/start-recording', async (req, res) => {
|
|
158
|
+
const { systemAudio, microphone, systemDeviceId } = req.body;
|
|
159
|
+
|
|
160
|
+
try {
|
|
161
|
+
await recorder.startRecording('./api-recording.mov', {
|
|
162
|
+
includeSystemAudio: systemAudio, // true/false
|
|
163
|
+
includeMicrophone: microphone, // true/false
|
|
164
|
+
systemAudioDeviceId: systemDeviceId || null
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
res.json({ success: true, message: 'Recording started' });
|
|
168
|
+
} catch (error) {
|
|
169
|
+
res.status(500).json({ success: false, error: error.message });
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// Kayıt durdur
|
|
174
|
+
app.post('/api/stop-recording', async (req, res) => {
|
|
175
|
+
const result = await recorder.stopRecording();
|
|
176
|
+
res.json(result);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
// Test et
|
|
180
|
+
if (require.main === module) {
|
|
181
|
+
myAppRecording().catch(console.error);
|
|
182
|
+
}
|
package/src/mac_recorder.mm
CHANGED
|
@@ -71,6 +71,7 @@ Napi::Value StartRecording(const Napi::CallbackInfo& info) {
|
|
|
71
71
|
bool includeSystemAudio = true; // Default olarak sistem sesi açık
|
|
72
72
|
CGDirectDisplayID displayID = CGMainDisplayID(); // Default ana ekran
|
|
73
73
|
NSString *audioDeviceId = nil; // Default audio device ID
|
|
74
|
+
NSString *systemAudioDeviceId = nil; // System audio device ID
|
|
74
75
|
|
|
75
76
|
if (info.Length() > 1 && info[1].IsObject()) {
|
|
76
77
|
Napi::Object options = info[1].As<Napi::Object>();
|
|
@@ -109,20 +110,40 @@ Napi::Value StartRecording(const Napi::CallbackInfo& info) {
|
|
|
109
110
|
includeSystemAudio = options.Get("includeSystemAudio").As<Napi::Boolean>();
|
|
110
111
|
}
|
|
111
112
|
|
|
113
|
+
// System audio device ID
|
|
114
|
+
if (options.Has("systemAudioDeviceId") && !options.Get("systemAudioDeviceId").IsNull()) {
|
|
115
|
+
std::string sysDeviceId = options.Get("systemAudioDeviceId").As<Napi::String>().Utf8Value();
|
|
116
|
+
systemAudioDeviceId = [NSString stringWithUTF8String:sysDeviceId.c_str()];
|
|
117
|
+
}
|
|
118
|
+
|
|
112
119
|
// Display ID
|
|
113
120
|
if (options.Has("displayId") && !options.Get("displayId").IsNull()) {
|
|
114
121
|
double displayIdNum = options.Get("displayId").As<Napi::Number>().DoubleValue();
|
|
115
122
|
|
|
116
|
-
//
|
|
123
|
+
// Use the display ID directly (not as an index)
|
|
124
|
+
// The JavaScript layer passes the actual CGDirectDisplayID
|
|
125
|
+
displayID = (CGDirectDisplayID)displayIdNum;
|
|
126
|
+
|
|
127
|
+
// Verify that this display ID is valid
|
|
117
128
|
uint32_t displayCount;
|
|
118
129
|
CGGetActiveDisplayList(0, NULL, &displayCount);
|
|
119
130
|
if (displayCount > 0) {
|
|
120
131
|
CGDirectDisplayID *displays = (CGDirectDisplayID*)malloc(displayCount * sizeof(CGDirectDisplayID));
|
|
121
132
|
CGGetActiveDisplayList(displayCount, displays, &displayCount);
|
|
122
133
|
|
|
123
|
-
|
|
124
|
-
|
|
134
|
+
bool validDisplay = false;
|
|
135
|
+
for (uint32_t i = 0; i < displayCount; i++) {
|
|
136
|
+
if (displays[i] == displayID) {
|
|
137
|
+
validDisplay = true;
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
125
140
|
}
|
|
141
|
+
|
|
142
|
+
if (!validDisplay) {
|
|
143
|
+
// Fallback to main display if invalid ID provided
|
|
144
|
+
displayID = CGMainDisplayID();
|
|
145
|
+
}
|
|
146
|
+
|
|
126
147
|
free(displays);
|
|
127
148
|
}
|
|
128
149
|
}
|
|
@@ -200,11 +221,71 @@ Napi::Value StartRecording(const Napi::CallbackInfo& info) {
|
|
|
200
221
|
}
|
|
201
222
|
}
|
|
202
223
|
|
|
203
|
-
// System audio
|
|
204
|
-
// includeSystemAudio parametresi screen input'un ses yakalama özelliğini kontrol eder
|
|
224
|
+
// System audio configuration
|
|
205
225
|
if (includeSystemAudio) {
|
|
226
|
+
// Enable audio capture in screen input
|
|
206
227
|
g_screenInput.capturesMouseClicks = YES;
|
|
207
|
-
|
|
228
|
+
|
|
229
|
+
// Try to add system audio input using Core Audio
|
|
230
|
+
// This approach captures system audio by creating a virtual audio device
|
|
231
|
+
if (@available(macOS 10.15, *)) {
|
|
232
|
+
// Configure screen input for better audio capture
|
|
233
|
+
g_screenInput.capturesCursor = captureCursor;
|
|
234
|
+
g_screenInput.capturesMouseClicks = YES;
|
|
235
|
+
|
|
236
|
+
// Try to find and add system audio device (like Soundflower, BlackHole, etc.)
|
|
237
|
+
NSArray *audioDevices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio];
|
|
238
|
+
AVCaptureDevice *systemAudioDevice = nil;
|
|
239
|
+
|
|
240
|
+
// If specific system audio device ID is provided, try to find it first
|
|
241
|
+
if (systemAudioDeviceId) {
|
|
242
|
+
for (AVCaptureDevice *device in audioDevices) {
|
|
243
|
+
if ([device.uniqueID isEqualToString:systemAudioDeviceId]) {
|
|
244
|
+
systemAudioDevice = device;
|
|
245
|
+
NSLog(@"[DEBUG] Found specified system audio device: %@ (ID: %@)", device.localizedName, device.uniqueID);
|
|
246
|
+
break;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// If no specific device found or specified, look for known system audio devices
|
|
252
|
+
if (!systemAudioDevice) {
|
|
253
|
+
for (AVCaptureDevice *device in audioDevices) {
|
|
254
|
+
NSString *deviceName = [device.localizedName lowercaseString];
|
|
255
|
+
// Check for common system audio capture devices
|
|
256
|
+
if ([deviceName containsString:@"soundflower"] ||
|
|
257
|
+
[deviceName containsString:@"blackhole"] ||
|
|
258
|
+
[deviceName containsString:@"loopback"] ||
|
|
259
|
+
[deviceName containsString:@"system audio"] ||
|
|
260
|
+
[deviceName containsString:@"aggregate"]) {
|
|
261
|
+
systemAudioDevice = device;
|
|
262
|
+
NSLog(@"[DEBUG] Auto-detected system audio device: %@", device.localizedName);
|
|
263
|
+
break;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// If we found a system audio device, add it as an additional input
|
|
269
|
+
if (systemAudioDevice && !includeMicrophone) {
|
|
270
|
+
// Only add system audio device if microphone is not already added
|
|
271
|
+
NSError *error;
|
|
272
|
+
AVCaptureDeviceInput *systemAudioInput = [[AVCaptureDeviceInput alloc] initWithDevice:systemAudioDevice error:&error];
|
|
273
|
+
if (systemAudioInput && [g_captureSession canAddInput:systemAudioInput]) {
|
|
274
|
+
[g_captureSession addInput:systemAudioInput];
|
|
275
|
+
NSLog(@"[DEBUG] Successfully added system audio device: %@", systemAudioDevice.localizedName);
|
|
276
|
+
} else if (error) {
|
|
277
|
+
NSLog(@"[DEBUG] Failed to add system audio device: %@", error.localizedDescription);
|
|
278
|
+
}
|
|
279
|
+
} else if (includeSystemAudio && !systemAudioDevice) {
|
|
280
|
+
NSLog(@"[DEBUG] System audio requested but no suitable device found. Available devices:");
|
|
281
|
+
for (AVCaptureDevice *device in audioDevices) {
|
|
282
|
+
NSLog(@"[DEBUG] - %@ (ID: %@)", device.localizedName, device.uniqueID);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
} else {
|
|
287
|
+
// Explicitly disable audio capture if not requested
|
|
288
|
+
g_screenInput.capturesMouseClicks = NO;
|
|
208
289
|
}
|
|
209
290
|
|
|
210
291
|
// Create movie file output
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#import <Foundation/Foundation.h>
|
|
2
|
+
#import <ScreenCaptureKit/ScreenCaptureKit.h>
|
|
3
|
+
#import <AVFoundation/AVFoundation.h>
|
|
4
|
+
|
|
5
|
+
API_AVAILABLE(macos(12.3))
|
|
6
|
+
@interface ScreenCaptureKitRecorder : NSObject
|
|
7
|
+
|
|
8
|
+
+ (BOOL)isScreenCaptureKitAvailable;
|
|
9
|
+
+ (BOOL)startRecordingWithConfiguration:(NSDictionary *)config
|
|
10
|
+
delegate:(id)delegate
|
|
11
|
+
error:(NSError **)error;
|
|
12
|
+
+ (void)stopRecording;
|
|
13
|
+
+ (BOOL)isRecording;
|
|
14
|
+
|
|
15
|
+
@end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const MacRecorder = require('./index');
|
|
2
|
+
|
|
3
|
+
async function testWithSystemSound() {
|
|
4
|
+
const recorder = new MacRecorder();
|
|
5
|
+
|
|
6
|
+
console.log('🎵 SİSTEM SESİ TEST EDİLİYOR\n');
|
|
7
|
+
console.log('📋 ÖNEMLİ: Bu testi yapmadan önce:');
|
|
8
|
+
console.log('1. System Preferences > Sound > Output');
|
|
9
|
+
console.log('2. "iMobie Speaker" veya "iMobie Aggregate Device" seç');
|
|
10
|
+
console.log('3. Müzik çalabildiğini kontrol et');
|
|
11
|
+
console.log('');
|
|
12
|
+
console.log('⏳ 5 saniye bekleniyor, hazırlık yap...');
|
|
13
|
+
|
|
14
|
+
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
15
|
+
|
|
16
|
+
try {
|
|
17
|
+
console.log('🔴 KAYIT BAŞLIYOR (3 saniye)...');
|
|
18
|
+
console.log('🎵 ŞİMDİ MÜZİK ÇAL veya YouTube video aç!');
|
|
19
|
+
|
|
20
|
+
await recorder.startRecording('./test-output/system-sound-test.mov', {
|
|
21
|
+
includeSystemAudio: true,
|
|
22
|
+
includeMicrophone: false,
|
|
23
|
+
systemAudioDeviceId: 'iMobie_AggregateDevice_UID', // Direkt ID kullan
|
|
24
|
+
captureArea: { x: 0, y: 0, width: 400, height: 300 }
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// 3 saniye kayıt
|
|
28
|
+
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
29
|
+
|
|
30
|
+
await recorder.stopRecording();
|
|
31
|
+
console.log('✅ KAYIT TAMAMLANDI!');
|
|
32
|
+
console.log('📁 Dosya: ./test-output/system-sound-test.mov');
|
|
33
|
+
|
|
34
|
+
// Dosyayı aç
|
|
35
|
+
console.log('🔍 Dosya açılıyor...');
|
|
36
|
+
require('child_process').exec('open ./test-output/system-sound-test.mov');
|
|
37
|
+
|
|
38
|
+
console.log('\n✅ Eğer ses duyuyorsan: SİSTEM SESİ ÇALIŞIYOR! 🎉');
|
|
39
|
+
console.log('❌ Eğer ses yoksa: Sistem ses output\'unu iMobie cihazına ayarla');
|
|
40
|
+
|
|
41
|
+
} catch (error) {
|
|
42
|
+
console.error('❌ Test hatası:', error.message);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
testWithSystemSound();
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
const MacRecorder = require('./index');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
async function testSystemAudio() {
|
|
5
|
+
const recorder = new MacRecorder();
|
|
6
|
+
|
|
7
|
+
console.log('🎵 Sistem Sesi Yakalama Testi Başlıyor...\n');
|
|
8
|
+
|
|
9
|
+
try {
|
|
10
|
+
// Önce izinleri kontrol et
|
|
11
|
+
const permissions = await recorder.checkPermissions();
|
|
12
|
+
console.log('📋 İzinler:', permissions);
|
|
13
|
+
|
|
14
|
+
if (!permissions.screenRecording) {
|
|
15
|
+
console.log('❌ Ekran kayıt izni gerekli. System Preferences > Security & Privacy > Screen Recording');
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Mevcut ses cihazlarını listele
|
|
20
|
+
console.log('\n🎤 Mevcut Ses Cihazları:');
|
|
21
|
+
const audioDevices = await recorder.getAudioDevices();
|
|
22
|
+
audioDevices.forEach((device, index) => {
|
|
23
|
+
console.log(`${index + 1}. ${device.name}`);
|
|
24
|
+
if (device.id) console.log(` ID: ${device.id}`);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// Sistem sesi cihazı ara
|
|
28
|
+
const systemAudioDevice = audioDevices.find(device =>
|
|
29
|
+
device.name.toLowerCase().includes('blackhole') ||
|
|
30
|
+
device.name.toLowerCase().includes('soundflower') ||
|
|
31
|
+
device.name.toLowerCase().includes('loopback') ||
|
|
32
|
+
device.name.toLowerCase().includes('aggregate') ||
|
|
33
|
+
device.name.toLowerCase().includes('imobie')
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
console.log('\n=== Test 1: Sistem Sesi KAPALI ===');
|
|
37
|
+
const outputPath1 = path.join(__dirname, 'test-output', 'no-system-audio.mov');
|
|
38
|
+
|
|
39
|
+
await recorder.startRecording(outputPath1, {
|
|
40
|
+
includeSystemAudio: false, // Sistem sesi kapalı
|
|
41
|
+
includeMicrophone: false,
|
|
42
|
+
captureCursor: true,
|
|
43
|
+
captureArea: { x: 0, y: 0, width: 400, height: 300 } // Küçük alan
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
console.log('🔴 5 saniye kayıt yapılıyor (sistem sesi KAPALI)...');
|
|
47
|
+
console.log('💡 Şimdi müzik çal veya YouTube video aç - ses KAYIT EDİLMEMELİ');
|
|
48
|
+
|
|
49
|
+
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
50
|
+
await recorder.stopRecording();
|
|
51
|
+
console.log(`✅ Kayıt tamamlandı: ${outputPath1}`);
|
|
52
|
+
|
|
53
|
+
// 2 saniye bekle
|
|
54
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
55
|
+
|
|
56
|
+
console.log('\n=== Test 2: Sistem Sesi AÇIK ===');
|
|
57
|
+
const outputPath2 = path.join(__dirname, 'test-output', 'with-system-audio.mov');
|
|
58
|
+
|
|
59
|
+
const options2 = {
|
|
60
|
+
includeSystemAudio: true, // Sistem sesi açık
|
|
61
|
+
includeMicrophone: false,
|
|
62
|
+
captureCursor: true,
|
|
63
|
+
captureArea: { x: 0, y: 0, width: 400, height: 300 }
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// Eğer sistem sesi cihazı varsa onu kullan
|
|
67
|
+
if (systemAudioDevice) {
|
|
68
|
+
options2.systemAudioDeviceId = systemAudioDevice.id;
|
|
69
|
+
console.log(`🎯 Kullanılan sistem sesi cihazı: ${systemAudioDevice.name}`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
await recorder.startRecording(outputPath2, options2);
|
|
73
|
+
|
|
74
|
+
console.log('🔴 5 saniye kayıt yapılıyor (sistem sesi AÇIK)...');
|
|
75
|
+
console.log('🎵 Şimdi müzik çal veya YouTube video aç - ses KAYIT EDİLMELİ');
|
|
76
|
+
|
|
77
|
+
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
78
|
+
await recorder.stopRecording();
|
|
79
|
+
console.log(`✅ Kayıt tamamlandı: ${outputPath2}`);
|
|
80
|
+
|
|
81
|
+
console.log('\n=== 🎉 TEST TAMAMLANDI ===');
|
|
82
|
+
console.log('📁 Kayıtları karşılaştır:');
|
|
83
|
+
console.log(`1. ${outputPath1} (sistem sesi YOK)`);
|
|
84
|
+
console.log(`2. ${outputPath2} (sistem sesi VAR)`);
|
|
85
|
+
|
|
86
|
+
if (!systemAudioDevice) {
|
|
87
|
+
console.log('\n⚠️ Sistem sesi cihazı bulunamadı!');
|
|
88
|
+
console.log('💡 Daha iyi sistem sesi yakalama için şunları yükle:');
|
|
89
|
+
console.log(' • BlackHole: https://github.com/ExistentialAudio/BlackHole');
|
|
90
|
+
console.log(' • Soundflower: https://github.com/mattingalls/Soundflower');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
console.log('\n🔍 Kayıtları test etmek için:');
|
|
94
|
+
console.log('1. Dosyaları QuickTime Player ile aç');
|
|
95
|
+
console.log('2. İlk kayıtta ses olmamalı');
|
|
96
|
+
console.log('3. İkinci kayıtta sistem sesi olmalı');
|
|
97
|
+
|
|
98
|
+
} catch (error) {
|
|
99
|
+
console.error('❌ Test hatası:', error.message);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Testi çalıştır
|
|
104
|
+
testSystemAudio();
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
const MacRecorder = require('./index');
|
|
2
|
+
|
|
3
|
+
// ===== PARAMETRIK KULLANIM ÖRNEKLERİ =====
|
|
4
|
+
|
|
5
|
+
async function examples() {
|
|
6
|
+
const recorder = new MacRecorder();
|
|
7
|
+
|
|
8
|
+
console.log('🎯 PARAMETRİK SİSTEM SESİ KULLANIMI\n');
|
|
9
|
+
|
|
10
|
+
// 1. SİSTEM SESİ AÇIK (default)
|
|
11
|
+
console.log('1️⃣ Sistem sesi AÇIK (default):');
|
|
12
|
+
await recorder.startRecording('./output1.mov', {
|
|
13
|
+
includeSystemAudio: true, // Default zaten true
|
|
14
|
+
includeMicrophone: false
|
|
15
|
+
});
|
|
16
|
+
// ... kayıt yap ve durdur
|
|
17
|
+
|
|
18
|
+
// 2. SİSTEM SESİ KAPALI
|
|
19
|
+
console.log('2️⃣ Sistem sesi KAPALI:');
|
|
20
|
+
await recorder.startRecording('./output2.mov', {
|
|
21
|
+
includeSystemAudio: false, // Açıkça kapat
|
|
22
|
+
includeMicrophone: true // Sadece mikrofon
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// 3. BELİRLİ SES CİHAZI İLE
|
|
26
|
+
console.log('3️⃣ Belirli sistem ses cihazı:');
|
|
27
|
+
|
|
28
|
+
// Önce cihazları listele
|
|
29
|
+
const devices = await recorder.getAudioDevices();
|
|
30
|
+
const systemDevice = devices.find(d =>
|
|
31
|
+
d.name.includes('iMobie') ||
|
|
32
|
+
d.name.includes('BlackHole') ||
|
|
33
|
+
d.name.includes('Aggregate')
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
if (systemDevice) {
|
|
37
|
+
await recorder.startRecording('./output3.mov', {
|
|
38
|
+
includeSystemAudio: true,
|
|
39
|
+
systemAudioDeviceId: systemDevice.id, // Belirli cihaz
|
|
40
|
+
includeMicrophone: false
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// 4. HER İKİSİ BİRDEN
|
|
45
|
+
console.log('4️⃣ Sistem sesi + Mikrofon:');
|
|
46
|
+
await recorder.startRecording('./output4.mov', {
|
|
47
|
+
includeSystemAudio: true, // Sistem sesi
|
|
48
|
+
includeMicrophone: true, // Mikrofon
|
|
49
|
+
audioDeviceId: 'BuiltInMicrophoneDevice', // Mikrofon cihazı
|
|
50
|
+
systemAudioDeviceId: systemDevice?.id // Sistem ses cihazı
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
console.log('✅ Tüm örnekler hazır!');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// BAŞKA UYGULAMADA KULLANIM
|
|
57
|
+
async function usageInYourApp() {
|
|
58
|
+
const recorder = new MacRecorder();
|
|
59
|
+
|
|
60
|
+
// ===== SENİN UYGULAMANLA ENTEGRASYON =====
|
|
61
|
+
|
|
62
|
+
// Kullanıcı ayarları
|
|
63
|
+
const userSettings = {
|
|
64
|
+
captureSystemAudio: true, // Kullanıcının seçimi
|
|
65
|
+
captureMicrophone: false, // Kullanıcının seçimi
|
|
66
|
+
preferredSystemAudioDevice: null // Kullanıcının seçtiği cihaz
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// Cihazları al ve kullanıcıya göster
|
|
70
|
+
const audioDevices = await recorder.getAudioDevices();
|
|
71
|
+
const systemAudioDevices = audioDevices.filter(device =>
|
|
72
|
+
device.name.toLowerCase().includes('aggregate') ||
|
|
73
|
+
device.name.toLowerCase().includes('blackhole') ||
|
|
74
|
+
device.name.toLowerCase().includes('soundflower') ||
|
|
75
|
+
device.name.toLowerCase().includes('imobie')
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
console.log('🎤 Sistem ses cihazları:');
|
|
79
|
+
systemAudioDevices.forEach((device, i) => {
|
|
80
|
+
console.log(`${i + 1}. ${device.name}`);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// Kayıt başlat
|
|
84
|
+
const recordingOptions = {
|
|
85
|
+
includeSystemAudio: userSettings.captureSystemAudio,
|
|
86
|
+
includeMicrophone: userSettings.captureMicrophone,
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// Eğer kullanıcı belirli cihaz seçtiyse
|
|
90
|
+
if (userSettings.preferredSystemAudioDevice) {
|
|
91
|
+
recordingOptions.systemAudioDeviceId = userSettings.preferredSystemAudioDevice;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Kayıt başlat
|
|
95
|
+
await recorder.startRecording('./user-recording.mov', recordingOptions);
|
|
96
|
+
|
|
97
|
+
console.log('🔴 Kayıt başladı...');
|
|
98
|
+
|
|
99
|
+
// Gerektiğinde durdur
|
|
100
|
+
setTimeout(async () => {
|
|
101
|
+
await recorder.stopRecording();
|
|
102
|
+
console.log('✅ Kayıt bitti!');
|
|
103
|
+
}, 5000);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// ===== REACT/ELECTRON ÖRNEĞİ =====
|
|
107
|
+
class AudioRecorderService {
|
|
108
|
+
constructor() {
|
|
109
|
+
this.recorder = new MacRecorder();
|
|
110
|
+
this.isRecording = false;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async getAvailableSystemAudioDevices() {
|
|
114
|
+
const devices = await this.recorder.getAudioDevices();
|
|
115
|
+
return devices.filter(device =>
|
|
116
|
+
device.name.toLowerCase().includes('aggregate') ||
|
|
117
|
+
device.name.toLowerCase().includes('blackhole') ||
|
|
118
|
+
device.name.toLowerCase().includes('imobie')
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async startRecording(options = {}) {
|
|
123
|
+
const {
|
|
124
|
+
outputPath,
|
|
125
|
+
includeSystemAudio = true, // Default açık
|
|
126
|
+
includeMicrophone = false,
|
|
127
|
+
systemAudioDeviceId = null,
|
|
128
|
+
audioDeviceId = null,
|
|
129
|
+
windowId = null,
|
|
130
|
+
displayId = null
|
|
131
|
+
} = options;
|
|
132
|
+
|
|
133
|
+
if (this.isRecording) {
|
|
134
|
+
throw new Error('Already recording');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const recordingConfig = {
|
|
138
|
+
includeSystemAudio,
|
|
139
|
+
includeMicrophone,
|
|
140
|
+
systemAudioDeviceId,
|
|
141
|
+
audioDeviceId,
|
|
142
|
+
windowId,
|
|
143
|
+
displayId
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
await this.recorder.startRecording(outputPath, recordingConfig);
|
|
147
|
+
this.isRecording = true;
|
|
148
|
+
|
|
149
|
+
return { success: true, config: recordingConfig };
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async stopRecording() {
|
|
153
|
+
if (!this.isRecording) {
|
|
154
|
+
throw new Error('Not recording');
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const result = await this.recorder.stopRecording();
|
|
158
|
+
this.isRecording = false;
|
|
159
|
+
|
|
160
|
+
return result;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
getRecordingStatus() {
|
|
164
|
+
return {
|
|
165
|
+
isRecording: this.isRecording,
|
|
166
|
+
...this.recorder.getStatus()
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// KULLANIM ÖRNEĞİ
|
|
172
|
+
async function exampleUsage() {
|
|
173
|
+
const service = new AudioRecorderService();
|
|
174
|
+
|
|
175
|
+
// Sistem ses cihazlarını listele
|
|
176
|
+
const systemDevices = await service.getAvailableSystemAudioDevices();
|
|
177
|
+
console.log('Mevcut sistem ses cihazları:', systemDevices);
|
|
178
|
+
|
|
179
|
+
// Kayıt başlat - sistem sesi açık
|
|
180
|
+
await service.startRecording({
|
|
181
|
+
outputPath: './my-app-recording.mov',
|
|
182
|
+
includeSystemAudio: true, // ✅ Sistem sesi
|
|
183
|
+
includeMicrophone: false, // ❌ Mikrofon
|
|
184
|
+
systemAudioDeviceId: systemDevices[0]?.id // İlk cihazı kullan
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
console.log('Recording started with system audio!');
|
|
188
|
+
|
|
189
|
+
// 10 saniye sonra durdur
|
|
190
|
+
setTimeout(async () => {
|
|
191
|
+
await service.stopRecording();
|
|
192
|
+
console.log('Recording finished!');
|
|
193
|
+
}, 10000);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Test et
|
|
197
|
+
if (require.main === module) {
|
|
198
|
+
console.log('🚀 Parametrik sistem sesi test ediliyor...\n');
|
|
199
|
+
exampleUsage().catch(console.error);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
module.exports = { AudioRecorderService };
|