mirror of https://github.com/jetkvm/kvm.git
[WIP] Improvements, Bugfixes: Improve audio experience when running in HTTP mode
This commit is contained in:
parent
d311dee4c6
commit
630571da25
|
@ -244,6 +244,11 @@ export default function AudioControlPopover({ microphone }: AudioControlPopoverP
|
||||||
|
|
||||||
// Handle microphone device change
|
// Handle microphone device change
|
||||||
const handleMicrophoneDeviceChange = async (deviceId: string) => {
|
const handleMicrophoneDeviceChange = async (deviceId: string) => {
|
||||||
|
// Don't process device changes for HTTPS-required placeholder
|
||||||
|
if (deviceId === 'https-required') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
setSelectedInputDevice(deviceId);
|
setSelectedInputDevice(deviceId);
|
||||||
|
|
||||||
// If microphone is currently active, restart it with the new device
|
// If microphone is currently active, restart it with the new device
|
||||||
|
@ -402,11 +407,15 @@ export default function AudioControlPopover({ microphone }: AudioControlPopoverP
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
{isMicrophoneActiveFromHook && (
|
{isHttpsRequired ? (
|
||||||
|
<p className="text-xs text-amber-600 dark:text-amber-400">
|
||||||
|
HTTPS connection required for microphone device selection
|
||||||
|
</p>
|
||||||
|
) : isMicrophoneActiveFromHook ? (
|
||||||
<p className="text-xs text-slate-500 dark:text-slate-400">
|
<p className="text-xs text-slate-500 dark:text-slate-400">
|
||||||
Changing device will restart the microphone
|
Changing device will restart the microphone
|
||||||
</p>
|
</p>
|
||||||
)}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Speaker Selection */}
|
{/* Speaker Selection */}
|
||||||
|
|
|
@ -33,31 +33,55 @@ export function useAudioDevices(): UseAudioDevicesReturn {
|
||||||
setError(null);
|
setError(null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Check if getUserMedia is available (requires HTTPS in most browsers)
|
// Check if we're on HTTP (microphone requires HTTPS, but speakers can work)
|
||||||
if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
|
const isHttp = window.location.protocol === 'http:';
|
||||||
// Set placeholder devices when HTTPS is required
|
const hasMediaDevices = !!navigator.mediaDevices;
|
||||||
|
const hasGetUserMedia = !!navigator.mediaDevices?.getUserMedia;
|
||||||
|
const hasEnumerateDevices = !!navigator.mediaDevices?.enumerateDevices;
|
||||||
|
|
||||||
|
if (isHttp || !hasMediaDevices || !hasGetUserMedia) {
|
||||||
|
// Set placeholder devices when HTTPS is required for microphone
|
||||||
setAudioInputDevices([
|
setAudioInputDevices([
|
||||||
{ deviceId: 'https-required', label: 'HTTPS Required for Microphone Access', kind: 'audioinput' }
|
{ deviceId: 'https-required', label: 'HTTPS Required for Microphone Access', kind: 'audioinput' }
|
||||||
]);
|
]);
|
||||||
// Speakers still work on HTTP, so enumerate them normally
|
|
||||||
const devices = await navigator.mediaDevices.enumerateDevices();
|
|
||||||
const outputDevices: AudioDevice[] = [
|
|
||||||
{ deviceId: 'default', label: 'Default Speaker', kind: 'audiooutput' }
|
|
||||||
];
|
|
||||||
|
|
||||||
devices.forEach(device => {
|
// Try to enumerate speakers if possible, otherwise provide defaults
|
||||||
if (device.kind === 'audiooutput' && device.deviceId !== 'default') {
|
if (hasMediaDevices && hasEnumerateDevices) {
|
||||||
outputDevices.push({
|
try {
|
||||||
deviceId: device.deviceId,
|
const devices = await navigator.mediaDevices.enumerateDevices();
|
||||||
label: device.label || `Speaker ${device.deviceId.slice(0, 8)}`,
|
const outputDevices: AudioDevice[] = [
|
||||||
kind: 'audiooutput'
|
{ deviceId: 'default', label: 'Default Speaker', kind: 'audiooutput' }
|
||||||
|
];
|
||||||
|
|
||||||
|
devices.forEach(device => {
|
||||||
|
if (device.kind === 'audiooutput' && device.deviceId !== 'default') {
|
||||||
|
outputDevices.push({
|
||||||
|
deviceId: device.deviceId,
|
||||||
|
label: device.label || `Speaker ${device.deviceId.slice(0, 8)}`,
|
||||||
|
kind: 'audiooutput'
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
setAudioOutputDevices(outputDevices);
|
||||||
|
} catch {
|
||||||
|
// Fallback to default speakers if enumeration fails
|
||||||
|
setAudioOutputDevices([
|
||||||
|
{ deviceId: 'default', label: 'Default Speaker', kind: 'audiooutput' },
|
||||||
|
{ deviceId: 'system-default', label: 'System Default Audio Output', kind: 'audiooutput' }
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
});
|
} else {
|
||||||
|
// No enumeration available, use defaults
|
||||||
|
setAudioOutputDevices([
|
||||||
|
{ deviceId: 'default', label: 'Default Speaker', kind: 'audiooutput' },
|
||||||
|
{ deviceId: 'system-default', label: 'System Default Audio Output', kind: 'audiooutput' }
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
setAudioOutputDevices(outputDevices);
|
|
||||||
setSelectedInputDevice('https-required');
|
setSelectedInputDevice('https-required');
|
||||||
throw new Error('Microphone access requires HTTPS connection. Please use HTTPS to access audio features.');
|
setSelectedOutputDevice('default');
|
||||||
|
return; // Exit gracefully without throwing error on HTTP
|
||||||
}
|
}
|
||||||
|
|
||||||
// Request permissions first to get device labels
|
// Request permissions first to get device labels
|
||||||
|
@ -95,7 +119,12 @@ export function useAudioDevices(): UseAudioDevicesReturn {
|
||||||
// Audio devices enumerated
|
// Audio devices enumerated
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
devError('Failed to enumerate audio devices:', err);
|
// Only log errors on HTTPS where we expect full device access
|
||||||
|
const isHttp = window.location.protocol === 'http:';
|
||||||
|
if (!isHttp) {
|
||||||
|
devError('Failed to enumerate audio devices:', err);
|
||||||
|
}
|
||||||
|
|
||||||
let errorMessage = 'Failed to access audio devices';
|
let errorMessage = 'Failed to access audio devices';
|
||||||
|
|
||||||
if (err instanceof Error) {
|
if (err instanceof Error) {
|
||||||
|
@ -112,7 +141,10 @@ export function useAudioDevices(): UseAudioDevicesReturn {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setError(errorMessage);
|
// Only set error state on HTTPS where we expect device access to work
|
||||||
|
if (!isHttp) {
|
||||||
|
setError(errorMessage);
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,15 @@ export interface MicrophoneError {
|
||||||
|
|
||||||
// Helper function to check if HTTPS is required for microphone access
|
// Helper function to check if HTTPS is required for microphone access
|
||||||
export function isHttpsRequired(): boolean {
|
export function isHttpsRequired(): boolean {
|
||||||
return !navigator.mediaDevices || !navigator.mediaDevices.getUserMedia;
|
// Check if we're on HTTP (not HTTPS)
|
||||||
|
const isHttp = window.location.protocol === 'http:';
|
||||||
|
|
||||||
|
// Check if media devices are available
|
||||||
|
const hasMediaDevices = !!navigator.mediaDevices;
|
||||||
|
const hasGetUserMedia = !!navigator.mediaDevices?.getUserMedia;
|
||||||
|
|
||||||
|
// HTTPS is required if we're on HTTP OR if media devices aren't available
|
||||||
|
return isHttp || !hasMediaDevices || !hasGetUserMedia;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useMicrophone() {
|
export function useMicrophone() {
|
||||||
|
|
Loading…
Reference in New Issue