music / src /App.vue
ahutchen's picture
feat(player): 优化播放逻辑并添加状态过期处理
25e7862
<template>
<div id="app">
<!-- PC端侧边栏导航 -->
<DesktopSidebar />
<!-- 主内容区域 -->
<main class="main-content">
<router-view v-slot="{ Component, route }">
<keep-alive>
<component
:is="Component"
v-if="route.meta?.keepAlive"
:key="route.name"
/>
</keep-alive>
<component
:is="Component"
v-if="!route.meta?.keepAlive"
:key="route.name"
/>
</router-view>
</main>
<!-- 迷你播放器 -->
<MiniPlayer
v-show="!shouldHideMiniPlayer"
@openFullPlayer="showFullPlayer = true"
@togglePlay="togglePlay"
@playNext="playNext"
@playPrevious="playPrevious"
/>
<!-- 底部导航栏 -->
<AppTabBar />
<!-- 全屏播放器 -->
<FullPlayerPage
v-if="showFullPlayer"
@close="showFullPlayer = false"
@seek="handleSeek"
@togglePlay="togglePlay"
/>
<!-- 音频元素 -->
<audio
ref="audioRef"
preload="metadata"
@loadedmetadata="handleLoadedMetadata"
@timeupdate="handleTimeUpdate"
@ended="handleEnded"
@play="handlePlay"
@pause="handlePause"
@error="handleError"
/>
<!-- 通知组件 -->
<Toast />
</div>
</template>
<script setup>
import { ref, computed, onMounted, onBeforeUnmount, watch } from 'vue'
import { useRoute } from 'vue-router'
import { usePlayerStore } from '@/stores/player'
import { usePlayQueueStore } from '@/stores/playqueue'
import { useSearchStore } from '@/stores/search'
import { useFavoritesStore } from '@/stores/favorites'
import { useHistoryStore } from '@/stores/history'
import { useSettingsStore } from '@/stores/settings'
import { musicApi, utils } from '@/services/musicApi'
import mediaSessionManager from '@/utils/mediaSession'
import DesktopSidebar from '@/components/layout/DesktopSidebar.vue'
import AppTabBar from '@/components/layout/AppTabBar.vue'
import MiniPlayer from '@/components/layout/MiniPlayer.vue'
import FullPlayerPage from '@/views/FullPlayerPage.vue'
import Toast from '@/components/common/Toast.vue'
// Router
const route = useRoute()
// Store
const playerStore = usePlayerStore()
const playQueueStore = usePlayQueueStore()
const searchStore = useSearchStore()
const favoritesStore = useFavoritesStore()
const historyStore = useHistoryStore()
const settingsStore = useSettingsStore()
// 响应式数据
const audioRef = ref(null)
const showFullPlayer = ref(false)
const isLoading = ref(false)
// 计算属性
const currentSong = computed(() => playerStore.currentSong)
const isPlaying = computed(() => playerStore.isPlaying)
// 判断是否应该隐藏迷你播放器(在全屏播放页面时隐藏)
const shouldHideMiniPlayer = computed(() => {
return route.meta?.fullScreen || false
})
// 播放控制
const togglePlay = async () => {
if (!currentSong.value) {
console.log('没有歌曲信息')
return
}
if (isPlaying.value) {
if (audioRef.value) {
audioRef.value.pause()
}
playerStore.setPlayingState(false)
} else {
// 如果没有音频源,需要先加载歌曲
if (!playerStore.audioSrc || !audioRef.value.src || audioRef.value.src === location.href) {
console.log('没有音频源,开始加载歌曲')
await loadAndPlaySong(currentSong.value)
return
}
// 有音频源,直接播放
try {
if (!audioRef.value) {
console.log('音频元素不可用')
return
}
// 确保音频元素有正确的src
if (audioRef.value.src !== playerStore.audioSrc) {
audioRef.value.src = playerStore.audioSrc
audioRef.value.load()
}
await audioRef.value.play()
playerStore.setPlayingState(true)
console.log('开始播放:', currentSong.value.name)
} catch (error) {
console.error('播放失败:', error)
showNotification('播放失败,请重试', 'error')
}
}
}
const playNext = () => {
const nextSong = playQueueStore.playNext()
if (nextSong) {
// 强制从头开始播放下一首歌曲
playerStore.setCurrentTime(0)
loadAndPlaySong(nextSong)
}
}
const playPrevious = () => {
const prevSong = playQueueStore.playPrevious()
if (prevSong) {
// 强制从头开始播放上一首歌曲
playerStore.setCurrentTime(0)
loadAndPlaySong(prevSong)
}
}
// 快进到指定时间
const handleSeek = (time) => {
if (audioRef.value && isFinite(time) && time >= 0 && time <= (playerStore.duration || 0)) {
// 立即更新播放器时间
audioRef.value.currentTime = time
playerStore.setCurrentTime(time)
// 如果当前没有播放,且有音频源,尝试播放
if (!isPlaying.value && playerStore.audioSrc) {
audioRef.value.play().then(() => {
playerStore.setPlayingState(true)
console.log('拖动进度条后开始播放:', time)
}).catch(error => {
console.log('自动播放被阻止:', error)
})
}
console.log('跳转到时间:', time)
}
}
// 加载并播放歌曲
const loadAndPlaySong = async (song) => {
if (!song || isLoading.value) return
isLoading.value = true
try {
// 获取音乐链接 - 修复参数顺序:source, id, quality
const quality = settingsStore.getSetting('defaultQuality') || '320'
const result = await musicApi.getMusicUrl(song.source, song.id, quality)
if (result) {
playerStore.setAudioSrc(result)
// 强制重置播放进度为0(所有切歌都从头开始)
playerStore.setCurrentTime(0)
playerStore.setDuration(0)
console.log('切换歌曲,强制从头开始播放:', song.name)
// 更新音频元素
if (audioRef.value) {
audioRef.value.src = result
audioRef.value.load()
}
// 添加到播放历史
historyStore.addToHistory(song)
console.log('歌曲加载成功:', song.name, result)
// 等待音频元数据加载后再尝试播放
if (audioRef.value) {
const tryPlay = async () => {
try {
if (audioRef.value.readyState >= 1) { // 有足够数据可以开始播放
await audioRef.value.play()
playerStore.setPlayingState(true)
console.log('开始播放:', song.name)
}
} catch (error) {
console.log('自动播放被阻止,用户需要手动播放')
playerStore.setPlayingState(false)
}
}
if (audioRef.value.readyState >= 1) {
tryPlay()
} else {
audioRef.value.addEventListener('loadeddata', tryPlay, { once: true })
}
}
} else {
showNotification('无法获取播放链接', 'error')
}
} catch (error) {
console.error('加载歌曲失败:', error)
showNotification('加载歌曲失败', 'error')
} finally {
isLoading.value = false
}
}
// 音频事件处理
const handleLoadedMetadata = () => {
if (audioRef.value) {
const duration = audioRef.value.duration
if (isFinite(duration)) {
playerStore.setDuration(duration)
// 恢复播放进度
const savedTime = playerStore.currentTime
if (savedTime > 0 && savedTime < duration && settingsStore.getSetting('rememberProgress')) {
audioRef.value.currentTime = savedTime
}
}
}
}
const handleTimeUpdate = () => {
if (audioRef.value && isFinite(audioRef.value.currentTime)) {
playerStore.setCurrentTime(audioRef.value.currentTime)
// 更新Media Session位置信息
if (settingsStore.getSetting('mediaSession') && audioRef.value.duration) {
mediaSessionManager.setPositionState(
audioRef.value.duration,
audioRef.value.currentTime
)
}
}
}
const handleEnded = () => {
if (settingsStore.getSetting('autoNext')) {
playNext()
} else {
playerStore.setPlayingState(false)
}
}
const handlePlay = async () => {
playerStore.setPlayingState(true)
// 使用新的Media Session管理器
if (currentSong.value && settingsStore.getSetting('mediaSession')) {
await mediaSessionManager.setMetadata(currentSong.value, {
onPlay: togglePlay,
onPause: togglePlay,
onNext: playNext,
onPrev: playPrevious,
onSeekTo: (time) => {
if (audioRef.value) {
audioRef.value.currentTime = time
playerStore.setCurrentTime(time)
}
}
})
// 设置播放状态
mediaSessionManager.setPlaybackState('playing')
// 设置位置信息
if (audioRef.value) {
mediaSessionManager.setPositionState(
audioRef.value.duration || 0,
audioRef.value.currentTime || 0
)
}
}
}
const handlePause = () => {
playerStore.setPlayingState(false)
// 更新Media Session播放状态
if (settingsStore.getSetting('mediaSession')) {
mediaSessionManager.setPlaybackState('paused')
}
}
const handleError = (e) => {
console.error('音频播放错误:', e)
showNotification('播放出错,请重试', 'error')
playerStore.setPlayingState(false)
}
// 通知函数
const showNotification = (message, type = 'info') => {
// 这里需要实现通知组件的显示逻辑
console.log(`${type}: ${message}`)
}
// 监听当前歌曲变化
watch(currentSong, (newSong, oldSong) => {
if (newSong && newSong !== oldSong) {
// 如果是页面刷新后的恢复,且已有audioSrc,则不重新加载
if (oldSong === null && playerStore.audioSrc) {
console.log('恢复播放会话,跳过重新加载')
// 恢复音频元素的src
if (audioRef.value) {
audioRef.value.src = playerStore.audioSrc
audioRef.value.load()
}
return
}
loadAndPlaySong(newSong)
}
}, { immediate: true })
// 监听音量变化
watch(() => playerStore.volume, (newVolume) => {
if (audioRef.value) {
audioRef.value.volume = newVolume / 100
}
}, { immediate: true })
// 组件挂载
onMounted(() => {
// 加载所有存储状态
playerStore.loadPlayerState()
playQueueStore.loadQueue()
searchStore.loadSearchSettings()
searchStore.loadSearchHistory()
favoritesStore.loadFavorites()
historyStore.loadHistory()
settingsStore.loadSettings()
// 建立音频元素连接
if (audioRef.value) {
playerStore.setAudioElement(audioRef.value)
audioRef.value.volume = (playerStore.volume || 80) / 100
}
// 监听播放器状态过期事件
window.addEventListener('playerStateExpired', () => {
console.log('播放器状态已过期,跳转到主页')
router.push('/')
})
// 应用主题
const theme = settingsStore.getSetting('theme') || 'light'
settingsStore.applyTheme(theme)
// 监听来自路由版FullPlayerPage的加载歌曲事件
const handleLoadAndPlaySong = (event) => {
if (event.detail?.song) {
loadAndPlaySong(event.detail.song)
}
}
// 监听来自侧边栏的播放控制事件
const handleSidebarTogglePlay = () => {
togglePlay()
}
window.addEventListener('loadAndPlaySong', handleLoadAndPlaySong)
window.addEventListener('sidebarTogglePlay', handleSidebarTogglePlay)
// 恢复逻辑交由watch处理,避免重复设置
console.log('App mounted, 恢复状态:', {
currentSong: currentSong.value?.name,
hasAudioSrc: !!playerStore.audioSrc,
currentTheme: theme
})
})
// 组件卸载时清理资源
onBeforeUnmount(() => {
// 清理Media Session资源
mediaSessionManager.cleanup()
// 移除事件监听器
window.removeEventListener('loadAndPlaySong', () => {})
window.removeEventListener('sidebarTogglePlay', () => {})
window.removeEventListener('playerStateExpired', () => {})
})
</script>
<style>
.main-content {
flex: 1;
padding-bottom: calc(var(--tabbar-height) + var(--mini-player-height));
overflow-y: auto;
background: var(--bg-primary);
}
/* 当没有当前歌曲时,减少底部边距 */
.main-content:not(.has-mini-player) {
padding-bottom: var(--tabbar-height);
background: var(--bg-primary);
}
/* PC端布局适配 */
@media (min-width: 1024px) {
#app {
display: flex;
height: 100vh;
}
.main-content {
flex: 1;
margin-left: 280px; /* 侧边栏宽度 */
padding-bottom: var(--mini-player-height);
}
.main-content:not(.has-mini-player) {
padding-bottom: 0;
}
}
/* iOS PWA 模式适配 */
@supports (-webkit-touch-callout: none) {
@media all and (display-mode: standalone) {
.main-content {
padding-bottom: calc(var(--tabbar-height) + var(--mini-player-height) + 20px);
}
.main-content:not(.has-mini-player) {
padding-bottom: calc(var(--tabbar-height) + 20px);
}
}
/* PC端PWA模式 */
@media (min-width: 1024px) and (display-mode: standalone) {
.main-content {
padding-bottom: var(--mini-player-height);
}
.main-content:not(.has-mini-player) {
padding-bottom: 0;
}
}
}
</style>