Files
lx-music-mobile-mod/src/plugins/player/playList.ts
2023-12-20 11:49:59 +08:00

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)
// })