mirror of
https://github.com/ikun0014/lx-music-mobile.git
synced 2025-07-16 01:22:07 +08:00
469 lines
14 KiB
TypeScript
469 lines
14 KiB
TypeScript
import { isInitialized, initial as playerInitial, isEmpty, setPause, setPlay, setResource, setStop } from '@/plugins/player'
|
|
import {
|
|
setStatusText,
|
|
} from '@/core/player/playStatus'
|
|
import playerState from '@/store/player/state'
|
|
import settingState from '@/store/setting/state'
|
|
import {
|
|
getList,
|
|
setPlayMusicInfo,
|
|
setMusicInfo,
|
|
setPlayListId,
|
|
} from '@/core/player/playInfo'
|
|
import {
|
|
clearPlayedList,
|
|
addPlayedList,
|
|
removePlayedList,
|
|
} from '@/core/player/playedList'
|
|
import {
|
|
clearTempPlayeList,
|
|
removeTempPlayList,
|
|
} from '@/core/player/tempPlayList'
|
|
import { getMusicUrl, getPicPath, getLyricInfo } from '@/core/music'
|
|
import { requestMsg } from '@/utils/message'
|
|
import { getRandom } from '@/utils/common'
|
|
import { filterList } from './utils'
|
|
import BackgroundTimer from 'react-native-background-timer'
|
|
import { checkNotificationPermission } from '@/utils/tools'
|
|
|
|
// import { checkMusicFileAvailable } from '@renderer/utils/music'
|
|
|
|
const createDelayNextTimeout = (delay: number) => {
|
|
let timeout: number | null
|
|
const clearDelayNextTimeout = () => {
|
|
// console.log(this.timeout)
|
|
if (timeout) {
|
|
BackgroundTimer.clearTimeout(timeout)
|
|
timeout = null
|
|
}
|
|
}
|
|
|
|
const addDelayNextTimeout = () => {
|
|
clearDelayNextTimeout()
|
|
timeout = BackgroundTimer.setTimeout(() => {
|
|
timeout = null
|
|
if (global.lx.isPlayedStop) return
|
|
console.log('delay next timeout timeout', delay)
|
|
void playNext(true)
|
|
}, delay)
|
|
}
|
|
|
|
return {
|
|
clearDelayNextTimeout,
|
|
addDelayNextTimeout,
|
|
}
|
|
}
|
|
const { addDelayNextTimeout, clearDelayNextTimeout } = createDelayNextTimeout(5000)
|
|
const { addDelayNextTimeout: addLoadTimeout, clearDelayNextTimeout: clearLoadTimeout } = createDelayNextTimeout(100000)
|
|
|
|
/**
|
|
* 检查音乐信息是否已更改
|
|
*/
|
|
const diffCurrentMusicInfo = (curMusicInfo: LX.Music.MusicInfo | LX.Download.ListItem): boolean => {
|
|
return curMusicInfo !== playerState.playMusicInfo.musicInfo || playerState.isPlay
|
|
}
|
|
|
|
const getMusicPlayUrl = async(musicInfo: LX.Music.MusicInfo | LX.Download.ListItem, isRefresh = false, isRetryed = false): Promise<string | null> => {
|
|
// this.musicInfo.url = await getMusicPlayUrl(targetSong, type)
|
|
setStatusText(global.i18n.t('player__geting_url'))
|
|
|
|
// const type = getPlayType(settingState.setting['player.isPlayHighQuality'], musicInfo)
|
|
|
|
return getMusicUrl({
|
|
musicInfo,
|
|
isRefresh,
|
|
onToggleSource(mInfo) {
|
|
if (diffCurrentMusicInfo(musicInfo)) return
|
|
setStatusText(global.i18n.t('toggle_source_try'))
|
|
},
|
|
}).then(url => {
|
|
if (global.lx.isPlayedStop || diffCurrentMusicInfo(musicInfo)) return null
|
|
|
|
return url
|
|
}).catch(async err => {
|
|
// console.log('err', err.message)
|
|
if (global.lx.isPlayedStop ||
|
|
diffCurrentMusicInfo(musicInfo) ||
|
|
err.message == requestMsg.cancelRequest) return null
|
|
|
|
if (!isRetryed) return getMusicPlayUrl(musicInfo, isRefresh, true)
|
|
|
|
throw err
|
|
})
|
|
}
|
|
|
|
export const setMusicUrl = (musicInfo: LX.Music.MusicInfo | LX.Download.ListItem, isRefresh?: boolean) => {
|
|
addLoadTimeout()
|
|
global.lx.gettingUrlId = musicInfo.id
|
|
void getMusicPlayUrl(musicInfo, isRefresh).then((url) => {
|
|
if (!url) return
|
|
setResource(musicInfo, url, playerState.progress.nowPlayTime)
|
|
}).catch((err: any) => {
|
|
console.log(err)
|
|
setStatusText(err.message)
|
|
global.app_event.error()
|
|
addDelayNextTimeout()
|
|
}).finally(() => {
|
|
if (musicInfo === playerState.playMusicInfo.musicInfo) {
|
|
global.lx.gettingUrlId = ''
|
|
clearLoadTimeout()
|
|
}
|
|
})
|
|
}
|
|
|
|
// 恢复上次播放的状态
|
|
const handleRestorePlay = async(restorePlayInfo: LX.Player.SavedPlayInfo) => {
|
|
const musicInfo = playerState.playMusicInfo.musicInfo
|
|
if (!musicInfo) return
|
|
|
|
setTimeout(() => {
|
|
global.app_event.setProgress(settingState.setting['player.isSavePlayTime'] ? restorePlayInfo.time : 0, restorePlayInfo.maxTime)
|
|
})
|
|
|
|
const playMusicInfo = playerState.playMusicInfo
|
|
|
|
void getPicPath({ musicInfo, listId: playMusicInfo.listId }).then((url: string) => {
|
|
if (musicInfo.id != playMusicInfo.musicInfo?.id) return
|
|
setMusicInfo({ pic: url })
|
|
global.app_event.picUpdated()
|
|
})
|
|
|
|
void getLyricInfo({ musicInfo }).then((lyricInfo) => {
|
|
if (musicInfo.id != playMusicInfo.musicInfo?.id) return
|
|
setMusicInfo({
|
|
lrc: lyricInfo.lyric,
|
|
tlrc: lyricInfo.tlyric,
|
|
lxlrc: lyricInfo.lxlyric,
|
|
rlrc: lyricInfo.rlyric,
|
|
rawlrc: lyricInfo.rawlrcInfo.lyric,
|
|
})
|
|
global.app_event.lyricUpdated()
|
|
}).catch((err) => {
|
|
console.log(err)
|
|
if (musicInfo.id != playMusicInfo.musicInfo?.id) return
|
|
setStatusText(global.i18n.t('lyric__load_error'))
|
|
})
|
|
|
|
if (settingState.setting['player.togglePlayMethod'] == 'random' && !playMusicInfo.isTempPlay) addPlayedList(playMusicInfo as LX.Player.PlayMusicInfo)
|
|
}
|
|
|
|
|
|
// 处理音乐播放
|
|
const handlePlay = async() => {
|
|
if (!isInitialized()) {
|
|
await checkNotificationPermission()
|
|
await playerInitial({
|
|
volume: settingState.setting['player.volume'],
|
|
playRate: settingState.setting['player.playbackRate'],
|
|
cacheSize: settingState.setting['player.cacheSize'] ? parseInt(settingState.setting['player.cacheSize']) : 0,
|
|
isHandleAudioFocus: settingState.setting['player.isHandleAudioFocus'],
|
|
})
|
|
}
|
|
|
|
global.lx.isPlayedStop &&= false
|
|
|
|
if (global.lx.restorePlayInfo) {
|
|
void handleRestorePlay(global.lx.restorePlayInfo)
|
|
global.lx.restorePlayInfo = null
|
|
return
|
|
}
|
|
|
|
const playMusicInfo = playerState.playMusicInfo
|
|
const musicInfo = playMusicInfo.musicInfo
|
|
|
|
if (!musicInfo || global.lx.gettingUrlId == musicInfo.id) return
|
|
global.lx.gettingUrlId &&= ''
|
|
|
|
await setStop()
|
|
global.app_event.pause()
|
|
|
|
clearDelayNextTimeout()
|
|
clearLoadTimeout()
|
|
|
|
|
|
if (settingState.setting['player.togglePlayMethod'] == 'random' && !playMusicInfo.isTempPlay) addPlayedList(playMusicInfo as LX.Player.PlayMusicInfo)
|
|
|
|
setMusicUrl(musicInfo)
|
|
|
|
void getPicPath({ musicInfo, listId: playMusicInfo.listId }).then((url: string) => {
|
|
if (musicInfo.id != playMusicInfo.musicInfo?.id) return
|
|
setMusicInfo({ pic: url })
|
|
global.app_event.picUpdated()
|
|
})
|
|
|
|
void getLyricInfo({ musicInfo }).then((lyricInfo) => {
|
|
if (musicInfo.id != playMusicInfo.musicInfo?.id) return
|
|
setMusicInfo({
|
|
lrc: lyricInfo.lyric,
|
|
tlrc: lyricInfo.tlyric,
|
|
lxlrc: lyricInfo.lxlyric,
|
|
rlrc: lyricInfo.rlyric,
|
|
rawlrc: lyricInfo.rawlrcInfo.lyric,
|
|
})
|
|
global.app_event.lyricUpdated()
|
|
}).catch((err) => {
|
|
console.log(err)
|
|
if (musicInfo.id != playMusicInfo.musicInfo?.id) return
|
|
setStatusText(global.i18n.t('lyric__load_error'))
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 播放列表内歌曲
|
|
* @param listId 列表id
|
|
* @param index 播放的歌曲位置
|
|
*/
|
|
export const playList = async(listId: string, index: number) => {
|
|
await pause()
|
|
setPlayListId(listId)
|
|
setPlayMusicInfo(listId, getList(listId)[index])
|
|
clearPlayedList()
|
|
clearTempPlayeList()
|
|
await handlePlay()
|
|
}
|
|
|
|
const handleToggleStop = async() => {
|
|
await stop()
|
|
setTimeout(() => {
|
|
setPlayMusicInfo(null, null)
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 下一曲
|
|
* @param isAutoToggle 是否自动切换
|
|
* @returns
|
|
*/
|
|
export const playNext = async(isAutoToggle = false): Promise<void> => {
|
|
if (playerState.tempPlayList.length) { // 如果稍后播放列表存在歌曲则直接播放改列表的歌曲
|
|
const playMusicInfo = playerState.tempPlayList[0]
|
|
removeTempPlayList(0)
|
|
await pause()
|
|
setPlayMusicInfo(playMusicInfo.listId, playMusicInfo.musicInfo, playMusicInfo.isTempPlay)
|
|
await handlePlay()
|
|
return
|
|
}
|
|
|
|
const playMusicInfo = playerState.playMusicInfo
|
|
const playInfo = playerState.playInfo
|
|
if (playMusicInfo.musicInfo == null) return handleToggleStop()
|
|
|
|
// console.log(playInfo.playerListId)
|
|
const currentListId = playInfo.playerListId
|
|
if (!currentListId) return handleToggleStop()
|
|
const currentList = getList(currentListId)
|
|
|
|
const playedList = playerState.playedList
|
|
|
|
if (playedList.length) { // 移除已播放列表内不存在原列表的歌曲
|
|
let currentId: string
|
|
if (playMusicInfo.isTempPlay) {
|
|
const musicInfo = currentList[playInfo.playerPlayIndex]
|
|
if (musicInfo) currentId = musicInfo.id
|
|
} else {
|
|
currentId = playMusicInfo.musicInfo.id
|
|
}
|
|
// 从已播放列表移除播放列表已删除的歌曲
|
|
let index
|
|
for (index = playedList.findIndex(m => m.musicInfo.id === currentId) + 1; index < playedList.length; index++) {
|
|
const playMusicInfo = playedList[index]
|
|
const currentId = playMusicInfo.musicInfo.id
|
|
if (playMusicInfo.listId == currentListId && !currentList.some(m => m.id === currentId)) {
|
|
removePlayedList(index)
|
|
continue
|
|
}
|
|
break
|
|
}
|
|
|
|
if (index < playedList.length) {
|
|
const playMusicInfo = playedList[index]
|
|
await pause()
|
|
setPlayMusicInfo(playMusicInfo.listId, playMusicInfo.musicInfo, playMusicInfo.isTempPlay)
|
|
await handlePlay()
|
|
return
|
|
}
|
|
}
|
|
// const isCheckFile = findNum > 2 // 针对下载列表,如果超过两次都碰到无效歌曲,则过滤整个列表内的无效歌曲
|
|
let { filteredList, playerIndex } = filterList({ // 过滤已播放歌曲
|
|
listId: currentListId,
|
|
list: currentList,
|
|
playedList,
|
|
playerMusicInfo: currentList[playInfo.playerPlayIndex],
|
|
})
|
|
|
|
if (!filteredList.length) return handleToggleStop()
|
|
// let currentIndex: number = filteredList.indexOf(currentList[playInfo.playerPlayIndex])
|
|
if (playerIndex == -1 && filteredList.length) playerIndex = 0
|
|
let nextIndex = playerIndex
|
|
|
|
let togglePlayMethod = settingState.setting['player.togglePlayMethod']
|
|
if (!isAutoToggle) {
|
|
switch (togglePlayMethod) {
|
|
case 'list':
|
|
case 'singleLoop':
|
|
case 'none':
|
|
togglePlayMethod = 'listLoop'
|
|
}
|
|
}
|
|
switch (togglePlayMethod) {
|
|
case 'listLoop':
|
|
nextIndex = playerIndex === filteredList.length - 1 ? 0 : playerIndex + 1
|
|
break
|
|
case 'random':
|
|
nextIndex = getRandom(0, filteredList.length)
|
|
break
|
|
case 'list':
|
|
nextIndex = playerIndex === filteredList.length - 1 ? -1 : playerIndex + 1
|
|
break
|
|
case 'singleLoop':
|
|
break
|
|
default:
|
|
nextIndex = -1
|
|
return
|
|
}
|
|
if (nextIndex < 0) return
|
|
|
|
const nextPlayMusicInfo = {
|
|
musicInfo: filteredList[nextIndex],
|
|
listId: currentListId,
|
|
isTempPlay: false,
|
|
}
|
|
|
|
await pause()
|
|
setPlayMusicInfo(nextPlayMusicInfo.listId, nextPlayMusicInfo.musicInfo)
|
|
await handlePlay()
|
|
}
|
|
|
|
/**
|
|
* 上一曲
|
|
*/
|
|
export const playPrev = async(isAutoToggle = false): Promise<void> => {
|
|
const playMusicInfo = playerState.playMusicInfo
|
|
if (playMusicInfo.musicInfo == null) return handleToggleStop()
|
|
const playInfo = playerState.playInfo
|
|
|
|
const currentListId = playInfo.playerListId
|
|
if (!currentListId) return handleToggleStop()
|
|
const currentList = getList(currentListId)
|
|
|
|
const playedList = playerState.playedList
|
|
if (playedList.length) {
|
|
let currentId: string
|
|
if (playMusicInfo.isTempPlay) {
|
|
const musicInfo = currentList[playInfo.playerPlayIndex]
|
|
if (musicInfo) currentId = musicInfo.id
|
|
} else {
|
|
currentId = playMusicInfo.musicInfo.id
|
|
}
|
|
// 从已播放列表移除播放列表已删除的歌曲
|
|
let index
|
|
for (index = playedList.findIndex(m => m.musicInfo.id === currentId) - 1; index > -1; index--) {
|
|
const playMusicInfo = playedList[index]
|
|
const currentId = playMusicInfo.musicInfo.id
|
|
if (playMusicInfo.listId == currentListId && !currentList.some(m => m.id === currentId)) {
|
|
removePlayedList(index)
|
|
continue
|
|
}
|
|
break
|
|
}
|
|
|
|
if (index > -1) {
|
|
const playMusicInfo = playedList[index]
|
|
await pause()
|
|
setPlayMusicInfo(playMusicInfo.listId, playMusicInfo.musicInfo, playMusicInfo.isTempPlay)
|
|
await handlePlay()
|
|
return
|
|
}
|
|
}
|
|
|
|
// const isCheckFile = findNum > 2
|
|
let { filteredList, playerIndex } = filterList({ // 过滤已播放歌曲
|
|
listId: currentListId,
|
|
list: currentList,
|
|
playedList,
|
|
playerMusicInfo: currentList[playInfo.playerPlayIndex],
|
|
})
|
|
if (!filteredList.length) return handleToggleStop()
|
|
|
|
// let currentIndex = filteredList.indexOf(currentList[playInfo.playerPlayIndex])
|
|
if (playerIndex == -1 && filteredList.length) playerIndex = 0
|
|
let nextIndex = playerIndex
|
|
if (!playMusicInfo.isTempPlay) {
|
|
let togglePlayMethod = settingState.setting['player.togglePlayMethod']
|
|
if (!isAutoToggle) {
|
|
switch (togglePlayMethod) {
|
|
case 'list':
|
|
case 'singleLoop':
|
|
case 'none':
|
|
togglePlayMethod = 'listLoop'
|
|
}
|
|
}
|
|
switch (togglePlayMethod) {
|
|
case 'random':
|
|
nextIndex = getRandom(0, filteredList.length)
|
|
break
|
|
case 'listLoop':
|
|
case 'list':
|
|
nextIndex = playerIndex === 0 ? filteredList.length - 1 : playerIndex - 1
|
|
break
|
|
case 'singleLoop':
|
|
break
|
|
default:
|
|
nextIndex = -1
|
|
return
|
|
}
|
|
if (nextIndex < 0) return
|
|
}
|
|
|
|
const nextPlayMusicInfo = {
|
|
musicInfo: filteredList[nextIndex],
|
|
listId: currentListId,
|
|
isTempPlay: false,
|
|
}
|
|
|
|
await pause()
|
|
setPlayMusicInfo(nextPlayMusicInfo.listId, nextPlayMusicInfo.musicInfo)
|
|
await handlePlay()
|
|
}
|
|
|
|
/**
|
|
* 恢复播放
|
|
*/
|
|
export const play = () => {
|
|
if (playerState.playMusicInfo.musicInfo == null) return
|
|
if (isEmpty()) {
|
|
if (playerState.playMusicInfo.musicInfo.id != global.lx.gettingUrlId) setMusicUrl(playerState.playMusicInfo.musicInfo)
|
|
return
|
|
}
|
|
void setPlay()
|
|
}
|
|
|
|
/**
|
|
* 暂停播放
|
|
*/
|
|
export const pause = async() => {
|
|
await setPause()
|
|
}
|
|
|
|
/**
|
|
* 停止播放
|
|
*/
|
|
export const stop = async() => {
|
|
await setStop()
|
|
setTimeout(() => {
|
|
global.app_event.stop()
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 播放、暂停播放切换
|
|
*/
|
|
export const togglePlay = () => {
|
|
global.lx.isPlayedStop &&= false
|
|
if (playerState.isPlay) {
|
|
void pause()
|
|
} else {
|
|
play()
|
|
}
|
|
}
|