hookehuyr

fix(AudioPlayer): 增强音频播放控制的健壮性和错误处理

- 在loadAudio方法中添加对歌曲URL的检查,避免无效URL导致的错误
- 为所有播放控制方法添加try-catch块和状态检查
- 改进prevSong/nextSong/selectSong的流程,确保音频实例正确加载后再播放
- 添加更多错误日志帮助调试问题
- 完善播放状态管理,避免状态不一致
...@@ -157,21 +157,45 @@ onMounted(() => { ...@@ -157,21 +157,45 @@ onMounted(() => {
157 157
158 // 核心方法:音频加载 158 // 核心方法:音频加载
159 const loadAudio = async () => { 159 const loadAudio = async () => {
160 - if (!currentSong.value) return 160 + // 如果没有当前歌曲,直接返回
161 + if (!currentSong.value) {
162 + console.warn('没有可播放的歌曲')
163 + return false
164 + }
165 +
166 + // 设置加载状态
161 isLoading.value = true 167 isLoading.value = true
168 +
162 try { 169 try {
170 + // 清理旧的音频实例
163 if (audio.value) { 171 if (audio.value) {
164 audio.value.removeEventListener('timeupdate', updateProgress) 172 audio.value.removeEventListener('timeupdate', updateProgress)
165 audio.value.removeEventListener('ended', handleEnded) 173 audio.value.removeEventListener('ended', handleEnded)
166 } 174 }
175 +
176 + // 检查歌曲URL是否存在
177 + if (!currentSong.value.url) {
178 + console.error('歌曲URL不存在:', currentSong.value)
179 + audio.value = null
180 + return false
181 + }
182 +
183 + // 创建新的音频实例
167 audio.value = new Audio(currentSong.value.url) 184 audio.value = new Audio(currentSong.value.url)
168 audio.value.volume = volume.value / 100 185 audio.value.volume = volume.value / 100
169 audio.value.playbackRate = speed.value 186 audio.value.playbackRate = speed.value
187 +
188 + // 添加事件监听
170 audio.value.addEventListener('timeupdate', updateProgress) 189 audio.value.addEventListener('timeupdate', updateProgress)
171 audio.value.addEventListener('ended', handleEnded) 190 audio.value.addEventListener('ended', handleEnded)
191 +
192 + // 加载音频
172 await audio.value.load() 193 await audio.value.load()
194 + return true
173 } catch (error) { 195 } catch (error) {
174 console.error('加载音频失败:', error) 196 console.error('加载音频失败:', error)
197 + audio.value = null
198 + return false
175 } finally { 199 } finally {
176 isLoading.value = false 200 isLoading.value = false
177 } 201 }
...@@ -182,18 +206,41 @@ const emit = defineEmits(['play', 'pause']) ...@@ -182,18 +206,41 @@ const emit = defineEmits(['play', 'pause'])
182 206
183 // 播放控制:切换播放/暂停状态 207 // 播放控制:切换播放/暂停状态
184 const togglePlay = async () => { 208 const togglePlay = async () => {
209 + // 如果正在加载中,不执行任何操作
185 if (isLoading.value) return 210 if (isLoading.value) return
211 +
186 try { 212 try {
213 + // 如果音频实例不存在,尝试加载
187 if (!audio.value) { 214 if (!audio.value) {
188 - await loadAudio() 215 + const loadSuccess = await loadAudio()
216 + // 如果加载失败,直接返回
217 + if (!loadSuccess || !audio.value) {
218 + console.warn('无法加载音频实例')
219 + return
220 + }
189 } 221 }
222 +
223 + // 根据当前播放状态执行相应操作
190 if (isPlaying.value) { 224 if (isPlaying.value) {
191 - await audio.value?.pause() 225 + // 暂停播放
192 - emit('pause', audio.value) 226 + if (audio.value) {
227 + await audio.value.pause()
228 + emit('pause', audio.value)
229 + }
193 } else { 230 } else {
194 - await audio.value?.play() 231 + // 开始播放
195 - emit('play', audio.value) 232 + if (audio.value) {
233 + try {
234 + await audio.value.play()
235 + emit('play', audio.value)
236 + } catch (playError) {
237 + console.error('播放失败:', playError)
238 + return
239 + }
240 + }
196 } 241 }
242 +
243 + // 切换播放状态
197 isPlaying.value = !isPlaying.value 244 isPlaying.value = !isPlaying.value
198 } catch (error) { 245 } catch (error) {
199 console.error('播放控制失败:', error) 246 console.error('播放控制失败:', error)
...@@ -204,10 +251,14 @@ const togglePlay = async () => { ...@@ -204,10 +251,14 @@ const togglePlay = async () => {
204 defineExpose({ 251 defineExpose({
205 togglePlay, 252 togglePlay,
206 pause: () => { 253 pause: () => {
254 + // 只有在播放状态且音频实例存在时才执行暂停
207 if (isPlaying.value && audio.value) { 255 if (isPlaying.value && audio.value) {
208 audio.value.pause(); 256 audio.value.pause();
209 isPlaying.value = false; 257 isPlaying.value = false;
210 emit('pause', audio.value); 258 emit('pause', audio.value);
259 + } else {
260 + // 如果音频实例不存在或已经暂停,只更新状态
261 + isPlaying.value = false;
211 } 262 }
212 }, 263 },
213 isPlaying: () => isPlaying.value 264 isPlaying: () => isPlaying.value
...@@ -228,41 +279,81 @@ const handleEnded = () => { ...@@ -228,41 +279,81 @@ const handleEnded = () => {
228 279
229 // 控制方法:切换到上一首 280 // 控制方法:切换到上一首
230 const prevSong = async () => { 281 const prevSong = async () => {
231 - if (audio.value) { 282 + try {
283 + // 如果音频实例存在,先暂停并清除
284 + if (audio.value) {
285 + isPlaying.value = false
286 + await audio.value.pause()
287 + audio.value = null
288 + }
289 +
290 + // 计算上一首歌曲的索引
291 + currentIndex.value = (currentIndex.value - 1 + props.songs.length) % props.songs.length
292 +
293 + // 加载新的音频
294 + const loadSuccess = await loadAudio()
295 +
296 + // 确保音频实例加载成功后再播放
297 + if (loadSuccess && audio.value) {
298 + try {
299 + await audio.value.play()
300 + // 使用非线性映射来调整音量变化的灵敏度
301 + const normalizedVolume = Math.pow(volume.value / 100, 2)
302 + audio.value.volume = normalizedVolume
303 + isPlaying.value = true
304 + // 发射播放事件
305 + emit('play', audio.value)
306 + } catch (playError) {
307 + console.error('播放上一首歌曲失败:', playError)
308 + isPlaying.value = false
309 + }
310 + } else {
311 + console.warn('无法加载上一首歌曲')
312 + isPlaying.value = false
313 + }
314 + } catch (error) {
315 + console.error('切换到上一首歌曲失败:', error)
232 isPlaying.value = false 316 isPlaying.value = false
233 - await audio.value.pause()
234 - audio.value = null
235 - }
236 - currentIndex.value = (currentIndex.value - 1 + props.songs.length) % props.songs.length
237 - await loadAudio()
238 - if (audio.value) {
239 - await audio.value.play()
240 - // 使用非线性映射来调整音量变化的灵敏度
241 - const normalizedVolume = Math.pow(volume.value / 100, 2)
242 - audio.value.volume = normalizedVolume
243 - isPlaying.value = true
244 - // 发射播放事件
245 - emit('play', audio.value)
246 } 317 }
247 } 318 }
248 319
249 // 控制方法:切换到下一首 320 // 控制方法:切换到下一首
250 const nextSong = async () => { 321 const nextSong = async () => {
251 - if (audio.value) { 322 + try {
323 + // 如果音频实例存在,先暂停并清除
324 + if (audio.value) {
325 + isPlaying.value = false
326 + await audio.value.pause()
327 + audio.value = null
328 + }
329 +
330 + // 计算下一首歌曲的索引
331 + currentIndex.value = (currentIndex.value + 1) % props.songs.length
332 +
333 + // 加载新的音频
334 + const loadSuccess = await loadAudio()
335 +
336 + // 确保音频实例加载成功后再播放
337 + if (loadSuccess && audio.value) {
338 + try {
339 + await audio.value.play()
340 + // 使用非线性映射来调整音量变化的灵敏度
341 + const normalizedVolume = Math.pow(volume.value / 100, 2)
342 + audio.value.volume = normalizedVolume
343 + isPlaying.value = true
344 + // 发射播放事件
345 + emit('play', audio.value)
346 + } catch (playError) {
347 + console.error('播放下一首歌曲失败:', playError)
348 + isPlaying.value = false
349 + }
350 + } else {
351 + console.warn('无法加载下一首歌曲')
352 + isPlaying.value = false
353 + }
354 + } catch (error) {
355 + console.error('切换到下一首歌曲失败:', error)
252 isPlaying.value = false 356 isPlaying.value = false
253 - await audio.value.pause()
254 - audio.value = null
255 - }
256 - currentIndex.value = (currentIndex.value + 1) % props.songs.length
257 - await loadAudio()
258 - if (audio.value) {
259 - await audio.value.play()
260 - // 使用非线性映射来调整音量变化的灵敏度
261 - const normalizedVolume = Math.pow(volume.value / 100, 2)
262 - audio.value.volume = normalizedVolume
263 - isPlaying.value = true
264 - // 发射播放事件
265 - emit('play', audio.value)
266 } 357 }
267 } 358 }
268 359
...@@ -324,26 +415,46 @@ const closePlaylist = () => { ...@@ -324,26 +415,46 @@ const closePlaylist = () => {
324 415
325 // 选择并播放指定歌曲 416 // 选择并播放指定歌曲
326 const selectSong = async (index) => { 417 const selectSong = async (index) => {
327 - if (audio.value) { 418 + try {
328 - isPlaying.value = false 419 + // 如果音频实例存在,先暂停并清除
329 - await audio.value.pause() 420 + if (audio.value) {
330 - audio.value = null 421 + isPlaying.value = false
331 - } 422 + await audio.value.pause()
332 - currentIndex.value = index 423 + audio.value = null
333 - await loadAudio() 424 + }
334 - if (audio.value) { 425 +
335 - await audio.value.play() 426 + // 设置当前索引
336 - isPlaying.value = true 427 + currentIndex.value = index
337 - // 发射播放事件 428 +
338 - emit('play', audio.value) 429 + // 加载新的音频
339 - // 关闭播放列表 430 + const loadSuccess = await loadAudio()
340 - isPlaylistVisible.value = false 431 +
341 - // 滚动到当前播放的音频 432 + // 确保音频实例加载成功后再播放
342 - const playlistItems = document.querySelector('.playlist-items') 433 + if (loadSuccess && audio.value) {
343 - const activeItem = playlistItems?.querySelector('.active') 434 + try {
344 - if (playlistItems && activeItem) { 435 + await audio.value.play()
345 - activeItem.scrollIntoView({ behavior: 'smooth', block: 'nearest' }) 436 + isPlaying.value = true
437 + // 发射播放事件
438 + emit('play', audio.value)
439 + // 关闭播放列表
440 + isPlaylistVisible.value = false
441 + // 滚动到当前播放的音频
442 + const playlistItems = document.querySelector('.playlist-items')
443 + const activeItem = playlistItems?.querySelector('.active')
444 + if (playlistItems && activeItem) {
445 + activeItem.scrollIntoView({ behavior: 'smooth', block: 'nearest' })
446 + }
447 + } catch (playError) {
448 + console.error('播放选择的歌曲失败:', playError)
449 + isPlaying.value = false
450 + }
451 + } else {
452 + console.warn('无法加载选择的歌曲')
453 + isPlaying.value = false
346 } 454 }
455 + } catch (error) {
456 + console.error('选择歌曲失败:', error)
457 + isPlaying.value = false
347 } 458 }
348 } 459 }
349 460
......