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 => { // 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 => { 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 => { 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() } }