95 lines
3.4 KiB
TypeScript
95 lines
3.4 KiB
TypeScript
import { getMusicUrl } from '~/utils/musicUrl'
|
|
import { isBilibiliSong } from '~/utils/bilibiliSource'
|
|
import { useAudioPlayer, type PlayableSong } from './useAudioPlayer'
|
|
import { useToast } from './useToast'
|
|
import { useLocale } from '~/utils/locale'
|
|
import type { Song } from '~/types'
|
|
|
|
export const useSongPlayer = () => {
|
|
const audioPlayer = useAudioPlayer()
|
|
const { showToast } = useToast()
|
|
const { audioPlayer: audioPlayerLocale } = useLocale()
|
|
|
|
// 判断歌曲是否可入队(有自定义链接、B站视频或平台ID)
|
|
const isPlayableSong = (s: Song | PlayableSong) => {
|
|
return Boolean(
|
|
(s.playUrl && s.playUrl.trim()) ||
|
|
isBilibiliSong(s) ||
|
|
(s.musicPlatform && s.musicId)
|
|
)
|
|
}
|
|
|
|
// 根据队列来源构建播放队列,返回队列与当前歌曲索引;来源为空时退化为单曲队列
|
|
const buildQueue = (song: PlayableSong, queueSource?: (Song | PlayableSong)[]) => {
|
|
const queue = (queueSource || []).filter(isPlayableSong) as PlayableSong[]
|
|
const index = queue.findIndex((s) => String(s.id) === String(song.id))
|
|
if (index === -1) {
|
|
return { queue: [song, ...queue], index: 0 }
|
|
}
|
|
return { queue, index }
|
|
}
|
|
|
|
const playSong = async (song: Song | PlayableSong, queueSource?: (Song | PlayableSong)[]) => {
|
|
// 如果是当前选中的歌曲
|
|
if (audioPlayer.isCurrentSong(song.id)) {
|
|
// 如果正在播放,则暂停
|
|
if (audioPlayer.getPlayingStatus().value) {
|
|
audioPlayer.pauseSong()
|
|
return
|
|
}
|
|
|
|
// 如果是当前歌曲但暂停了,则恢复播放
|
|
const currentGlobalSong = audioPlayer.getCurrentSong().value
|
|
if (currentGlobalSong && (currentGlobalSong.musicUrl || isBilibiliSong(currentGlobalSong))) {
|
|
// 恢复播放时同步刷新播放队列
|
|
const { queue, index } = buildQueue(currentGlobalSong, queueSource)
|
|
audioPlayer.playSong(currentGlobalSong, queue, index)
|
|
return
|
|
}
|
|
}
|
|
|
|
try {
|
|
let url = null
|
|
|
|
// 如果有手动填入的 playUrl,优先使用它(统一处理所有平台)
|
|
if (song.playUrl && song.playUrl.trim()) {
|
|
url = song.playUrl.trim()
|
|
} else if (!isBilibiliSong(song)) {
|
|
// 如果不是哔哩哔哩歌曲且没有手动 playUrl,通过 API 获取 URL
|
|
url = await getMusicUrl(song.musicPlatform, song.musicId, song.playUrl, {
|
|
musicInfo: {
|
|
name: song.title,
|
|
artist: song.artist,
|
|
album: song.album || undefined
|
|
}
|
|
})
|
|
}
|
|
// 如果是哔哩哔哩歌曲且没有手动 playUrl,url 保持为 null,播放器会处理
|
|
|
|
const playableSong: PlayableSong = {
|
|
...song,
|
|
musicUrl: url
|
|
}
|
|
|
|
const { queue, index } = buildQueue(playableSong, queueSource)
|
|
audioPlayer.playSong(playableSong, queue, index)
|
|
} catch (error: any) {
|
|
console.error('播放失败:', error)
|
|
const errorMessage = error instanceof Error ? error.message : String(error)
|
|
showToast(audioPlayerLocale.value.playFailed(errorMessage), 'error')
|
|
|
|
// 即使获取 URL 失败,也应该调用 playSong 以触发播放器的错误处理和弹窗逻辑
|
|
const playableSong: PlayableSong = {
|
|
...song,
|
|
musicUrl: null
|
|
}
|
|
const { queue, index } = buildQueue(playableSong, queueSource)
|
|
audioPlayer.playSong(playableSong, queue, index)
|
|
}
|
|
}
|
|
|
|
return {
|
|
playSong
|
|
}
|
|
}
|