From 371f79a35f394e107c8705372f3cc1cddfc0464b Mon Sep 17 00:00:00 2001 From: Siddharth Date: Sat, 7 Mar 2026 16:44:10 -0800 Subject: [PATCH] system audio --- README.md | 10 +++ electron-builder.json5 | 6 +- electron/main.ts | 7 ++ src/components/launch/LaunchWindow.tsx | 19 ++++- src/hooks/useScreenRecorder.ts | 96 ++++++++++++++++++++------ 5 files changed, 115 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index ba776fc..ffbd7b6 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,8 @@ OpenScreen is 100% free for personal and commercial use. Use it, modify it, dist ## Core Features - Record your whole screen or specific apps +- Microphone recording with device selection. +- System audio capture (record what's playing on your screen) - Add manual zooms (customizable depth levels) - Customize the duration and position of zooms however you please - Crop video recordings to hide parts @@ -70,6 +72,14 @@ You may need to grant screen recording permissions depending on your desktop env ./Openscreen-Linux-*.AppImage --no-sandbox ``` +### Limitations + +System audio capture relies on Electron's [desktopCapturer](https://www.electronjs.org/docs/latest/api/desktop-capturer) and has some platform-specific quirks: + +- **macOS**: Requires macOS 13+. On macOS 14.2+ you'll be prompted to grant audio capture permission. macOS 12 and below does not support system audio (mic still works). +- **Windows**: Works out of the box. +- **Linux**: Needs PipeWire (default on Ubuntu 22.04+, Fedora 34+). Older PulseAudio-only setups may not support system audio (mic should still works). + ## Built with - Electron - React diff --git a/electron-builder.json5 b/electron-builder.json5 index 7e1b92a..b8073db 100644 --- a/electron-builder.json5 +++ b/electron-builder.json5 @@ -35,7 +35,11 @@ } ], "icon": "icons/icons/mac/icon.icns", - "artifactName": "${productName}-Mac-${arch}-${version}-Installer.${ext}" + "artifactName": "${productName}-Mac-${arch}-${version}-Installer.${ext}", + "extendInfo": { + "NSAudioCaptureUsageDescription": "OpenScreen needs audio capture permission to record system audio.", + "NSCameraUseContinuityCameraDeviceType": true + } }, "linux": { "target": [ diff --git a/electron/main.ts b/electron/main.ts index 9efbf73..32fb59a 100644 --- a/electron/main.ts +++ b/electron/main.ts @@ -8,6 +8,13 @@ import { registerIpcHandlers } from './ipc/handlers' const __dirname = path.dirname(fileURLToPath(import.meta.url)) +// Use Screen & System Audio Recording permissions instead of CoreAudio Tap API on macOS. +// CoreAudio Tap requires NSAudioCaptureUsageDescription in the parent app's Info.plist, +// which doesn't work when running from a terminal/IDE during development, makes my life easier +if (process.platform === 'darwin') { + app.commandLine.appendSwitch('disable-features', 'MacCatapLoopbackAudioForScreenShare') +} + export const RECORDINGS_DIR = path.join(app.getPath('userData'), 'recordings') diff --git a/src/components/launch/LaunchWindow.tsx b/src/components/launch/LaunchWindow.tsx index 108cd14..4c8b92e 100644 --- a/src/components/launch/LaunchWindow.tsx +++ b/src/components/launch/LaunchWindow.tsx @@ -7,14 +7,14 @@ import { AudioLevelMeter } from "../ui/audio-level-meter"; import { Button } from "../ui/button"; import { BsRecordCircle } from "react-icons/bs"; import { FaRegStopCircle } from "react-icons/fa"; -import { MdMonitor, MdMic, MdMicOff } from "react-icons/md"; +import { MdMonitor, MdMic, MdMicOff, MdVolumeUp, MdVolumeOff } from "react-icons/md"; import { RxDragHandleDots2 } from "react-icons/rx"; import { FaFolderMinus } from "react-icons/fa6"; import { FiMinus, FiX } from "react-icons/fi"; import { ContentClamp } from "../ui/content-clamp"; export function LaunchWindow() { - const { recording, toggleRecording, microphoneEnabled, setMicrophoneEnabled, microphoneDeviceId, setMicrophoneDeviceId } = useScreenRecorder(); + const { recording, toggleRecording, microphoneEnabled, setMicrophoneEnabled, microphoneDeviceId, setMicrophoneDeviceId, systemAudioEnabled, setSystemAudioEnabled } = useScreenRecorder(); const [recordingStart, setRecordingStart] = useState(null); const [elapsed, setElapsed] = useState(0); @@ -165,6 +165,21 @@ export function LaunchWindow() {
+ +