import { isEmpty, setPause, setPlay, setResource, setStop } from '@renderer/plugins/player' import { isPlay, playedList, playInfo, playMusicInfo, tempPlayList } from '@renderer/store/player/state' import { getList, clearPlayedList, clearTempPlayeList, setPlayMusicInfo, addPlayedList, setMusicInfo, setAllStatus, removeTempPlayList, setPlayListId, removePlayedList, } from '@renderer/store/player/action' import { appSetting } from '@renderer/store/setting' import { getMusicUrl, getPicPath, getLyricInfo } from '../music/index' import { filterList } from './utils' import { requestMsg } from '@renderer/utils/message' import { getRandom } from '@renderer/utils/index' // import { checkMusicFileAvailable } from '@renderer/utils/music' let gettingUrlId = '' const createDelayNextTimeout = (delay: number) => { let timeout: NodeJS.Timeout | null const clearDelayNextTimeout = () => { // console.log(this.timeout) if (timeout) { clearTimeout(timeout) timeout = null } } const addDelayNextTimeout = () => { clearDelayNextTimeout() timeout = setTimeout(() => { timeout = null if (window.lx.isPlayedStop) return console.warn('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 !== playMusicInfo.musicInfo || isPlay.value return gettingUrlId != curMusicInfo.id || curMusicInfo.id != playMusicInfo.musicInfo?.id || isPlay.value } let cancelDelayRetry: (() => void) | null = null const delayRetry = async(musicInfo: LX.Music.MusicInfo | LX.Download.ListItem, isRefresh = false): Promise => { // if (cancelDelayRetry) cancelDelayRetry() return new Promise((resolve, reject) => { const time = getRandom(2, 6) setAllStatus(window.i18n.t('player__geting_url_delay_retry', { time })) const tiemout = setTimeout(() => { getMusicPlayUrl(musicInfo, isRefresh, true).then((result) => { cancelDelayRetry = null resolve(result) }).catch(async(err: any) => { cancelDelayRetry = null reject(err) }) }, time * 1000) cancelDelayRetry = () => { clearTimeout(tiemout) cancelDelayRetry = null resolve(null) } }) } const getMusicPlayUrl = async(musicInfo: LX.Music.MusicInfo | LX.Download.ListItem, isRefresh = false, isRetryed = false): Promise => { // this.musicInfo.url = await getMusicPlayUrl(targetSong, type) setAllStatus(window.i18n.t('player__geting_url')) if (appSetting['player.autoSkipOnError']) addLoadTimeout() // const type = getPlayType(appSetting['player.highQuality'], musicInfo) return getMusicUrl({ musicInfo, isRefresh, onToggleSource(mInfo) { if (diffCurrentMusicInfo(musicInfo)) return setAllStatus(window.i18n.t('toggle_source_try')) }, }).then(url => { if (window.lx.isPlayedStop || diffCurrentMusicInfo(musicInfo)) return null return url // eslint-disable-next-line @typescript-eslint/promise-function-async }).catch(err => { // console.log('err', err.message) if (window.lx.isPlayedStop || diffCurrentMusicInfo(musicInfo) || err.message == requestMsg.cancelRequest) return null if (err.message == requestMsg.tooManyRequests) return delayRetry(musicInfo, isRefresh) if (!isRetryed) return getMusicPlayUrl(musicInfo, isRefresh, true) throw err }) } export const setMusicUrl = (musicInfo: LX.Music.MusicInfo | LX.Download.ListItem, isRefresh?: boolean) => { // if (appSetting['player.autoSkipOnError']) addLoadTimeout() if (!diffCurrentMusicInfo(musicInfo)) return if (cancelDelayRetry) cancelDelayRetry() gettingUrlId = musicInfo.id void getMusicPlayUrl(musicInfo, isRefresh).then((url) => { if (!url) return setResource(url) }).catch((err: any) => { console.log(err) setAllStatus(err.message) window.app_event.error() if (appSetting['player.autoSkipOnError']) addDelayNextTimeout() }).finally(() => { if (musicInfo === playMusicInfo.musicInfo) { gettingUrlId = '' clearLoadTimeout() } }) } // 恢复上次播放的状态 const handleRestorePlay = async(restorePlayInfo: LX.Player.SavedPlayInfo) => { const musicInfo = playMusicInfo.musicInfo if (!musicInfo) return setImmediate(() => { if (musicInfo.id != playMusicInfo.musicInfo?.id) return window.app_event.setProgress(appSetting['player.isSavePlayTime'] ? restorePlayInfo.time : 0, restorePlayInfo.maxTime) }) void getPicPath({ musicInfo, listId: playMusicInfo.listId }).then((url: string) => { if (musicInfo.id != playMusicInfo.musicInfo?.id) return setMusicInfo({ pic: url }) window.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, }) window.app_event.lyricUpdated() }).catch((err) => { console.log(err) if (musicInfo.id != playMusicInfo.musicInfo?.id) return setAllStatus(window.i18n.t('lyric__load_error')) }) if (appSetting['player.togglePlayMethod'] == 'random' && !playMusicInfo.isTempPlay) addPlayedList({ ...playMusicInfo as LX.Player.PlayMusicInfo }) } // 处理音乐播放 const handlePlay = () => { window.lx.isPlayedStop &&= false if (window.lx.restorePlayInfo) { void handleRestorePlay(window.lx.restorePlayInfo) window.lx.restorePlayInfo = null return } const musicInfo = playMusicInfo.musicInfo if (!musicInfo) return setStop() window.app_event.pause() clearDelayNextTimeout() clearLoadTimeout() if (appSetting['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 }) window.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, }) window.app_event.lyricUpdated() }).catch((err) => { console.log(err) if (musicInfo.id != playMusicInfo.musicInfo?.id) return setAllStatus(window.i18n.t('lyric__load_error')) }) } /** * 播放列表内歌曲 * @param listId 列表id * @param index 播放的歌曲位置 */ export const playList = (listId: string, index: number) => { setPlayListId(listId) pause() setPlayMusicInfo(listId, getList(listId)[index]) clearPlayedList() clearTempPlayeList() handlePlay() } const handleToggleStop = () => { stop() setTimeout(() => { setPlayMusicInfo(null, null) }) } /** * 下一曲 * @param isAutoToggle 是否自动切换 * @returns */ export const playNext = async(isAutoToggle = false): Promise => { if (tempPlayList.length) { // 如果稍后播放列表存在歌曲则直接播放改列表的歌曲 const playMusicInfo = tempPlayList[0] removeTempPlayList(0) pause() setPlayMusicInfo(playMusicInfo.listId, playMusicInfo.musicInfo, playMusicInfo.isTempPlay) handlePlay() return } if (playMusicInfo.musicInfo == null) { handleToggleStop() return } // console.log(playInfo.playerListId) const currentListId = playInfo.playerListId if (!currentListId) { handleToggleStop() return } const currentList = getList(currentListId) 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] pause() setPlayMusicInfo(playMusicInfo.listId, playMusicInfo.musicInfo, playMusicInfo.isTempPlay) handlePlay() return } } // const isCheckFile = findNum > 2 // 针对下载列表,如果超过两次都碰到无效歌曲,则过滤整个列表内的无效歌曲 let { filteredList, playerIndex } = await filterList({ // 过滤已播放歌曲 listId: currentListId, list: currentList, playedList, playerMusicInfo: currentList[playInfo.playerPlayIndex], }) if (!filteredList.length) { handleToggleStop() return } // let currentIndex: number = filteredList.indexOf(currentList[playInfo.playerPlayIndex]) if (playerIndex == -1 && filteredList.length) playerIndex = 0 let nextIndex = playerIndex let togglePlayMethod = appSetting['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, } pause() setPlayMusicInfo(nextPlayMusicInfo.listId, nextPlayMusicInfo.musicInfo) handlePlay() } /** * 上一曲 */ export const playPrev = async(isAutoToggle = false): Promise => { if (playMusicInfo.musicInfo == null) { handleToggleStop() return } const currentListId = playInfo.playerListId if (!currentListId) { handleToggleStop() return } const currentList = getList(currentListId) 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] pause() setPlayMusicInfo(playMusicInfo.listId, playMusicInfo.musicInfo, playMusicInfo.isTempPlay) handlePlay() return } } // const isCheckFile = findNum > 2 let { filteredList, playerIndex } = await filterList({ // 过滤已播放歌曲 listId: currentListId, list: currentList, playedList, playerMusicInfo: currentList[playInfo.playerPlayIndex], }) if (!filteredList.length) { handleToggleStop() return } // let currentIndex = filteredList.indexOf(currentList[playInfo.playerPlayIndex]) if (playerIndex == -1 && filteredList.length) playerIndex = 0 let nextIndex = playerIndex if (!playMusicInfo.isTempPlay) { let togglePlayMethod = appSetting['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, } pause() setPlayMusicInfo(nextPlayMusicInfo.listId, nextPlayMusicInfo.musicInfo) handlePlay() } /** * 恢复播放 */ export const play = () => { if (playMusicInfo.musicInfo == null) return if (isEmpty()) { if (playMusicInfo.musicInfo.id != gettingUrlId) setMusicUrl(playMusicInfo.musicInfo) return } setPlay() } /** * 暂停播放 */ export const pause = () => { setPause() } /** * 停止播放 */ export const stop = () => { setStop() setTimeout(() => { window.app_event.stop() }) } /** * 播放、暂停播放切换 */ export const togglePlay = () => { window.lx.isPlayedStop &&= false if (isPlay.value) { pause() } else { play() } }