UNPKG

audio.libx.js

Version:

Comprehensive audio library with progressive streaming, recording capabilities, real-time processing, and intelligent caching for web applications

298 lines 12.1 kB
import { PermissionError } from './types.js'; export class PermissionManager { constructor() { this._currentStream = null; this._permissionState = { status: 'unknown', isSupported: false, }; this._initialize(); } static getInstance() { if (!PermissionManager._instance) { PermissionManager._instance = new PermissionManager(); } return PermissionManager._instance; } _initialize() { this._permissionState.isSupported = this._isGetUserMediaSupported(); if (this._permissionState.isSupported) { this._checkInitialPermissionState(); } } _isGetUserMediaSupported() { return !!(navigator.mediaDevices?.getUserMedia || navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia); } async _checkInitialPermissionState() { if ('permissions' in navigator && 'query' in navigator.permissions) { try { const permission = await navigator.permissions.query({ name: 'microphone' }); this._permissionState.status = permission.state; permission.addEventListener('change', () => { this._permissionState.status = permission.state; }); } catch (error) { this._permissionState.status = 'prompt'; } } else { this._permissionState.status = 'prompt'; } } async requestPermission(constraints = {}) { if (!this._permissionState.isSupported) { const error = new PermissionError('getUserMedia is not supported in this browser'); return { granted: false, state: { ...this._permissionState, error }, error, }; } try { const mediaConstraints = this._buildMediaConstraints(constraints); const stream = await this._getUserMedia(mediaConstraints); this._permissionState.status = 'granted'; this._currentStream = stream; return { granted: true, state: { ...this._permissionState }, stream, }; } catch (error) { const permissionError = this._handlePermissionError(error); this._permissionState.error = permissionError; return { granted: false, state: { ...this._permissionState }, error: permissionError, }; } } _buildMediaConstraints(options) { const audioConstraints = { echoCancellation: options.echoCancellation ?? true, noiseSuppression: options.noiseSuppression ?? true, autoGainControl: options.autoGainControl ?? true, }; if (options.deviceId) { audioConstraints.deviceId = options.deviceId; } if (options.sampleRate) { audioConstraints.sampleRate = options.sampleRate; } if (options.channelCount) { audioConstraints.channelCount = options.channelCount; } if (this._isSafari()) { const safariConstraints = { echoCancellation: audioConstraints.echoCancellation, noiseSuppression: audioConstraints.noiseSuppression, autoGainControl: audioConstraints.autoGainControl, }; if (audioConstraints.deviceId) { safariConstraints.deviceId = audioConstraints.deviceId; } return { audio: safariConstraints, }; } return { audio: audioConstraints }; } async _getUserMedia(constraints) { if (navigator.mediaDevices?.getUserMedia) { return await navigator.mediaDevices.getUserMedia(constraints); } const getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia; if (!getUserMedia) { throw new Error('getUserMedia is not supported'); } return new Promise((resolve, reject) => { getUserMedia.call(navigator, constraints, resolve, reject); }); } _handlePermissionError(error) { let message = 'Unknown permission error'; let status = 'unknown'; if (error.name === 'NotAllowedError' || error.name === 'PermissionDeniedError') { message = 'Microphone access was denied by the user. Please enable microphone permissions in your browser settings.'; status = 'denied'; } else if (error.name === 'NotFoundError' || error.name === 'DevicesNotFoundError') { message = 'No microphone device was found. Please check that a microphone is connected and try again.'; status = 'denied'; } else if (error.name === 'NotSupportedError') { message = 'Microphone access is not supported in this browser or context (HTTPS required).'; status = 'denied'; } else if (error.name === 'NotReadableError' || error.name === 'TrackStartError') { message = 'The microphone is already in use by another application. Please close other applications using the microphone and try again.'; status = 'denied'; } else if (error.name === 'OverconstrainedError' || error.name === 'ConstraintNotSatisfiedError') { message = 'The requested audio constraints could not be satisfied. Please try with different settings.'; status = 'denied'; } else if (error.name === 'SecurityError') { message = 'Microphone access blocked due to security restrictions. Please ensure you are using HTTPS and try again.'; status = 'denied'; } else if (error.name === 'AbortError') { message = 'Permission request was cancelled or interrupted.'; status = 'prompt'; } this._permissionState.status = status; return new PermissionError(message, error); } async checkPermissionState() { if (!this._permissionState.isSupported) { return { ...this._permissionState }; } if ('permissions' in navigator && 'query' in navigator.permissions) { try { const permission = await navigator.permissions.query({ name: 'microphone' }); this._permissionState.status = permission.state; } catch (error) { } } return { ...this._permissionState }; } stopCurrentStream() { if (this._currentStream) { this._currentStream.getTracks().forEach((track) => { track.stop(); }); this._currentStream = null; } } getCurrentStream() { return this._currentStream; } getPermissionErrorGuidance(error) { const guidance = []; if (error.message.includes('denied')) { guidance.push("Click the microphone icon in your browser's address bar"); guidance.push('Select "Always allow" for microphone access'); guidance.push('Refresh the page and try again'); } else if (error.message.includes('not found')) { guidance.push('Check that your microphone is properly connected'); guidance.push('Try selecting a different microphone in your browser settings'); guidance.push('Restart your browser if the issue persists'); } else if (error.message.includes('in use')) { guidance.push('Close other applications that might be using your microphone'); guidance.push('Check for other browser tabs using the microphone'); guidance.push('Restart your browser if necessary'); } else if (error.message.includes('HTTPS')) { guidance.push('Microphone access requires a secure connection (HTTPS)'); guidance.push('Try accessing the site via HTTPS'); guidance.push('Contact the site administrator if the issue persists'); } else { guidance.push('Try refreshing the page'); guidance.push("Check your browser's microphone permissions"); guidance.push('Try using a different browser if the issue persists'); } return guidance; } async testMicrophoneAccess(constraints = {}) { const result = await this.requestPermission(constraints); if (result.stream) { result.stream.getTracks().forEach((track) => track.stop()); } return { ...result, stream: undefined, }; } async getAudioInputDevices() { if (!navigator.mediaDevices?.enumerateDevices) { throw new PermissionError('Device enumeration is not supported'); } try { const devices = await navigator.mediaDevices.enumerateDevices(); return devices.filter((device) => device.kind === 'audioinput'); } catch (error) { throw new PermissionError('Failed to enumerate audio devices', error); } } _isSafari() { const userAgent = navigator.userAgent.toLowerCase(); return userAgent.includes('safari') && !userAgent.includes('chrome'); } getBrowserSpecificGuidance() { const userAgent = navigator.userAgent.toLowerCase(); if (userAgent.includes('chrome')) { return [ 'Chrome: Click the microphone icon in the address bar', 'Select "Always allow" for this site', 'Check chrome://settings/content/microphone for global settings', ]; } else if (userAgent.includes('firefox')) { return [ 'Firefox: Click the shield icon or microphone icon in the address bar', 'Select "Allow" and check "Remember this decision"', 'Check about:preferences#privacy for global settings', ]; } else if (this._isSafari()) { return [ 'Safari: Check Safari > Preferences > Websites > Microphone', 'Set this website to "Allow"', 'Ensure microphone access is enabled in System Preferences > Security & Privacy', ]; } else if (userAgent.includes('edge')) { return [ 'Edge: Click the microphone icon in the address bar', 'Select "Always allow on this site"', 'Check edge://settings/content/microphone for global settings', ]; } return [ "Check your browser's microphone permissions for this site", 'Look for a microphone icon in the address bar', 'Ensure microphone access is enabled in your browser settings', ]; } getCapabilities() { return { isSupported: this._permissionState.isSupported, hasPermissionsAPI: 'permissions' in navigator && 'query' in navigator.permissions, hasEnumerateDevices: !!navigator.mediaDevices?.enumerateDevices, currentStatus: this._permissionState.status, browser: this._getBrowserInfo(), }; } _getBrowserInfo() { const userAgent = navigator.userAgent.toLowerCase(); if (userAgent.includes('chrome')) return 'chrome'; if (userAgent.includes('firefox')) return 'firefox'; if (this._isSafari()) return 'safari'; if (userAgent.includes('edge')) return 'edge'; return 'unknown'; } dispose() { this.stopCurrentStream(); this._permissionState = { status: 'unknown', isSupported: false, }; } } //# sourceMappingURL=PermissionManager.js.map