|
@@ -0,0 +1,176 @@
|
|
|
|
|
+/**
|
|
|
|
|
+ * 浏览器兼容性检查工具
|
|
|
|
|
+ */
|
|
|
|
|
+
|
|
|
|
|
+export interface BrowserCompatibility {
|
|
|
|
|
+ supported: boolean;
|
|
|
|
|
+ incompatibleFeatures: string[];
|
|
|
|
|
+ browserInfo: {
|
|
|
|
|
+ name: string;
|
|
|
|
|
+ version: string;
|
|
|
|
|
+ isMobile: boolean;
|
|
|
|
|
+ };
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 检查浏览器对录音功能的兼容性
|
|
|
|
|
+ */
|
|
|
|
|
+export function checkBrowserCompatibility(): BrowserCompatibility {
|
|
|
|
|
+ const incompatibleFeatures: string[] = [];
|
|
|
|
|
+ const userAgent = navigator.userAgent.toLowerCase();
|
|
|
|
|
+
|
|
|
|
|
+ // 检测浏览器信息
|
|
|
|
|
+ const browserInfo = {
|
|
|
|
|
+ name: getBrowserName(userAgent),
|
|
|
|
|
+ version: getBrowserVersion(userAgent),
|
|
|
|
|
+ isMobile: isMobileDevice(userAgent)
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ // 检查必需的API
|
|
|
|
|
+ if (!('MediaRecorder' in window)) {
|
|
|
|
|
+ incompatibleFeatures.push('MediaRecorder');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (!('navigator' in window) || !('mediaDevices' in navigator)) {
|
|
|
|
|
+ incompatibleFeatures.push('navigator.mediaDevices');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (!navigator.mediaDevices || typeof navigator.mediaDevices.getUserMedia !== 'function') {
|
|
|
|
|
+ incompatibleFeatures.push('getUserMedia');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (!window.AudioContext && !(window as any).webkitAudioContext) {
|
|
|
|
|
+ incompatibleFeatures.push('AudioContext');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 检查HTTPS要求(生产环境)
|
|
|
|
|
+ if (location.protocol !== 'https:' && location.hostname !== 'localhost' && location.hostname !== '127.0.0.1') {
|
|
|
|
|
+ incompatibleFeatures.push('HTTPS (录音功能需要安全连接)');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 移动端特殊检查
|
|
|
|
|
+ if (browserInfo.isMobile) {
|
|
|
|
|
+ // iOS Safari版本检查
|
|
|
|
|
+ if (browserInfo.name === 'Safari' && browserInfo.version) {
|
|
|
|
|
+ const version = parseInt(browserInfo.version);
|
|
|
|
|
+ if (version < 14) {
|
|
|
|
|
+ incompatibleFeatures.push('iOS Safari版本过低 (需要14.0+)');
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 检查移动端支持的音频格式
|
|
|
|
|
+ if ('MediaRecorder' in window) {
|
|
|
|
|
+ const supportedFormats = [
|
|
|
|
|
+ 'audio/webm;codecs=opus',
|
|
|
|
|
+ 'audio/webm',
|
|
|
|
|
+ 'audio/mp4',
|
|
|
|
|
+ 'audio/ogg;codecs=opus'
|
|
|
|
|
+ ];
|
|
|
|
|
+
|
|
|
|
|
+ const hasSupportedFormat = supportedFormats.some(format =>
|
|
|
|
|
+ MediaRecorder.isTypeSupported(format)
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ if (!hasSupportedFormat) {
|
|
|
|
|
+ incompatibleFeatures.push('移动端音频格式支持');
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return {
|
|
|
|
|
+ supported: incompatibleFeatures.length === 0,
|
|
|
|
|
+ incompatibleFeatures,
|
|
|
|
|
+ browserInfo
|
|
|
|
|
+ };
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 检测是否为移动设备
|
|
|
|
|
+ */
|
|
|
|
|
+export function isMobileDevice(userAgent?: string): boolean {
|
|
|
|
|
+ const ua = userAgent || navigator.userAgent.toLowerCase();
|
|
|
|
|
+ return /android|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(ua);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 获取浏览器名称
|
|
|
|
|
+ */
|
|
|
|
|
+function getBrowserName(userAgent: string): string {
|
|
|
|
|
+ if (userAgent.includes('chrome') && !userAgent.includes('edg')) {
|
|
|
|
|
+ return 'Chrome';
|
|
|
|
|
+ } else if (userAgent.includes('firefox')) {
|
|
|
|
|
+ return 'Firefox';
|
|
|
|
|
+ } else if (userAgent.includes('safari') && !userAgent.includes('chrome')) {
|
|
|
|
|
+ return 'Safari';
|
|
|
|
|
+ } else if (userAgent.includes('edg')) {
|
|
|
|
|
+ return 'Edge';
|
|
|
|
|
+ } else if (userAgent.includes('opera')) {
|
|
|
|
|
+ return 'Opera';
|
|
|
|
|
+ }
|
|
|
|
|
+ return 'Unknown';
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 获取浏览器版本
|
|
|
|
|
+ */
|
|
|
|
|
+function getBrowserVersion(userAgent: string): string {
|
|
|
|
|
+ let version = 'Unknown';
|
|
|
|
|
+
|
|
|
|
|
+ if (userAgent.includes('chrome') && !userAgent.includes('edg')) {
|
|
|
|
|
+ const match = userAgent.match(/chrome\/(\d+)/i);
|
|
|
|
|
+ if (match) version = match[1];
|
|
|
|
|
+ } else if (userAgent.includes('firefox')) {
|
|
|
|
|
+ const match = userAgent.match(/firefox\/(\d+)/i);
|
|
|
|
|
+ if (match) version = match[1];
|
|
|
|
|
+ } else if (userAgent.includes('safari') && !userAgent.includes('chrome')) {
|
|
|
|
|
+ const match = userAgent.match(/version\/(\d+)/i);
|
|
|
|
|
+ if (match) version = match[1];
|
|
|
|
|
+ } else if (userAgent.includes('edg')) {
|
|
|
|
|
+ const match = userAgent.match(/edg\/(\d+)/i);
|
|
|
|
|
+ if (match) version = match[1];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return version;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 获取详细的兼容性报告
|
|
|
|
|
+ */
|
|
|
|
|
+export function getCompatibilityReport(): string {
|
|
|
|
|
+ const compatibility = checkBrowserCompatibility();
|
|
|
|
|
+ const { browserInfo, supported, incompatibleFeatures } = compatibility;
|
|
|
|
|
+
|
|
|
|
|
+ let report = `浏览器: ${browserInfo.name} ${browserInfo.version}\n`;
|
|
|
|
|
+ report += `设备类型: ${browserInfo.isMobile ? '移动设备' : '桌面设备'}\n`;
|
|
|
|
|
+ report += `录音支持: ${supported ? '✅ 支持' : '❌ 不支持'}\n`;
|
|
|
|
|
+
|
|
|
|
|
+ if (!supported && incompatibleFeatures.length > 0) {
|
|
|
|
|
+ report += `缺少功能: ${incompatibleFeatures.join(', ')}\n`;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // API支持详情
|
|
|
|
|
+ report += '\nAPI支持详情:\n';
|
|
|
|
|
+ report += `- MediaRecorder: ${'MediaRecorder' in window ? '✅' : '❌'}\n`;
|
|
|
|
|
+ report += `- getUserMedia: ${navigator.mediaDevices && typeof navigator.mediaDevices.getUserMedia === 'function' ? '✅' : '❌'}\n`;
|
|
|
|
|
+ report += `- AudioContext: ${window.AudioContext || (window as any).webkitAudioContext ? '✅' : '❌'}\n`;
|
|
|
|
|
+ report += `- HTTPS: ${location.protocol === 'https:' || location.hostname === 'localhost' ? '✅' : '❌'}\n`;
|
|
|
|
|
+
|
|
|
|
|
+ // 音频格式支持
|
|
|
|
|
+ if ('MediaRecorder' in window) {
|
|
|
|
|
+ const formats = [
|
|
|
|
|
+ 'audio/webm;codecs=opus',
|
|
|
|
|
+ 'audio/webm',
|
|
|
|
|
+ 'audio/mp4',
|
|
|
|
|
+ 'audio/ogg;codecs=opus',
|
|
|
|
|
+ 'audio/wav'
|
|
|
|
|
+ ];
|
|
|
|
|
+
|
|
|
|
|
+ report += '\n支持的音频格式:\n';
|
|
|
|
|
+ formats.forEach(format => {
|
|
|
|
|
+ const supported = MediaRecorder.isTypeSupported(format);
|
|
|
|
|
+ report += `- ${format}: ${supported ? '✅' : '❌'}\n`;
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return report;
|
|
|
|
|
+}
|