fix: avoid ScreenCaptureKit launch crash on macOS 11 (#424)

Co-authored-by: txhno <[email protected]>
This commit is contained in:
Roshan Warrier
2026-04-16 01:58:29 -07:00
committed by GitHub
co-authored by txhno
parent 7184a25e44
commit a5d5c780c2
6 changed files with 47 additions and 18 deletions
+18 -2
View File
@@ -26,8 +26,24 @@ export function useSystemAudioCapture({
// Check if system audio capture is supported
useEffect(() => {
const supported = platform.audio.isSystemAudioSupported();
setIsSupported(supported);
let isActive = true;
void platform.audio
.isSystemAudioSupported()
.then((supported) => {
if (isActive) {
setIsSupported(supported);
}
})
.catch(() => {
if (isActive) {
setIsSupported(false);
}
});
return () => {
isActive = false;
};
}, [platform]);
const startRecording = useCallback(async () => {
+1 -1
View File
@@ -42,7 +42,7 @@ export interface AudioDevice {
}
export interface PlatformAudio {
isSystemAudioSupported(): boolean;
isSystemAudioSupported(): Promise<boolean>;
startSystemAudioCapture(maxDurationSecs: number): Promise<void>;
stopSystemAudioCapture(): Promise<Blob>;
listOutputDevices(): Promise<AudioDevice[]>;
+4
View File
@@ -5,6 +5,10 @@ fn main() {
// Link Swift runtime libraries for screencapturekit crate
#[cfg(target_os = "macos")]
{
// ScreenCaptureKit does not exist on macOS 11, so weak-link it to
// allow the app to launch and gate usage at runtime instead.
println!("cargo:rustc-link-arg=-Wl,-weak_framework,ScreenCaptureKit");
// Add Swift runtime library paths to RPATH
println!("cargo:rustc-link-arg=-Wl,-rpath,/usr/lib/swift");
println!("cargo:rustc-link-arg=-L/usr/lib/swift");
+21 -11
View File
@@ -13,6 +13,7 @@ use screencapturekit::{
},
};
use std::io::Cursor;
use std::process::Command;
use std::sync::{Arc, Mutex};
use tokio::sync::mpsc;
@@ -20,6 +21,10 @@ pub async fn start_capture(
state: &AudioCaptureState,
max_duration_secs: u32,
) -> Result<(), String> {
if !is_supported() {
return Err("System audio capture requires macOS 12.3 or newer.".to_string());
}
// Reset previous samples
state.reset();
@@ -144,17 +149,22 @@ pub async fn stop_capture(state: &AudioCaptureState) -> Result<String, String> {
}
pub fn is_supported() -> bool {
// ScreenCaptureKit requires macOS 12.3+
// Check if we're on a supported version
#[cfg(target_os = "macos")]
{
// Basic check - ScreenCaptureKit should be available on macOS 12.3+
true
}
#[cfg(not(target_os = "macos"))]
{
false
}
macos_version_at_least(12, 3)
}
fn macos_version_at_least(required_major: u64, required_minor: u64) -> bool {
let output = match Command::new("sw_vers").arg("-productVersion").output() {
Ok(output) if output.status.success() => output,
_ => return false,
};
let version = String::from_utf8_lossy(&output.stdout);
let mut parts = version.trim().split('.');
let major = parts.next().and_then(|part| part.parse::<u64>().ok()).unwrap_or(0);
let minor = parts.next().and_then(|part| part.parse::<u64>().ok()).unwrap_or(0);
major > required_major || (major == required_major && minor >= required_minor)
}
fn extract_audio_samples(sample_buffer: CMSampleBuffer) -> Result<Vec<f32>, String> {
+2 -3
View File
@@ -2,9 +2,8 @@ import { invoke } from '@tauri-apps/api/core';
import type { PlatformAudio, AudioDevice } from '@/platform/types';
export const tauriAudio: PlatformAudio = {
isSystemAudioSupported(): boolean {
// This will be checked dynamically via invoke
return true; // Tauri supports it, but actual support depends on platform
async isSystemAudioSupported(): Promise<boolean> {
return await invoke<boolean>('is_system_audio_supported');
},
async startSystemAudioCapture(maxDurationSecs: number): Promise<void> {
+1 -1
View File
@@ -1,7 +1,7 @@
import type { PlatformAudio, AudioDevice } from '@/platform/types';
export const webAudio: PlatformAudio = {
isSystemAudioSupported(): boolean {
async isSystemAudioSupported(): Promise<boolean> {
return false; // System audio capture not supported in web
},