mirror of
https://github.com/ikun0014/lx-music-mobile.git
synced 2025-07-15 15:12:07 +08:00
293 lines
9.5 KiB
TypeScript
293 lines
9.5 KiB
TypeScript
import TrackPlayer, { State } from 'react-native-track-player'
|
|
import BackgroundTimer from 'react-native-background-timer'
|
|
import { defaultUrl } from '@/config'
|
|
// import { action as playerAction } from '@/store/modules/player'
|
|
import settingState from '@/store/setting/state'
|
|
|
|
|
|
const list: LX.Player.Track[] = []
|
|
|
|
const defaultUserAgent = 'Mozilla/5.0 (Linux; Android 10; Pixel 3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.79 Mobile Safari/537.36'
|
|
const httpRxp = /^(https?:\/\/.+|\/.+)/
|
|
|
|
let isPlaying = false
|
|
let prevDuration = -1
|
|
|
|
const formatMusicInfo = (musicInfo: LX.Player.PlayMusic) => {
|
|
return 'progress' in musicInfo ? {
|
|
id: musicInfo.id,
|
|
pic: musicInfo.metadata.musicInfo.meta.picUrl,
|
|
name: musicInfo.metadata.musicInfo.name,
|
|
singer: musicInfo.metadata.musicInfo.singer,
|
|
album: musicInfo.metadata.musicInfo.meta.albumName,
|
|
} : {
|
|
id: musicInfo.id,
|
|
pic: musicInfo.meta.picUrl,
|
|
name: musicInfo.name,
|
|
singer: musicInfo.singer,
|
|
album: musicInfo.meta.albumName,
|
|
}
|
|
}
|
|
|
|
const buildTracks = (musicInfo: LX.Player.PlayMusic, url: LX.Player.Track['url'], duration?: LX.Player.Track['duration']): LX.Player.Track[] => {
|
|
const mInfo = formatMusicInfo(musicInfo)
|
|
const track = [] as LX.Player.Track[]
|
|
const isShowNotificationImage = settingState.setting['player.isShowNotificationImage']
|
|
const album = mInfo.album || undefined
|
|
const artwork = isShowNotificationImage && mInfo.pic && httpRxp.test(mInfo.pic) ? mInfo.pic : undefined
|
|
if (url) {
|
|
track.push({
|
|
id: `${mInfo.id}__//${Math.random()}__//${url}`,
|
|
url,
|
|
title: mInfo.name || 'Unknow',
|
|
artist: mInfo.singer || 'Unknow',
|
|
album,
|
|
artwork,
|
|
userAgent: defaultUserAgent,
|
|
musicId: mInfo.id,
|
|
// original: { ...musicInfo },
|
|
duration,
|
|
})
|
|
}
|
|
track.push({
|
|
id: `${mInfo.id}__//${Math.random()}__//default`,
|
|
url: defaultUrl,
|
|
title: mInfo.name || 'Unknow',
|
|
artist: mInfo.singer || 'Unknow',
|
|
album,
|
|
artwork,
|
|
musicId: mInfo.id,
|
|
// original: { ...musicInfo },
|
|
duration: 0,
|
|
})
|
|
return track
|
|
// console.log('buildTrack', musicInfo.name, url)
|
|
}
|
|
// const buildTrack = (musicInfo: LX.Player.PlayMusic, url: LX.Player.Track['url'], duration?: LX.Player.Track['duration']): LX.Player.Track => {
|
|
// const mInfo = formatMusicInfo(musicInfo)
|
|
// const isShowNotificationImage = settingState.setting['player.isShowNotificationImage']
|
|
// const album = mInfo.album || undefined
|
|
// const artwork = isShowNotificationImage && mInfo.pic && httpRxp.test(mInfo.pic) ? mInfo.pic : undefined
|
|
// return url
|
|
// ? {
|
|
// id: `${mInfo.id}__//${Math.random()}__//${url}`,
|
|
// url,
|
|
// title: mInfo.name || 'Unknow',
|
|
// artist: mInfo.singer || 'Unknow',
|
|
// album,
|
|
// artwork,
|
|
// userAgent: defaultUserAgent,
|
|
// musicId: `${mInfo.id}`,
|
|
// original: { ...musicInfo },
|
|
// duration,
|
|
// }
|
|
// : {
|
|
// id: `${mInfo.id}__//${Math.random()}__//default`,
|
|
// url: defaultUrl,
|
|
// title: mInfo.name || 'Unknow',
|
|
// artist: mInfo.singer || 'Unknow',
|
|
// album,
|
|
// artwork,
|
|
// musicId: `${mInfo.id}`,
|
|
// original: { ...musicInfo },
|
|
// duration: 0,
|
|
// }
|
|
// }
|
|
|
|
export const isTempTrack = (trackId: string) => /\/\/default$/.test(trackId)
|
|
|
|
|
|
export const getCurrentTrackId = async() => {
|
|
const currentTrackIndex = await TrackPlayer.getCurrentTrack()
|
|
return list[currentTrackIndex]?.id
|
|
}
|
|
export const getCurrentTrack = async() => {
|
|
const currentTrackIndex = await TrackPlayer.getCurrentTrack()
|
|
return list[currentTrackIndex]
|
|
}
|
|
|
|
export const updateMetaData = async(musicInfo: LX.Player.MusicInfo, isPlay: boolean, force = false) => {
|
|
if (!force && isPlay == isPlaying) {
|
|
const duration = await TrackPlayer.getDuration()
|
|
// console.log('currentIsPlaying', prevDuration, duration)
|
|
if (prevDuration != duration) {
|
|
prevDuration = duration
|
|
const trackInfo = await getCurrentTrack()
|
|
if (trackInfo && musicInfo) {
|
|
delayUpdateMusicInfo(musicInfo)
|
|
}
|
|
}
|
|
} else {
|
|
const [duration, trackInfo] = await Promise.all([TrackPlayer.getDuration(), getCurrentTrack()])
|
|
prevDuration = duration
|
|
if (trackInfo && musicInfo) {
|
|
delayUpdateMusicInfo(musicInfo)
|
|
}
|
|
}
|
|
}
|
|
|
|
const handlePlayMusic = async(musicInfo: LX.Player.PlayMusic, url: string, time: number) => {
|
|
// console.log(tracks, time)
|
|
const tracks = buildTracks(musicInfo, url)
|
|
const track = tracks[0]
|
|
// await updateMusicInfo(track)
|
|
const currentTrackIndex = await TrackPlayer.getCurrentTrack()
|
|
await TrackPlayer.add(tracks).then(() => list.push(...tracks))
|
|
const queue = await TrackPlayer.getQueue() as LX.Player.Track[]
|
|
await TrackPlayer.skip(queue.findIndex(t => t.id == track.id))
|
|
|
|
if (currentTrackIndex == null) {
|
|
if (!isTempTrack(track.id as string)) {
|
|
if (time) await TrackPlayer.seekTo(time)
|
|
if (global.lx.restorePlayInfo) {
|
|
await TrackPlayer.pause()
|
|
// let startupAutoPlay = settingState.setting['player.startupAutoPlay']
|
|
global.lx.restorePlayInfo = null
|
|
|
|
// TODO startupAutoPlay
|
|
// if (startupAutoPlay) store.dispatch(playerAction.playMusic())
|
|
} else {
|
|
await TrackPlayer.play()
|
|
}
|
|
}
|
|
} else {
|
|
await TrackPlayer.pause()
|
|
if (!isTempTrack(track.id as string)) {
|
|
await TrackPlayer.seekTo(time)
|
|
await TrackPlayer.play()
|
|
}
|
|
}
|
|
|
|
if (queue.length > 2) {
|
|
void TrackPlayer.remove(Array(queue.length - 2).fill(null).map((_, i) => i)).then(() => list.splice(0, list.length - 2))
|
|
}
|
|
}
|
|
let playPromise = Promise.resolve()
|
|
let actionId = Math.random()
|
|
export const playMusic = (musicInfo: LX.Player.PlayMusic, url: string, time: number) => {
|
|
const id = actionId = Math.random()
|
|
void playPromise.finally(() => {
|
|
if (id != actionId) return
|
|
playPromise = handlePlayMusic(musicInfo, url, time)
|
|
})
|
|
}
|
|
|
|
// let musicId = null
|
|
// let duration = 0
|
|
// let artwork = null
|
|
const updateMetaInfo = async(mInfo: LX.Player.MusicInfo) => {
|
|
const isShowNotificationImage = settingState.setting['player.isShowNotificationImage']
|
|
// const mInfo = formatMusicInfo(musicInfo)
|
|
// console.log('+++++updateMusicPic+++++', track.artwork, track.duration)
|
|
|
|
// if (track.musicId == musicId) {
|
|
// if (global.playInfo.musicInfo.img != null) artwork = global.playInfo.musicInfo.img
|
|
// if (track.duration != null) duration = global.playInfo.duration
|
|
// } else {
|
|
// musicId = track.musicId
|
|
// artwork = global.playInfo.musicInfo.img
|
|
// duration = global.playInfo.duration || 0
|
|
// }
|
|
// console.log('+++++updateMetaInfo+++++', mInfo.name)
|
|
isPlaying = await TrackPlayer.getState() == State.Playing
|
|
await TrackPlayer.updateNowPlayingMetadata({
|
|
title: mInfo.name ?? 'Unknow',
|
|
artist: mInfo.singer ?? 'Unknow',
|
|
album: mInfo.album ?? undefined,
|
|
artwork: isShowNotificationImage ? mInfo.pic ?? undefined : undefined,
|
|
duration: prevDuration || 0,
|
|
}, isPlaying)
|
|
}
|
|
|
|
|
|
// 解决快速切歌导致的通知栏歌曲信息与当前播放歌曲对不上的问题
|
|
const debounceUpdateMetaInfoTools = {
|
|
updateMetaPromise: Promise.resolve(),
|
|
musicInfo: null as LX.Player.MusicInfo | null,
|
|
debounce(fn: (musicInfo: LX.Player.MusicInfo) => void | Promise<void>) {
|
|
// let delayTimer = null
|
|
let isDelayRun = false
|
|
let timer: number | null = null
|
|
let _musicInfo: LX.Player.MusicInfo | null = null
|
|
return (musicInfo: LX.Player.MusicInfo) => {
|
|
// console.log('debounceUpdateMetaInfoTools', musicInfo)
|
|
if (timer) {
|
|
BackgroundTimer.clearTimeout(timer)
|
|
timer = null
|
|
}
|
|
// if (delayTimer) {
|
|
// BackgroundTimer.clearTimeout(delayTimer)
|
|
// delayTimer = null
|
|
// }
|
|
if (isDelayRun) {
|
|
_musicInfo = musicInfo
|
|
timer = BackgroundTimer.setTimeout(() => {
|
|
timer = null
|
|
let musicInfo = _musicInfo
|
|
_musicInfo = null
|
|
if (!musicInfo) return
|
|
// isDelayRun = false
|
|
void fn(musicInfo)
|
|
}, 1000)
|
|
} else {
|
|
isDelayRun = true
|
|
void fn(musicInfo)
|
|
BackgroundTimer.setTimeout(() => {
|
|
// delayTimer = null
|
|
isDelayRun = false
|
|
}, 1000)
|
|
}
|
|
}
|
|
},
|
|
init() {
|
|
return this.debounce(async(musicInfo: LX.Player.MusicInfo) => {
|
|
this.musicInfo = musicInfo
|
|
return this.updateMetaPromise.then(() => {
|
|
// console.log('run')
|
|
if (this.musicInfo?.id === musicInfo.id) {
|
|
this.updateMetaPromise = updateMetaInfo(musicInfo)
|
|
}
|
|
})
|
|
})
|
|
},
|
|
}
|
|
|
|
export const delayUpdateMusicInfo = debounceUpdateMetaInfoTools.init()
|
|
|
|
// export const delayUpdateMusicInfo = ((fn, delay = 800) => {
|
|
// let delayTimer = null
|
|
// let isDelayRun = false
|
|
// let timer = null
|
|
// let _track = null
|
|
// return track => {
|
|
// _track = track
|
|
// if (timer) {
|
|
// BackgroundTimer.clearTimeout(timer)
|
|
// timer = null
|
|
// }
|
|
// if (isDelayRun) {
|
|
// if (delayTimer) {
|
|
// BackgroundTimer.clearTimeout(delayTimer)
|
|
// delayTimer = null
|
|
// }
|
|
// timer = BackgroundTimer.setTimeout(() => {
|
|
// timer = null
|
|
// let track = _track
|
|
// _track = null
|
|
// isDelayRun = false
|
|
// fn(track)
|
|
// }, delay)
|
|
// } else {
|
|
// isDelayRun = true
|
|
// fn(track)
|
|
// delayTimer = BackgroundTimer.setTimeout(() => {
|
|
// delayTimer = null
|
|
// isDelayRun = false
|
|
// }, 500)
|
|
// }
|
|
// }
|
|
// })(track => {
|
|
// console.log('+++++delayUpdateMusicPic+++++', track.artwork)
|
|
// updateMetaInfo(track)
|
|
// })
|