添加歌曲预加载功能
parent
0345419ef3
commit
4e3bdbcc74
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
### 优化
|
### 优化
|
||||||
|
|
||||||
|
- 优化正常播放结束时的下一首歌曲播放衔接度,在歌曲即将播放结束时将预获取下一首歌曲的播放链接,减少自动切歌时的等待时间
|
||||||
- 修正搜索歌曲提示框文案(#2050)
|
- 修正搜索歌曲提示框文案(#2050)
|
||||||
- 优化播放详情页UI,歌曲名字、歌手等文字过长时被截断的问题(#2049)
|
- 优化播放详情页UI,歌曲名字、歌手等文字过长时被截断的问题(#2049)
|
||||||
- Scheme URL 的播放歌曲允许更长的专辑名称
|
- Scheme URL 的播放歌曲允许更长的专辑名称
|
||||||
|
|
|
@ -14,6 +14,7 @@ import { apis } from '@renderer/utils/musicSdk/api-source'
|
||||||
|
|
||||||
|
|
||||||
const getOtherSourcePromises = new Map()
|
const getOtherSourcePromises = new Map()
|
||||||
|
const otherSourceCache = new Map<LX.Music.MusicInfo | LX.Download.ListItem, LX.Music.MusicInfoOnline[]>()
|
||||||
export const existTimeExp = /\[\d{1,2}:.*\d{1,4}\]/
|
export const existTimeExp = /\[\d{1,2}:.*\d{1,4}\]/
|
||||||
|
|
||||||
export const getOtherSource = async(musicInfo: LX.Music.MusicInfo | LX.Download.ListItem, isRefresh = false): Promise<LX.Music.MusicInfoOnline[]> => {
|
export const getOtherSource = async(musicInfo: LX.Music.MusicInfo | LX.Download.ListItem, isRefresh = false): Promise<LX.Music.MusicInfoOnline[]> => {
|
||||||
|
@ -21,6 +22,7 @@ export const getOtherSource = async(musicInfo: LX.Music.MusicInfo | LX.Download.
|
||||||
// const cachedInfo = await getOtherSourceFromStore(musicInfo.id)
|
// const cachedInfo = await getOtherSourceFromStore(musicInfo.id)
|
||||||
// if (cachedInfo.length) return cachedInfo
|
// if (cachedInfo.length) return cachedInfo
|
||||||
// }
|
// }
|
||||||
|
if (otherSourceCache.has(musicInfo)) return otherSourceCache.get(musicInfo)!
|
||||||
let key: string
|
let key: string
|
||||||
let searchMusicInfo: {
|
let searchMusicInfo: {
|
||||||
name: string
|
name: string
|
||||||
|
@ -56,7 +58,10 @@ export const getOtherSource = async(musicInfo: LX.Music.MusicInfo | LX.Download.
|
||||||
reject(new Error('find music timeout'))
|
reject(new Error('find music timeout'))
|
||||||
}, 15_000)
|
}, 15_000)
|
||||||
musicSdk.findMusic(searchMusicInfo).then((otherSource) => {
|
musicSdk.findMusic(searchMusicInfo).then((otherSource) => {
|
||||||
resolve(otherSource.map(toNewMusicInfo) as LX.Music.MusicInfoOnline[])
|
if (otherSourceCache.size > 100) otherSourceCache.clear()
|
||||||
|
const source = otherSource.map(toNewMusicInfo) as LX.Music.MusicInfoOnline[]
|
||||||
|
otherSourceCache.set(musicInfo, source)
|
||||||
|
resolve(source)
|
||||||
}).catch(reject).finally(() => {
|
}).catch(reject).finally(() => {
|
||||||
if (timeout) clearTimeout(timeout)
|
if (timeout) clearTimeout(timeout)
|
||||||
})
|
})
|
||||||
|
|
|
@ -188,6 +188,7 @@ const handleRestorePlay = async(restorePlayInfo: LX.Player.SavedPlayInfo) => {
|
||||||
const handlePlay = () => {
|
const handlePlay = () => {
|
||||||
window.lx.isPlayedStop &&= false
|
window.lx.isPlayedStop &&= false
|
||||||
|
|
||||||
|
resetRandomNextMusicInfo()
|
||||||
if (window.lx.restorePlayInfo) {
|
if (window.lx.restorePlayInfo) {
|
||||||
void handleRestorePlay(window.lx.restorePlayInfo)
|
void handleRestorePlay(window.lx.restorePlayInfo)
|
||||||
window.lx.restorePlayInfo = null
|
window.lx.restorePlayInfo = null
|
||||||
|
@ -253,6 +254,104 @@ const handleToggleStop = () => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const randomNextMusicInfo = {
|
||||||
|
info: null as LX.Player.PlayMusicInfo | null,
|
||||||
|
index: -1,
|
||||||
|
}
|
||||||
|
export const resetRandomNextMusicInfo = () => {
|
||||||
|
if (randomNextMusicInfo.info) {
|
||||||
|
randomNextMusicInfo.info = null
|
||||||
|
randomNextMusicInfo.index = -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getNextPlayMusicInfo = async(): Promise<LX.Player.PlayMusicInfo | null> => {
|
||||||
|
if (tempPlayList.length) { // 如果稍后播放列表存在歌曲则直接播放改列表的歌曲
|
||||||
|
const playMusicInfo = tempPlayList[0]
|
||||||
|
return playMusicInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
if (playMusicInfo.musicInfo == null) return null
|
||||||
|
|
||||||
|
if (randomNextMusicInfo.info) return randomNextMusicInfo.info
|
||||||
|
|
||||||
|
// console.log(playInfo.playerListId)
|
||||||
|
const currentListId = playInfo.playerListId
|
||||||
|
if (!currentListId) return null
|
||||||
|
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) return playedList[index]
|
||||||
|
}
|
||||||
|
// const isCheckFile = findNum > 2 // 针对下载列表,如果超过两次都碰到无效歌曲,则过滤整个列表内的无效歌曲
|
||||||
|
let { filteredList, playerIndex } = await filterList({ // 过滤已播放歌曲
|
||||||
|
listId: currentListId,
|
||||||
|
list: currentList,
|
||||||
|
playedList,
|
||||||
|
playerMusicInfo: currentList[playInfo.playerPlayIndex],
|
||||||
|
isNext: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!filteredList.length) return null
|
||||||
|
// let currentIndex: number = filteredList.indexOf(currentList[playInfo.playerPlayIndex])
|
||||||
|
if (playerIndex == -1 && filteredList.length) playerIndex = 0
|
||||||
|
let nextIndex = playerIndex
|
||||||
|
|
||||||
|
let togglePlayMethod = appSetting['player.togglePlayMethod']
|
||||||
|
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:
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
if (nextIndex < 0) return null
|
||||||
|
|
||||||
|
const nextPlayMusicInfo = {
|
||||||
|
musicInfo: filteredList[nextIndex],
|
||||||
|
listId: currentListId,
|
||||||
|
isTempPlay: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (togglePlayMethod == 'random') {
|
||||||
|
randomNextMusicInfo.info = nextPlayMusicInfo
|
||||||
|
randomNextMusicInfo.index = nextIndex
|
||||||
|
}
|
||||||
|
return nextPlayMusicInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
const handlePlayNext = (playMusicInfo: LX.Player.PlayMusicInfo) => {
|
||||||
|
pause()
|
||||||
|
setPlayMusicInfo(playMusicInfo.listId, playMusicInfo.musicInfo, playMusicInfo.isTempPlay)
|
||||||
|
handlePlay()
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* 下一曲
|
* 下一曲
|
||||||
* @param isAutoToggle 是否自动切换
|
* @param isAutoToggle 是否自动切换
|
||||||
|
@ -263,9 +362,7 @@ export const playNext = async(isAutoToggle = false): Promise<void> => {
|
||||||
if (tempPlayList.length) { // 如果稍后播放列表存在歌曲则直接播放改列表的歌曲
|
if (tempPlayList.length) { // 如果稍后播放列表存在歌曲则直接播放改列表的歌曲
|
||||||
const playMusicInfo = tempPlayList[0]
|
const playMusicInfo = tempPlayList[0]
|
||||||
removeTempPlayList(0)
|
removeTempPlayList(0)
|
||||||
pause()
|
handlePlayNext(playMusicInfo)
|
||||||
setPlayMusicInfo(playMusicInfo.listId, playMusicInfo.musicInfo, playMusicInfo.isTempPlay)
|
|
||||||
handlePlay()
|
|
||||||
console.log('play temp list')
|
console.log('play temp list')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -306,14 +403,15 @@ export const playNext = async(isAutoToggle = false): Promise<void> => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index < playedList.length) {
|
if (index < playedList.length) {
|
||||||
const playMusicInfo = playedList[index]
|
handlePlayNext(playedList[index])
|
||||||
pause()
|
|
||||||
setPlayMusicInfo(playMusicInfo.listId, playMusicInfo.musicInfo, playMusicInfo.isTempPlay)
|
|
||||||
handlePlay()
|
|
||||||
console.log('play played list')
|
console.log('play played list')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (randomNextMusicInfo.info) {
|
||||||
|
handlePlayNext(randomNextMusicInfo.info)
|
||||||
|
return
|
||||||
|
}
|
||||||
// const isCheckFile = findNum > 2 // 针对下载列表,如果超过两次都碰到无效歌曲,则过滤整个列表内的无效歌曲
|
// const isCheckFile = findNum > 2 // 针对下载列表,如果超过两次都碰到无效歌曲,则过滤整个列表内的无效歌曲
|
||||||
let { filteredList, playerIndex } = await filterList({ // 过滤已播放歌曲
|
let { filteredList, playerIndex } = await filterList({ // 过滤已播放歌曲
|
||||||
listId: currentListId,
|
listId: currentListId,
|
||||||
|
@ -363,15 +461,11 @@ export const playNext = async(isAutoToggle = false): Promise<void> => {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const nextPlayMusicInfo = {
|
handlePlayNext({
|
||||||
musicInfo: filteredList[nextIndex],
|
musicInfo: filteredList[nextIndex],
|
||||||
listId: currentListId,
|
listId: currentListId,
|
||||||
isTempPlay: false,
|
isTempPlay: false,
|
||||||
}
|
})
|
||||||
|
|
||||||
pause()
|
|
||||||
setPlayMusicInfo(nextPlayMusicInfo.listId, nextPlayMusicInfo.musicInfo)
|
|
||||||
handlePlay()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -411,10 +505,7 @@ export const playPrev = async(isAutoToggle = false): Promise<void> => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index > -1) {
|
if (index > -1) {
|
||||||
const playMusicInfo = playedList[index]
|
handlePlayNext(playedList[index])
|
||||||
pause()
|
|
||||||
setPlayMusicInfo(playMusicInfo.listId, playMusicInfo.musicInfo, playMusicInfo.isTempPlay)
|
|
||||||
handlePlay()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -462,15 +553,11 @@ export const playPrev = async(isAutoToggle = false): Promise<void> => {
|
||||||
if (nextIndex < 0) return
|
if (nextIndex < 0) return
|
||||||
}
|
}
|
||||||
|
|
||||||
const nextPlayMusicInfo = {
|
handlePlayNext({
|
||||||
musicInfo: filteredList[nextIndex],
|
musicInfo: filteredList[nextIndex],
|
||||||
listId: currentListId,
|
listId: currentListId,
|
||||||
isTempPlay: false,
|
isTempPlay: false,
|
||||||
}
|
})
|
||||||
|
|
||||||
pause()
|
|
||||||
setPlayMusicInfo(nextPlayMusicInfo.listId, nextPlayMusicInfo.musicInfo)
|
|
||||||
handlePlay()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -34,6 +34,7 @@ import usePlaybackRate from './usePlaybackRate'
|
||||||
import useSoundEffect from './useSoundEffect'
|
import useSoundEffect from './useSoundEffect'
|
||||||
import useMaxOutputChannelCount from './useMaxOutputChannelCount'
|
import useMaxOutputChannelCount from './useMaxOutputChannelCount'
|
||||||
import { setPowerSaveBlocker } from '@renderer/core/player/utils'
|
import { setPowerSaveBlocker } from '@renderer/core/player/utils'
|
||||||
|
import usePreloadNextMusic from './usePreloadNextMusic'
|
||||||
|
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
|
@ -48,6 +49,7 @@ export default () => {
|
||||||
useSoundEffect()
|
useSoundEffect()
|
||||||
usePlaybackRate()
|
usePlaybackRate()
|
||||||
useWatchList()
|
useWatchList()
|
||||||
|
usePreloadNextMusic()
|
||||||
|
|
||||||
const handlePlayNext = () => {
|
const handlePlayNext = () => {
|
||||||
void playNext()
|
void playNext()
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
import { onBeforeUnmount, watch } from '@common/utils/vueTools'
|
||||||
|
import { onTimeupdate, getCurrentTime } from '@renderer/plugins/player'
|
||||||
|
import { playProgress } from '@renderer/store/player/playProgress'
|
||||||
|
import { musicInfo } from '@renderer/store/player/state'
|
||||||
|
// import { getList } from '@renderer/store/utils'
|
||||||
|
import { getNextPlayMusicInfo, resetRandomNextMusicInfo } from '@renderer/core/player'
|
||||||
|
import { getMusicUrl } from '@renderer/core/music'
|
||||||
|
import { checkUrl } from '@renderer/utils/request'
|
||||||
|
import { appSetting } from '@renderer/store/setting'
|
||||||
|
|
||||||
|
const preloadMusicInfo = {
|
||||||
|
isLoading: false,
|
||||||
|
preProgress: 0,
|
||||||
|
info: null as LX.Player.PlayMusicInfo | null,
|
||||||
|
}
|
||||||
|
const resetPreloadInfo = () => {
|
||||||
|
preloadMusicInfo.preProgress = 0
|
||||||
|
preloadMusicInfo.info = null
|
||||||
|
preloadMusicInfo.isLoading = false
|
||||||
|
}
|
||||||
|
const preloadNextMusicUrl = async(curTime: number) => {
|
||||||
|
if (preloadMusicInfo.isLoading || curTime - preloadMusicInfo.preProgress < 3) return
|
||||||
|
preloadMusicInfo.isLoading = true
|
||||||
|
console.log('preload next music url')
|
||||||
|
const info = await getNextPlayMusicInfo()
|
||||||
|
if (info) {
|
||||||
|
preloadMusicInfo.info = info
|
||||||
|
const url = await getMusicUrl({ musicInfo: info.musicInfo }).catch(() => '')
|
||||||
|
if (url) {
|
||||||
|
console.log('preload url', url)
|
||||||
|
const result = await checkUrl(url).then(() => true).catch(() => false)
|
||||||
|
if (!result) {
|
||||||
|
const url = await getMusicUrl({ musicInfo: info.musicInfo, isRefresh: true }).catch(() => '')
|
||||||
|
console.log('preload url refresh', url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
preloadMusicInfo.isLoading = false
|
||||||
|
}
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
const setProgress = (time: number) => {
|
||||||
|
if (!musicInfo.id) return
|
||||||
|
preloadMusicInfo.preProgress = time
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSetPlayInfo = () => {
|
||||||
|
resetPreloadInfo()
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(() => appSetting['player.togglePlayMethod'], () => {
|
||||||
|
if (!preloadMusicInfo.info || preloadMusicInfo.info.isTempPlay) return
|
||||||
|
resetRandomNextMusicInfo()
|
||||||
|
preloadMusicInfo.info = null
|
||||||
|
preloadMusicInfo.preProgress = playProgress.nowPlayTime
|
||||||
|
})
|
||||||
|
|
||||||
|
window.app_event.on('setProgress', setProgress)
|
||||||
|
window.app_event.on('musicToggled', handleSetPlayInfo)
|
||||||
|
|
||||||
|
const rOnTimeupdate = onTimeupdate(() => {
|
||||||
|
const time = getCurrentTime()
|
||||||
|
const duration = playProgress.maxPlayTime
|
||||||
|
if (duration > 10 && duration - time < 10 && !preloadMusicInfo.info) {
|
||||||
|
void preloadNextMusicUrl(time)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
rOnTimeupdate()
|
||||||
|
window.app_event.off('setProgress', setProgress)
|
||||||
|
window.app_event.off('musicToggled', handleSetPlayInfo)
|
||||||
|
})
|
||||||
|
}
|
|
@ -307,3 +307,16 @@ const fetchData = async(url, method, {
|
||||||
callback(null, resp, body)
|
callback(null, resp, body)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const checkUrl = (url, options = {}) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
fetchData(url, 'head', options, (err, resp) => {
|
||||||
|
if (err) return reject(err)
|
||||||
|
if (resp.statusCode === 200) {
|
||||||
|
resolve()
|
||||||
|
} else {
|
||||||
|
reject(new Error(resp.statusCode))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue