Skip to content

Commit 4e3bdbc

Browse files
committed
添加歌曲预加载功能
1 parent 0345419 commit 4e3bdbc

File tree

6 files changed

+207
-24
lines changed

6 files changed

+207
-24
lines changed

publish/changeLog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
### 优化
88

9+
- 优化正常播放结束时的下一首歌曲播放衔接度,在歌曲即将播放结束时将预获取下一首歌曲的播放链接,减少自动切歌时的等待时间
910
- 修正搜索歌曲提示框文案(#2050
1011
- 优化播放详情页UI,歌曲名字、歌手等文字过长时被截断的问题(#2049
1112
- Scheme URL 的播放歌曲允许更长的专辑名称

src/renderer/core/music/utils.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,15 @@ import { apis } from '@renderer/utils/musicSdk/api-source'
1414

1515

1616
const getOtherSourcePromises = new Map()
17+
const otherSourceCache = new Map<LX.Music.MusicInfo | LX.Download.ListItem, LX.Music.MusicInfoOnline[]>()
1718
export const existTimeExp = /\[\d{1,2}:.*\d{1,4}\]/
1819

1920
export const getOtherSource = async(musicInfo: LX.Music.MusicInfo | LX.Download.ListItem, isRefresh = false): Promise<LX.Music.MusicInfoOnline[]> => {
2021
// if (!isRefresh && musicInfo.id) {
2122
// const cachedInfo = await getOtherSourceFromStore(musicInfo.id)
2223
// if (cachedInfo.length) return cachedInfo
2324
// }
25+
if (otherSourceCache.has(musicInfo)) return otherSourceCache.get(musicInfo)!
2426
let key: string
2527
let searchMusicInfo: {
2628
name: string
@@ -56,7 +58,10 @@ export const getOtherSource = async(musicInfo: LX.Music.MusicInfo | LX.Download.
5658
reject(new Error('find music timeout'))
5759
}, 15_000)
5860
musicSdk.findMusic(searchMusicInfo).then((otherSource) => {
59-
resolve(otherSource.map(toNewMusicInfo) as LX.Music.MusicInfoOnline[])
61+
if (otherSourceCache.size > 100) otherSourceCache.clear()
62+
const source = otherSource.map(toNewMusicInfo) as LX.Music.MusicInfoOnline[]
63+
otherSourceCache.set(musicInfo, source)
64+
resolve(source)
6065
}).catch(reject).finally(() => {
6166
if (timeout) clearTimeout(timeout)
6267
})

src/renderer/core/player/action.ts

Lines changed: 110 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ const handleRestorePlay = async(restorePlayInfo: LX.Player.SavedPlayInfo) => {
188188
const handlePlay = () => {
189189
window.lx.isPlayedStop &&= false
190190

191+
resetRandomNextMusicInfo()
191192
if (window.lx.restorePlayInfo) {
192193
void handleRestorePlay(window.lx.restorePlayInfo)
193194
window.lx.restorePlayInfo = null
@@ -253,6 +254,104 @@ const handleToggleStop = () => {
253254
})
254255
}
255256

257+
const randomNextMusicInfo = {
258+
info: null as LX.Player.PlayMusicInfo | null,
259+
index: -1,
260+
}
261+
export const resetRandomNextMusicInfo = () => {
262+
if (randomNextMusicInfo.info) {
263+
randomNextMusicInfo.info = null
264+
randomNextMusicInfo.index = -1
265+
}
266+
}
267+
268+
export const getNextPlayMusicInfo = async(): Promise<LX.Player.PlayMusicInfo | null> => {
269+
if (tempPlayList.length) { // 如果稍后播放列表存在歌曲则直接播放改列表的歌曲
270+
const playMusicInfo = tempPlayList[0]
271+
return playMusicInfo
272+
}
273+
274+
if (playMusicInfo.musicInfo == null) return null
275+
276+
if (randomNextMusicInfo.info) return randomNextMusicInfo.info
277+
278+
// console.log(playInfo.playerListId)
279+
const currentListId = playInfo.playerListId
280+
if (!currentListId) return null
281+
const currentList = getList(currentListId)
282+
283+
if (playedList.length) { // 移除已播放列表内不存在原列表的歌曲
284+
let currentId: string
285+
if (playMusicInfo.isTempPlay) {
286+
const musicInfo = currentList[playInfo.playerPlayIndex]
287+
if (musicInfo) currentId = musicInfo.id
288+
} else {
289+
currentId = playMusicInfo.musicInfo.id
290+
}
291+
// 从已播放列表移除播放列表已删除的歌曲
292+
let index
293+
for (index = playedList.findIndex(m => m.musicInfo.id === currentId) + 1; index < playedList.length; index++) {
294+
const playMusicInfo = playedList[index]
295+
const currentId = playMusicInfo.musicInfo.id
296+
if (playMusicInfo.listId == currentListId && !currentList.some(m => m.id === currentId)) {
297+
removePlayedList(index)
298+
continue
299+
}
300+
break
301+
}
302+
303+
if (index < playedList.length) return playedList[index]
304+
}
305+
// const isCheckFile = findNum > 2 // 针对下载列表,如果超过两次都碰到无效歌曲,则过滤整个列表内的无效歌曲
306+
let { filteredList, playerIndex } = await filterList({ // 过滤已播放歌曲
307+
listId: currentListId,
308+
list: currentList,
309+
playedList,
310+
playerMusicInfo: currentList[playInfo.playerPlayIndex],
311+
isNext: true,
312+
})
313+
314+
if (!filteredList.length) return null
315+
// let currentIndex: number = filteredList.indexOf(currentList[playInfo.playerPlayIndex])
316+
if (playerIndex == -1 && filteredList.length) playerIndex = 0
317+
let nextIndex = playerIndex
318+
319+
let togglePlayMethod = appSetting['player.togglePlayMethod']
320+
switch (togglePlayMethod) {
321+
case 'listLoop':
322+
nextIndex = playerIndex === filteredList.length - 1 ? 0 : playerIndex + 1
323+
break
324+
case 'random':
325+
nextIndex = getRandom(0, filteredList.length)
326+
break
327+
case 'list':
328+
nextIndex = playerIndex === filteredList.length - 1 ? -1 : playerIndex + 1
329+
break
330+
case 'singleLoop':
331+
break
332+
default:
333+
return null
334+
}
335+
if (nextIndex < 0) return null
336+
337+
const nextPlayMusicInfo = {
338+
musicInfo: filteredList[nextIndex],
339+
listId: currentListId,
340+
isTempPlay: false,
341+
}
342+
343+
if (togglePlayMethod == 'random') {
344+
randomNextMusicInfo.info = nextPlayMusicInfo
345+
randomNextMusicInfo.index = nextIndex
346+
}
347+
return nextPlayMusicInfo
348+
}
349+
350+
const handlePlayNext = (playMusicInfo: LX.Player.PlayMusicInfo) => {
351+
pause()
352+
setPlayMusicInfo(playMusicInfo.listId, playMusicInfo.musicInfo, playMusicInfo.isTempPlay)
353+
handlePlay()
354+
}
256355
/**
257356
* 下一曲
258357
* @param isAutoToggle 是否自动切换
@@ -263,9 +362,7 @@ export const playNext = async(isAutoToggle = false): Promise<void> => {
263362
if (tempPlayList.length) { // 如果稍后播放列表存在歌曲则直接播放改列表的歌曲
264363
const playMusicInfo = tempPlayList[0]
265364
removeTempPlayList(0)
266-
pause()
267-
setPlayMusicInfo(playMusicInfo.listId, playMusicInfo.musicInfo, playMusicInfo.isTempPlay)
268-
handlePlay()
365+
handlePlayNext(playMusicInfo)
269366
console.log('play temp list')
270367
return
271368
}
@@ -306,14 +403,15 @@ export const playNext = async(isAutoToggle = false): Promise<void> => {
306403
}
307404

308405
if (index < playedList.length) {
309-
const playMusicInfo = playedList[index]
310-
pause()
311-
setPlayMusicInfo(playMusicInfo.listId, playMusicInfo.musicInfo, playMusicInfo.isTempPlay)
312-
handlePlay()
406+
handlePlayNext(playedList[index])
313407
console.log('play played list')
314408
return
315409
}
316410
}
411+
if (randomNextMusicInfo.info) {
412+
handlePlayNext(randomNextMusicInfo.info)
413+
return
414+
}
317415
// const isCheckFile = findNum > 2 // 针对下载列表,如果超过两次都碰到无效歌曲,则过滤整个列表内的无效歌曲
318416
let { filteredList, playerIndex } = await filterList({ // 过滤已播放歌曲
319417
listId: currentListId,
@@ -363,15 +461,11 @@ export const playNext = async(isAutoToggle = false): Promise<void> => {
363461
return
364462
}
365463

366-
const nextPlayMusicInfo = {
464+
handlePlayNext({
367465
musicInfo: filteredList[nextIndex],
368466
listId: currentListId,
369467
isTempPlay: false,
370-
}
371-
372-
pause()
373-
setPlayMusicInfo(nextPlayMusicInfo.listId, nextPlayMusicInfo.musicInfo)
374-
handlePlay()
468+
})
375469
}
376470

377471
/**
@@ -411,10 +505,7 @@ export const playPrev = async(isAutoToggle = false): Promise<void> => {
411505
}
412506

413507
if (index > -1) {
414-
const playMusicInfo = playedList[index]
415-
pause()
416-
setPlayMusicInfo(playMusicInfo.listId, playMusicInfo.musicInfo, playMusicInfo.isTempPlay)
417-
handlePlay()
508+
handlePlayNext(playedList[index])
418509
return
419510
}
420511
}
@@ -462,15 +553,11 @@ export const playPrev = async(isAutoToggle = false): Promise<void> => {
462553
if (nextIndex < 0) return
463554
}
464555

465-
const nextPlayMusicInfo = {
556+
handlePlayNext({
466557
musicInfo: filteredList[nextIndex],
467558
listId: currentListId,
468559
isTempPlay: false,
469-
}
470-
471-
pause()
472-
setPlayMusicInfo(nextPlayMusicInfo.listId, nextPlayMusicInfo.musicInfo)
473-
handlePlay()
560+
})
474561
}
475562

476563
/**

src/renderer/core/useApp/usePlayer/usePlayer.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import usePlaybackRate from './usePlaybackRate'
3434
import useSoundEffect from './useSoundEffect'
3535
import useMaxOutputChannelCount from './useMaxOutputChannelCount'
3636
import { setPowerSaveBlocker } from '@renderer/core/player/utils'
37+
import usePreloadNextMusic from './usePreloadNextMusic'
3738

3839

3940
export default () => {
@@ -48,6 +49,7 @@ export default () => {
4849
useSoundEffect()
4950
usePlaybackRate()
5051
useWatchList()
52+
usePreloadNextMusic()
5153

5254
const handlePlayNext = () => {
5355
void playNext()
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import { onBeforeUnmount, watch } from '@common/utils/vueTools'
2+
import { onTimeupdate, getCurrentTime } from '@renderer/plugins/player'
3+
import { playProgress } from '@renderer/store/player/playProgress'
4+
import { musicInfo } from '@renderer/store/player/state'
5+
// import { getList } from '@renderer/store/utils'
6+
import { getNextPlayMusicInfo, resetRandomNextMusicInfo } from '@renderer/core/player'
7+
import { getMusicUrl } from '@renderer/core/music'
8+
import { checkUrl } from '@renderer/utils/request'
9+
import { appSetting } from '@renderer/store/setting'
10+
11+
const preloadMusicInfo = {
12+
isLoading: false,
13+
preProgress: 0,
14+
info: null as LX.Player.PlayMusicInfo | null,
15+
}
16+
const resetPreloadInfo = () => {
17+
preloadMusicInfo.preProgress = 0
18+
preloadMusicInfo.info = null
19+
preloadMusicInfo.isLoading = false
20+
}
21+
const preloadNextMusicUrl = async(curTime: number) => {
22+
if (preloadMusicInfo.isLoading || curTime - preloadMusicInfo.preProgress < 3) return
23+
preloadMusicInfo.isLoading = true
24+
console.log('preload next music url')
25+
const info = await getNextPlayMusicInfo()
26+
if (info) {
27+
preloadMusicInfo.info = info
28+
const url = await getMusicUrl({ musicInfo: info.musicInfo }).catch(() => '')
29+
if (url) {
30+
console.log('preload url', url)
31+
const result = await checkUrl(url).then(() => true).catch(() => false)
32+
if (!result) {
33+
const url = await getMusicUrl({ musicInfo: info.musicInfo, isRefresh: true }).catch(() => '')
34+
console.log('preload url refresh', url)
35+
}
36+
}
37+
}
38+
preloadMusicInfo.isLoading = false
39+
}
40+
41+
export default () => {
42+
const setProgress = (time: number) => {
43+
if (!musicInfo.id) return
44+
preloadMusicInfo.preProgress = time
45+
}
46+
47+
const handleSetPlayInfo = () => {
48+
resetPreloadInfo()
49+
}
50+
51+
watch(() => appSetting['player.togglePlayMethod'], () => {
52+
if (!preloadMusicInfo.info || preloadMusicInfo.info.isTempPlay) return
53+
resetRandomNextMusicInfo()
54+
preloadMusicInfo.info = null
55+
preloadMusicInfo.preProgress = playProgress.nowPlayTime
56+
})
57+
58+
window.app_event.on('setProgress', setProgress)
59+
window.app_event.on('musicToggled', handleSetPlayInfo)
60+
61+
const rOnTimeupdate = onTimeupdate(() => {
62+
const time = getCurrentTime()
63+
const duration = playProgress.maxPlayTime
64+
if (duration > 10 && duration - time < 10 && !preloadMusicInfo.info) {
65+
void preloadNextMusicUrl(time)
66+
}
67+
})
68+
69+
70+
onBeforeUnmount(() => {
71+
rOnTimeupdate()
72+
window.app_event.off('setProgress', setProgress)
73+
window.app_event.off('musicToggled', handleSetPlayInfo)
74+
})
75+
}

src/renderer/utils/request.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,3 +307,16 @@ const fetchData = async(url, method, {
307307
callback(null, resp, body)
308308
})
309309
}
310+
311+
export const checkUrl = (url, options = {}) => {
312+
return new Promise((resolve, reject) => {
313+
fetchData(url, 'head', options, (err, resp) => {
314+
if (err) return reject(err)
315+
if (resp.statusCode === 200) {
316+
resolve()
317+
} else {
318+
reject(new Error(resp.statusCode))
319+
}
320+
})
321+
})
322+
}

0 commit comments

Comments
 (0)