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