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 + // 暂停播放
226 + if (audio.value) {
227 + await audio.value.pause()
192 emit('pause', audio.value) 228 emit('pause', audio.value)
229 + }
193 } else { 230 } else {
194 - await audio.value?.play() 231 + // 开始播放
232 + if (audio.value) {
233 + try {
234 + await audio.value.play()
195 emit('play', audio.value) 235 emit('play', audio.value)
236 + } catch (playError) {
237 + console.error('播放失败:', playError)
238 + return
239 + }
196 } 240 }
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,14 +279,23 @@ const handleEnded = () => { ...@@ -228,14 +279,23 @@ const handleEnded = () => {
228 279
229 // 控制方法:切换到上一首 280 // 控制方法:切换到上一首
230 const prevSong = async () => { 281 const prevSong = async () => {
282 + try {
283 + // 如果音频实例存在,先暂停并清除
231 if (audio.value) { 284 if (audio.value) {
232 isPlaying.value = false 285 isPlaying.value = false
233 await audio.value.pause() 286 await audio.value.pause()
234 audio.value = null 287 audio.value = null
235 } 288 }
289 +
290 + // 计算上一首歌曲的索引
236 currentIndex.value = (currentIndex.value - 1 + props.songs.length) % props.songs.length 291 currentIndex.value = (currentIndex.value - 1 + props.songs.length) % props.songs.length
237 - await loadAudio() 292 +
238 - if (audio.value) { 293 + // 加载新的音频
294 + const loadSuccess = await loadAudio()
295 +
296 + // 确保音频实例加载成功后再播放
297 + if (loadSuccess && audio.value) {
298 + try {
239 await audio.value.play() 299 await audio.value.play()
240 // 使用非线性映射来调整音量变化的灵敏度 300 // 使用非线性映射来调整音量变化的灵敏度
241 const normalizedVolume = Math.pow(volume.value / 100, 2) 301 const normalizedVolume = Math.pow(volume.value / 100, 2)
...@@ -243,19 +303,39 @@ const prevSong = async () => { ...@@ -243,19 +303,39 @@ const prevSong = async () => {
243 isPlaying.value = true 303 isPlaying.value = true
244 // 发射播放事件 304 // 发射播放事件
245 emit('play', audio.value) 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)
316 + isPlaying.value = false
246 } 317 }
247 } 318 }
248 319
249 // 控制方法:切换到下一首 320 // 控制方法:切换到下一首
250 const nextSong = async () => { 321 const nextSong = async () => {
322 + try {
323 + // 如果音频实例存在,先暂停并清除
251 if (audio.value) { 324 if (audio.value) {
252 isPlaying.value = false 325 isPlaying.value = false
253 await audio.value.pause() 326 await audio.value.pause()
254 audio.value = null 327 audio.value = null
255 } 328 }
329 +
330 + // 计算下一首歌曲的索引
256 currentIndex.value = (currentIndex.value + 1) % props.songs.length 331 currentIndex.value = (currentIndex.value + 1) % props.songs.length
257 - await loadAudio() 332 +
258 - if (audio.value) { 333 + // 加载新的音频
334 + const loadSuccess = await loadAudio()
335 +
336 + // 确保音频实例加载成功后再播放
337 + if (loadSuccess && audio.value) {
338 + try {
259 await audio.value.play() 339 await audio.value.play()
260 // 使用非线性映射来调整音量变化的灵敏度 340 // 使用非线性映射来调整音量变化的灵敏度
261 const normalizedVolume = Math.pow(volume.value / 100, 2) 341 const normalizedVolume = Math.pow(volume.value / 100, 2)
...@@ -263,6 +343,17 @@ const nextSong = async () => { ...@@ -263,6 +343,17 @@ const nextSong = async () => {
263 isPlaying.value = true 343 isPlaying.value = true
264 // 发射播放事件 344 // 发射播放事件
265 emit('play', audio.value) 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)
356 + isPlaying.value = false
266 } 357 }
267 } 358 }
268 359
...@@ -324,14 +415,23 @@ const closePlaylist = () => { ...@@ -324,14 +415,23 @@ const closePlaylist = () => {
324 415
325 // 选择并播放指定歌曲 416 // 选择并播放指定歌曲
326 const selectSong = async (index) => { 417 const selectSong = async (index) => {
418 + try {
419 + // 如果音频实例存在,先暂停并清除
327 if (audio.value) { 420 if (audio.value) {
328 isPlaying.value = false 421 isPlaying.value = false
329 await audio.value.pause() 422 await audio.value.pause()
330 audio.value = null 423 audio.value = null
331 } 424 }
425 +
426 + // 设置当前索引
332 currentIndex.value = index 427 currentIndex.value = index
333 - await loadAudio() 428 +
334 - if (audio.value) { 429 + // 加载新的音频
430 + const loadSuccess = await loadAudio()
431 +
432 + // 确保音频实例加载成功后再播放
433 + if (loadSuccess && audio.value) {
434 + try {
335 await audio.value.play() 435 await audio.value.play()
336 isPlaying.value = true 436 isPlaying.value = true
337 // 发射播放事件 437 // 发射播放事件
...@@ -344,6 +444,17 @@ const selectSong = async (index) => { ...@@ -344,6 +444,17 @@ const selectSong = async (index) => {
344 if (playlistItems && activeItem) { 444 if (playlistItems && activeItem) {
345 activeItem.scrollIntoView({ behavior: 'smooth', block: 'nearest' }) 445 activeItem.scrollIntoView({ behavior: 'smooth', block: 'nearest' })
346 } 446 }
447 + } catch (playError) {
448 + console.error('播放选择的歌曲失败:', playError)
449 + isPlaying.value = false
450 + }
451 + } else {
452 + console.warn('无法加载选择的歌曲')
453 + isPlaying.value = false
454 + }
455 + } catch (error) {
456 + console.error('选择歌曲失败:', error)
457 + isPlaying.value = false
347 } 458 }
348 } 459 }
349 460
......