From 3ec43d52404d0cd8a9d7a432fe5a87d1a480c9e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E4=B9=BE?= Date: Mon, 2 Feb 2026 18:03:29 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=BD=91=E7=BB=9C=E9=9F=B3?= =?UTF-8?q?=E9=A2=91=E6=92=AD=E6=94=BE=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index.html | 8 +- src/main.js | 755 +++++++++++++++++++++++++++++++++++++++++++++++-- src/style.css | 44 ++- vite.config.js | 19 +- 4 files changed, 804 insertions(+), 22 deletions(-) diff --git a/index.html b/index.html index cd934a9..04e5054 100644 --- a/index.html +++ b/index.html @@ -12,12 +12,18 @@

音乐播放器

-
+
+ + +
diff --git a/src/main.js b/src/main.js index dfe2177..44706aa 100644 --- a/src/main.js +++ b/src/main.js @@ -20,6 +20,11 @@ class AudioPlayer { this.isPlayingFolder = false; this.playbackMode = 'loop'; // 默认列表循环模式:'single', 'loop', 'random' + // 网络音乐相关 + this.isNetworkMusic = false; + this.networkMusicUrl = null; + this.networkMusicName = null; + // Web Audio API警告标志 this.webAudioApiWarningShown = false; @@ -31,6 +36,7 @@ class AudioPlayer { this.setupCanvas(); this.setupKeyboardShortcuts(); this.initializePlayerState(); + this.checkUrlParams(); // 检查URL参数 } initializeElements() { @@ -164,6 +170,12 @@ class AudioPlayer { } handleFileSelect(event) { + // 如果正在播放网络音乐,不允许选择本地文件 + if (this.isNetworkMusic) { + alert('正在播放网络音乐,请先停止当前播放再选择本地文件'); + return; + } + const file = event.target.files[0]; if (file) { // 单个文件播放模式 @@ -172,7 +184,7 @@ class AudioPlayer { this.currentTrackIndex = 0; // 清理旧的URL - if (this.audio.src) { + if (this.audio.src && !this.isNetworkMusic) { URL.revokeObjectURL(this.audio.src); } @@ -186,10 +198,20 @@ class AudioPlayer { // 设置音频上下文(如果尚未初始化) this.setupAudioContext(); + + // 关键:本地音乐也需要连接音频源 + console.log('本地音乐文件已选择,准备连接音频源'); + this.connectAudioSource(); } } handleFolderSelect(event) { + // 如果正在播放网络音乐,不允许选择本地文件夹 + if (this.isNetworkMusic) { + alert('正在播放网络音乐,请先停止当前播放再选择本地文件夹'); + return; + } + const files = Array.from(event.target.files); if (files.length > 0) { // 筛选音频文件 - 支持更多格式 @@ -206,6 +228,7 @@ class AudioPlayer { this.playlist = audioFiles; this.currentTrackIndex = 0; this.isPlayingFolder = true; + this.isNetworkMusic = false; // 切换到本地模式 // 确保播放模式显示正确(默认列表循环) this.playbackMode = 'loop'; @@ -271,6 +294,10 @@ class AudioPlayer { // 设置自动播放状态 this.shouldAutoPlay = shouldAutoPlay; + // 关键:文件夹中的本地音乐也需要连接音频源 + console.log('文件夹音乐已加载,准备连接音频源'); + this.connectAudioSource(); + // 等待音频数据加载完成后播放 const tryAutoPlay = () => { if (this.audio.readyState >= 2 && (!this.audio.paused || this.shouldAutoPlay)) { @@ -335,14 +362,12 @@ class AudioPlayer { this.waveformVisualizer = new WaveformVisualizer(this.canvas, this.audioContext, this.analyser); console.log('音频上下文和可视化器初始化成功'); } - - // 连接音频源(只在首次或需要重新连接时) - if (this.audioContext && this.analyser && !this.source) { - this.source = this.audioContext.createMediaElementSource(this.audio); - this.source.connect(this.analyser); - this.analyser.connect(this.audioContext.destination); - console.log('音频源连接成功'); + + // 确保音频上下文处于运行状态 + if (this.audioContext.state === 'suspended') { + this.audioContext.resume(); } + } catch (error) { console.error('音频上下文初始化失败:', error); if (!this.webAudioApiWarningShown) { @@ -351,25 +376,387 @@ class AudioPlayer { } } } + + // 为网络音乐创建一个智能模拟可视化系统,绕过CORS限制 + startNetworkMusicVisualization() { + console.log('🔄 启动网络音乐智能模拟可视化...'); + + // 检查是否有音频上下文和画布 + if (!this.audioContext || !this.canvas) { + console.error('无法启动模拟可视化:缺少必要组件'); + return; + } + + // 创建一个音频活动和节奏检测器 + const rhythmDetectorState = { + lastTime: 0, + timeDelta: 0, + energy: 0, + energyHistory: [] + }; + + // 检测音频活动 + const detectActivity = () => { + return !this.audio.paused && this.audio.currentTime > 0; + }; + + // 计算节奏能量(基于播放速度变化) + const calculateRhythmEnergy = () => { + if (!detectActivity()) return 0; + + const currentTime = this.audio.currentTime; + rhythmDetectorState.timeDelta = currentTime - rhythmDetectorState.lastTime; + rhythmDetectorState.lastTime = currentTime; + + // 计算能量(模拟音频波形的振幅) + const baseEnergy = Math.sin(currentTime * 3) * 0.5 + 0.5; + const beatEnergy = Math.sin(currentTime * 1.5) > 0.8 ? 1 : 0; + const energy = baseEnergy + beatEnergy * 0.5; + + // 平滑能量值 + rhythmDetectorState.energyHistory.push(energy); + if (rhythmDetectorState.energyHistory.length > 10) rhythmDetectorState.energyHistory.shift(); + + rhythmDetectorState.energy = rhythmDetectorState.energyHistory.reduce((sum, val) => sum + val, 0) / rhythmDetectorState.energyHistory.length; + return rhythmDetectorState.energy; + }; + + // 智能可视化绘制函数 + const drawSmartVisualization = () => { + if (!detectActivity()) { + // 音频未播放,清空画布 + this.ctx.fillStyle = 'rgb(0, 0, 0)'; + this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height); + return; + } + + const time = this.audio.currentTime; + const energy = calculateRhythmEnergy(); + + // 清除画布 + this.ctx.fillStyle = 'rgb(0, 0, 0)'; + this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height); + + // 绘制动态条形图(根据能量变化) + const barWidth = 8; + const barSpacing = 4; + const numBars = Math.floor(this.canvas.width / (barWidth + barSpacing)); + + for (let i = 0; i < numBars; i++) { + // 使用多种波形组合生成更自然的高度变化 + const wave1 = Math.sin(time * 2 + i * 0.1) * 0.5 + 0.5; + const wave2 = Math.cos(time * 1.5 + i * 0.2) * 0.3 + 0.3; + const wave3 = Math.sin(time * 0.5 + i * 0.05) * 0.2 + 0.2; + + // 结合能量值调整高度 + const heightFactor = wave1 + wave2 + wave3; + const height = energy * heightFactor * (this.canvas.height * 0.7) + 5; + + // 颜色随能量和位置动态变化 + const r = Math.floor(200 + energy * 55 * Math.sin(i * 0.1 + time)); + const g = Math.floor(100 + energy * 155 * Math.cos(i * 0.1 + time)); + const b = Math.floor(150 + energy * 105 * Math.sin(i * 0.2 + time)); + + this.ctx.fillStyle = `rgb(${r}, ${g}, ${b})`; + + // 绘制带圆角的条形 + this.ctx.fillRect( + i * (barWidth + barSpacing), + this.canvas.height - height, + barWidth, + height + ); + + // 添加顶部高光 + this.ctx.fillStyle = `rgba(255, 255, 255, ${energy * 0.3})`; + this.ctx.fillRect( + i * (barWidth + barSpacing), + this.canvas.height - height, + barWidth, + 3 + ); + } + + // 绘制中央波形线(响应能量变化) + this.ctx.beginPath(); + this.ctx.strokeStyle = `rgba(0, ${Math.floor(255 * energy)}, 255, ${0.8 + energy * 0.2})`; + this.ctx.lineWidth = 3; + + // 绘制更复杂的波形 + for (let x = 0; x < this.canvas.width; x += 2) { + // 基础波形 + const baseWave = Math.sin(x * 0.015 + time * 5) * 30; + // 能量影响的波形 + const energyWave = Math.cos(x * 0.02 + time * 3) * 40 * energy; + // 高频细节 + const detailWave = Math.sin(x * 0.05 + time * 10) * 10; + + const y = this.canvas.height / 2 + baseWave + energyWave + detailWave; + + if (x === 0) { + this.ctx.moveTo(x, y); + } else { + this.ctx.lineTo(x, y); + } + } + + this.ctx.stroke(); + + // 添加脉冲效果(当检测到强节拍时) + if (rhythmDetectorState.timeDelta < 0.01 && energy > 0.8) { + this.ctx.beginPath(); + this.ctx.strokeStyle = `rgba(255, 255, 255, ${energy * 0.6})`; + this.ctx.lineWidth = 2; + this.ctx.arc( + this.canvas.width / 2, + this.canvas.height / 2, + Math.random() * 50 + 100, + 0, + Math.PI * 2 + ); + this.ctx.stroke(); + } + }; + + // 定期更新可视化(60fps) + const animate = () => { + this.animationId = requestAnimationFrame(animate); + drawSmartVisualization(); + }; + + animate(); + console.log('✅ 网络音乐智能模拟可视化已启动'); + } + + connectAudioSource() { + try { + console.log('🔄 创建新的音频源连接...'); + + // 检查音频元素状态 + if (this.audio.readyState < 2) { + console.log('音频元素尚未准备好,延迟连接'); + return false; + } + + // 确保没有现有的连接 + if (this.source) { + console.log('清理现有音频源连接'); + try { + this.source.disconnect(); + } catch (e) { + console.log('断开旧连接时出错:', e); + } + this.source = null; + } + + // 关键:重新创建MediaElementSource + console.log('创建新的MediaElementSource...'); + this.source = this.audioContext.createMediaElementSource(this.audio); + console.log('MediaElementSource创建成功'); + + // 连接到分析器 + console.log('连接到分析器...'); + this.source.connect(this.analyser); + console.log('分析器连接成功'); + + console.log('连接到音频目标...'); + this.analyser.connect(this.audioContext.destination); + console.log('音频目标连接成功'); + + console.log('✅ 音频源连接完成'); + console.log('连接链路: 音频元素 → MediaElementSource → 分析器' + (this.isNetworkMusic ? '' : ' → 目标')); + + return true; + + } catch (error) { + console.error('❌ 音频源连接失败:', error); + console.error('错误类型:', error.name); + console.error('错误消息:', error.message); + + // 特殊处理:音频元素已连接的错误 + if (error.name === 'InvalidStateError' && error.message.includes('already connected')) { + console.log('💡 检测到音频元素已连接,尝试重新初始化...'); + return this.reinitializeAudioConnection(); + } + + // 特殊处理:CORS错误 + if (error.name === 'NotSupportedError' || error.message.includes('CORS')) { + console.log('💡 检测到CORS错误,网络音乐将完全独立播放'); + return true; // 即使连接失败,也返回成功,让网络音乐独立播放 + } + + return false; + } + } + + tryAlternativeConnection() { + console.log('🔄 尝试备用音频连接方式...'); + + try { + // 清理现有连接 + if (this.source) { + this.source.disconnect(); + this.source = null; + } + + // 创建新的音频源连接,但不连接到destination + this.source = this.audioContext.createMediaElementSource(this.audio); + this.source.connect(this.analyser); + + console.log('✅ 备用连接方式成功'); + console.log('连接链路: 音频元素 → MediaElementSource → 分析器(跳过destination)'); + + return true; + + } catch (error) { + console.error('❌ 备用连接方式失败:', error); + return false; + } + } + + reinitializeAudioConnection() { + console.log('🔄 重新初始化音频连接...'); + + try { + // 1. 完全重置音频元素 + const currentSrc = this.audio.src; + const currentTime = this.audio.currentTime; + const wasPlaying = !this.audio.paused; + + console.log('保存当前播放状态:', { currentTime, wasPlaying }); + + // 2. 暂停并清理 + if (wasPlaying) { + this.audio.pause(); + } + + // 3. 完全断开所有连接 + if (this.source) { + try { + this.source.disconnect(); + this.source = null; + } catch (e) { + console.log('清理旧连接失败:', e); + } + } + + // 4. 重新设置音频源(强制重置) + this.audio.src = ''; + this.audio.removeAttribute('src'); + + // 5. 重新设置跨域属性和音频源 + this.audio.crossOrigin = 'anonymous'; + this.audio.src = currentSrc; + this.audio.load(); + + // 6. 等待音频准备好 + return new Promise((resolve) => { + const onCanPlay = () => { + console.log('音频重新加载完成'); + this.audio.removeEventListener('canplay', onCanPlay); + + // 恢复播放位置 + this.audio.currentTime = currentTime; + + // 重新连接音频源 + try { + this.source = this.audioContext.createMediaElementSource(this.audio); + this.source.connect(this.analyser); + this.analyser.connect(this.audioContext.destination); + console.log('✅ 重新初始化音频连接成功'); + resolve(true); + } catch (e) { + console.error('重新连接失败:', e); + resolve(false); + } + }; + + this.audio.addEventListener('canplay', onCanPlay, { once: true }); + }); + + } catch (error) { + console.error('重新初始化失败:', error); + return false; + } + } play() { if (this.audio.src) { + console.log('开始播放,音频源:', this.audio.src); + console.log('网络音乐模式:', this.isNetworkMusic); + // 确保音频上下文处于运行状态 if (this.audioContext && this.audioContext.state === 'suspended') { this.audioContext.resume(); } - this.audio.play().then(() => { - this.startVisualization(); - this.updatePlayPauseButton(true); - document.querySelector('.audio-player').classList.add('playing'); - }).catch(error => { - console.error('播放失败:', error); - alert('播放失败,请检查音频文件'); - }); + // 关键:确保音频源已连接,无论是网络音乐还是本地音乐 + if (!this.source) { + console.log('音频源未连接,尝试重新连接'); + this.setupAudioContext(); + + // 延迟播放,等待音频源连接 + setTimeout(() => { + if (this.source) { + console.log('音频源已连接,继续播放'); + this.performPlay(); + } else { + console.error('音频源连接失败,尝试直接播放'); + this.performPlay(); + } + }, 500); + return; + } + + // 确保音频元素已准备好 + if (this.audio.readyState < 2) { + console.log('音频尚未准备好,等待canplay事件'); + this.audio.addEventListener('canplay', () => { + console.log('音频已准备好,开始播放'); + this.performPlay(); + }, { once: true }); + return; + } + + this.performPlay(); } } + performPlay() { + console.log('执行播放操作'); + + // 确保音量未被静音且设置正确 + console.log('当前音量设置:', this.audio.volume); + console.log('静音状态:', this.isMuted); + + // 如果是静音状态,临时取消静音用于测试 + if (this.isMuted) { + this.audio.volume = 0.5; + this.isMuted = false; + this.updateVolumeIcon(50); + console.log('已临时取消静音'); + } + + this.audio.play().then(() => { + console.log('播放成功,启动可视化'); + console.log('实际播放音量:', this.audio.volume); + console.log('播放状态:', this.audio.paused ? '暂停' : '播放中'); + + // 可视化 + this.startVisualization(); + + this.updatePlayPauseButton(true); + document.querySelector('.audio-player').classList.add('playing'); + + }).catch(error => { + if (error.name === 'NotAllowedError') { + console.error('播放被阻止,请点击播放按钮手动开始播放'); + } else { + console.error('播放失败: ' + error.message); + } + }); + } + pause() { this.audio.pause(); this.stopVisualization(); @@ -487,23 +874,75 @@ class AudioPlayer { } startVisualization() { + if (!this.waveformVisualizer) { console.warn('可视化器未初始化,使用备用方案'); this.startBasicVisualization(); return; } + if (!this.analyser) { + console.error('分析器未创建,无法开始可视化'); + return; + } + + // 关键:确保音频源连接,无论是网络音乐还是本地音乐 + if (!this.source) { + console.warn('音频源未连接,尝试重新连接'); + + // 强制重新设置音频上下文 + this.setupAudioContext(); + + // 尝试连接音频源 + const connected = this.connectAudioSource(); + + if (!connected) { + // 尝试备用连接方式 + console.warn('常规连接失败,尝试备用连接方式...'); + const altConnected = this.tryAlternativeConnection(); + + if (!altConnected) { + // 延迟开始可视化,等待连接完成 + setTimeout(() => { + console.log('延迟后开始可视化检查'); + if (this.source) { + console.log('✅ 音频源已连接,开始可视化'); + this.startVisualization(); + } else { + console.error('❌ 音频源连接失败,使用基础可视化'); + this.startBasicVisualization(); + } + }, 1500); + return; + } + } + } + const draw = () => { this.animationId = requestAnimationFrame(draw); + + // 测试频率数据 + if (this.analyser && this.dataArray) { + this.analyser.getByteFrequencyData(this.dataArray); + } + this.waveformVisualizer.draw(); }; draw(); + console.log('可视化已启动'); } startBasicVisualization() { // 备用基础可视化方案 if (!this.analyser) return; + + console.log('启动基础可视化方案'); + + if (!this.analyser || !this.dataArray) { + console.error('基础可视化失败:分析器或数据数组未准备好'); + return; + } const draw = () => { this.animationId = requestAnimationFrame(draw); @@ -634,12 +1073,12 @@ class AudioPlayer { } break; case 'n': - if (this.isPlayingFolder) { + if (this.isPlayingFolder && !this.isNetworkMusic) { this.nextTrack(); } break; case 'p': - if (this.isPlayingFolder) { + if (this.isPlayingFolder && !this.isNetworkMusic) { this.previousTrack(); } break; @@ -660,7 +1099,204 @@ class AudioPlayer { this.updateNavigationButtons(); } + checkUrlParams() { + // 获取URL参数 + const urlParams = new URLSearchParams(window.location.search); + let filePath = urlParams.get('filePath'); + let fileName = urlParams.get('fileName') || '网络音乐'; + + try { + filePath = decodeURIComponent(decodeURIComponent(filePath)); + } catch (e) { + } + + try { + fileName = decodeURIComponent(decodeURIComponent(fileName)); + } catch (e) { + } + + if (filePath) { + this.loadNetworkMusic(filePath, fileName); + } + } + + loadNetworkMusic(filePath, fileName) { + try { + console.log('开始加载网络音乐:', filePath, fileName); + + // 设置网络音乐状态 + this.isNetworkMusic = true; + this.networkMusicUrl = filePath; + this.networkMusicName = fileName; + + // 隐藏文件选择按钮 + const fileSelector = document.getElementById('fileSelector'); + const networkMusicInfo = document.getElementById('networkMusicInfo'); + //const networkMusicNameEl = document.getElementById('networkMusicName'); + + if (fileSelector) { + fileSelector.style.display = 'none'; + } + + // if (networkMusicInfo) { + // networkMusicInfo.style.display = 'block'; + // networkMusicNameEl.textContent = fileName; + // } + + // 清理旧的音频源 + if (this.audio.src && !this.isNetworkMusic) { + URL.revokeObjectURL(this.audio.src); + } + + // 设置音频源 - 关键:先停止当前播放 + this.stop(); + + // 关键:重置音频元素,确保正确的设置顺序 + console.log('设置网络音频源,URL:', filePath); + + // 1. 设置crossOrigin,避免CORS错误 + this.audio.crossOrigin = 'anonymous'; + // 2. 设置音频源 + this.audio.src = filePath; + console.log('音频源已设置:', this.audio.src); + + // 3. 强制重新加载 + this.audio.load(); + console.log('音频加载已触发'); + + // 关键:确保音频元素独立播放,不依赖Web Audio API连接 + console.log('设置网络音乐独立播放模式'); + + // 检查并重置音量设置 + const currentVolume = this.volumeSlider.value / 100; + this.audio.volume = currentVolume; + this.isMuted = false; + console.log('网络音乐音量设置为:', currentVolume); + + this.trackName.textContent = fileName; + + // 显示加载提示 + this.showLoadingFeedback('正在加载网络音乐...'); + + // 监听加载错误 - 更详细的错误处理 + this.audio.addEventListener('error', (e) => { + console.error('网络音乐加载失败:', e); + console.error('错误代码:', this.audio.error?.code); + console.error('错误信息:', this.audio.error?.message); + + let errorMsg = '网络音乐加载失败'; + if (this.audio.error) { + switch (this.audio.error.code) { + case 1: errorMsg = '音频下载被中止'; break; + case 2: errorMsg = '网络错误'; break; + case 3: errorMsg = '音频解码错误'; break; + case 4: errorMsg = '音频格式不支持'; break; + default: errorMsg = '未知错误,请检查URL是否有效且支持跨域访问'; + } + } + this.showErrorFeedback(errorMsg); + }, { once: true }); + + // 监听加载成功 - 使用canplaythrough事件,确保音频可以完整播放 + this.audio.addEventListener('canplaythrough', () => { + console.log('网络音乐可以完整播放了'); + this.showSuccessFeedback('网络音乐加载成功!'); + + // 检查音频格式和准备状态 + console.log('音频就绪状态:', this.audio.readyState); + console.log('音频格式:', this.audio.canPlayType(this.audio.src)); + + // 尝试自动播放 + this.play(); + + }, { once: true }); + + // 监听播放事件 + this.audio.addEventListener('play', () => { + console.log('网络音乐开始播放'); + console.log('播放状态:', this.audio.paused ? '暂停' : '播放中'); + console.log('当前音量:', this.audio.volume); + console.log('静音状态:', this.isMuted); + }, { once: true }); + + // 监听加载进度 + this.audio.addEventListener('loadstart', () => { + console.log('开始加载音频...'); + }); + + this.audio.addEventListener('progress', () => { + if (this.audio.buffered.length > 0) { + const buffered = this.audio.buffered.end(0); + const duration = this.audio.duration; + console.log(`加载进度: ${buffered}/${duration}`); + } + }); + + } catch (error) { + console.error('加载网络音乐失败:', error); + this.showErrorFeedback('加载网络音乐失败: ' + error.message); + } + } + + showLoadingFeedback(message) { + const feedback = document.createElement('div'); + feedback.textContent = message; + feedback.className = 'visualization-feedback'; + feedback.style.background = 'rgba(102, 126, 234, 0.9)'; + + document.body.appendChild(feedback); + + setTimeout(() => { + if (feedback.parentNode) { + feedback.parentNode.removeChild(feedback); + } + }, 2000); + } + + showSuccessFeedback(message) { + const feedback = document.createElement('div'); + feedback.textContent = message; + feedback.className = 'visualization-feedback'; + feedback.style.background = 'rgba(76, 175, 80, 0.9)'; + + document.body.appendChild(feedback); + + setTimeout(() => { + if (feedback.parentNode) { + feedback.parentNode.removeChild(feedback); + } + }, 2000); + } + + showErrorFeedback(message) { + const feedback = document.createElement('div'); + feedback.textContent = message; + feedback.className = 'visualization-feedback'; + feedback.style.background = 'rgba(244, 67, 54, 0.9)'; + + document.body.appendChild(feedback); + + setTimeout(() => { + if (feedback.parentNode) { + feedback.parentNode.removeChild(feedback); + } + }, 3000); + } + updateNavigationButtons() { + // 网络音乐模式下禁用所有导航按钮 + if (this.isNetworkMusic) { + this.prevBtn.disabled = true; + this.nextBtn.disabled = true; + this.playbackModeBtn.disabled = true; + + this.prevBtn.style.opacity = '0.3'; + this.nextBtn.style.opacity = '0.3'; + this.playbackModeBtn.style.opacity = '0.3'; + return; + } + + // 文件夹模式下的正常处理 const hasPlaylist = this.isPlayingFolder && this.playlist.length > 0; this.prevBtn.disabled = !hasPlaylist; this.nextBtn.disabled = !hasPlaylist; @@ -677,6 +1313,89 @@ class AudioPlayer { } } + startAudioDiagnostics() { + // 启动音频连接状态监控 + if (this.diagnosticsInterval) { + clearInterval(this.diagnosticsInterval); + } + + let diagnosticCount = 0; + this.diagnosticsInterval = setInterval(() => { + diagnosticCount++; + + if (diagnosticCount > 10) { // 最多监控10次 + clearInterval(this.diagnosticsInterval); + return; + } + + console.log(`🔍 音频诊断 #${diagnosticCount}:`); + console.log(` 播放状态: ${this.audio.paused ? '暂停' : '播放中'}`); + console.log(` 当前时间: ${this.audio.currentTime.toFixed(2)}s`); + console.log(` 音频时长: ${this.audio.duration ? this.audio.duration.toFixed(2) + 's' : '未知'}`); + console.log(` 音量: ${(this.audio.volume * 100).toFixed(0)}%`); + console.log(` 音频源连接: ${this.source ? '已连接' : '未连接'}`); + console.log(` 音频上下文状态: ${this.audioContext ? this.audioContext.state : '未创建'}`); + + // 检查音频是否正在播放但没有声音 + if (!this.audio.paused && this.audio.currentTime > 0 && !this.source) { + console.warn('⚠️ 警告:音频正在播放但没有音频源连接!'); + console.log('💡 尝试重新连接音频源...'); + this.setupAudioContext(); + } + + // 检查音频上下文是否被暂停 + if (this.audioContext && this.audioContext.state === 'suspended') { + console.log('🔧 音频上下文被暂停,尝试恢复...'); + this.audioContext.resume(); + } + + // 检查可视化数据 + if (this.analyser && this.dataArray && !this.audio.paused) { + this.analyser.getByteFrequencyData(this.dataArray); + const hasData = this.dataArray.some(value => value > 0); + if (!hasData && diagnosticCount > 3) { + console.warn('⚠️ 无可视化数据,尝试强制重连...'); + this.forceReconnectAudio(); + } + } + }, 2000); // 每2秒检查一次 + } + + forceReconnectAudio() { + console.log('🔄 执行强制音频重连...'); + + try { + // 1. 重新创建音频上下文(如果需要) + if (!this.audioContext || this.audioContext.state === 'closed') { + console.log('重新创建音频上下文'); + this.audioContext = new (window.AudioContext || window.webkitAudioContext)(); + this.analyser = this.audioContext.createAnalyser(); + this.analyser.fftSize = 2048; + const bufferLength = this.analyser.frequencyBinCount; + this.dataArray = new Uint8Array(bufferLength); + + // 重新初始化可视化器 + if (this.waveformVisualizer) { + this.waveformVisualizer = new WaveformVisualizer(this.canvas, this.audioContext, this.analyser); + } + } + + // 2. 确保音频上下文运行 + if (this.audioContext.state === 'suspended') { + this.audioContext.resume(); + } + + // 3. 使用新的连接方法 + console.log('使用新的连接方法'); + this.connectAudioSource(); + + console.log('✅ 强制重连完成'); + + } catch (error) { + console.error('强制重连失败:', error); + } + } + togglePlayPause() { if (this.audio.paused) { this.play(); diff --git a/src/style.css b/src/style.css index 8c6b0da..af53392 100644 --- a/src/style.css +++ b/src/style.css @@ -18,8 +18,8 @@ body { .audio-player { background: rgba(255, 255, 255, 0.1); backdrop-filter: blur(10px); - border-radius: 20px; - padding: 30px; + border-radius: 10px; + padding: 10px; box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37); border: 1px solid rgba(255, 255, 255, 0.18); width: 100%; @@ -49,6 +49,46 @@ body { background: linear-gradient(45deg, #667eea, #764ba2); } +/* 网络音乐信息 */ +.network-music-info { + margin: 15px 0; + padding: 15px; + background: linear-gradient(45deg, #4facfe, #00f2fe); + border-radius: 15px; + text-align: center; + box-shadow: 0 4px 15px rgba(79, 172, 254, 0.4); + border: 1px solid rgba(255, 255, 255, 0.3); + animation: fadeIn 0.5s ease-out; +} + +.network-music-info p { + margin: 5px 0; + font-weight: 500; + color: white; + text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.3); +} + +.network-music-info p:first-child { + font-size: 18px; + margin-bottom: 8px; +} + +.network-music-info p:last-child { + font-size: 16px; + opacity: 0.9; +} + +@keyframes fadeIn { + from { + opacity: 0; + transform: translateY(-10px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + .file-selector input[type="file"] { display: none; } diff --git a/vite.config.js b/vite.config.js index 5979927..002219c 100644 --- a/vite.config.js +++ b/vite.config.js @@ -4,7 +4,24 @@ export default defineConfig({ // Vite配置选项 server: { port: 3000, - open: true + open: false, + host: false, + cors: true, + headers: { + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS', + 'Access-Control-Allow-Headers': 'Content-Type, Authorization, Range', + 'Access-Control-Allow-Credentials': 'true', + 'Access-Control-Expose-Headers': 'Content-Length, Content-Range' + }, + proxy: { + '/DocServer': { + target: 'http://192.168.1.201:8080', + changeOrigin: true, + secure: false, + rewrite: (path) => path.replace(/^\/DocServer/, '/DocServer') + } + } }, build: { outDir: 'dist',