diff --git a/packages/uni-api/src/index.ts b/packages/uni-api/src/index.ts index 737381c34b914ac28f1c86c040c289dd9efad8b8..0315eef8ab4f782b3d210143e6a56913b529d688 100644 --- a/packages/uni-api/src/index.ts +++ b/packages/uni-api/src/index.ts @@ -21,6 +21,7 @@ export * from './protocols/base/canIUse' export * from './protocols/context/context' export * from './protocols/context/canvas' +export * from './protocols/context/backgroundAudio' export * from './protocols/device/makePhoneCall' export * from './protocols/device/setClipboardData' diff --git a/packages/uni-api/src/protocols/context/backgroundAudio.ts b/packages/uni-api/src/protocols/context/backgroundAudio.ts new file mode 100644 index 0000000000000000000000000000000000000000..f592c3099433fdd6f86822d8231f3a8c7dfcff47 --- /dev/null +++ b/packages/uni-api/src/protocols/context/backgroundAudio.ts @@ -0,0 +1,2 @@ +export const API_BACKGROUND_AUDIO = 'getBackgroundAudioManager' +export type API_TYPE_BACKGROUND_AUDIO = typeof uni.getBackgroundAudioManager diff --git a/packages/uni-app-plus/src/service/api/context/backgroundAudio.ts b/packages/uni-app-plus/src/service/api/context/backgroundAudio.ts new file mode 100644 index 0000000000000000000000000000000000000000..bb2403addbd996aff45a32e2eee64d8752f11323 --- /dev/null +++ b/packages/uni-app-plus/src/service/api/context/backgroundAudio.ts @@ -0,0 +1,449 @@ +import { extend } from '@vue/shared' +import { + defineSyncApi, + API_BACKGROUND_AUDIO, + API_TYPE_BACKGROUND_AUDIO, +} from '@dcloudio/uni-api' +import { once } from '@dcloudio/uni-shared' +import { getRealPath } from '@dcloudio/uni-platform' + +type eventNames = + | 'canplay' + | 'play' + | 'pause' + | 'stop' + | 'ended' + | 'timeUpdate' + | 'prev' + | 'next' + | 'error' + | 'waiting' +type onEventNames = + | 'onNext' + | 'onPrev' + | 'onWaiting' + | 'onTimeUpdate' + | 'onEnded' + | 'onStop' + | 'onPause' + | 'onPlay' + | 'onCanplay' + | 'onError' +type AudioEvents = + | 'canplay' + | 'play' + | 'pause' + | 'stop' + | 'ended' + | 'error' + | 'waiting' + | 'seeking' + | 'seeked' +type Events = 'play' | 'pause' | 'ended' | 'stop' | 'canplay' +type Audio = PlusAudioAudioPlayer & { + src?: string + title?: string + epname?: string + singer?: string + coverImgUrl?: string + webUrl?: string + startTime?: number + isStopped?: boolean +} + +const eventNames: eventNames[] = [ + 'canplay', + 'play', + 'pause', + 'stop', + 'ended', + 'timeUpdate', + 'prev', + 'next', + 'error', + 'waiting', +] + +const callbacks: Record = { + canplay: [], + play: [], + pause: [], + stop: [], + ended: [], + timeUpdate: [], + prev: [], + next: [], + error: [], + waiting: [], +} + +let audio: Audio +let timeUpdateTimer: number | null = null +const TIME_UPDATE = 250 +const events: Events[] = ['play', 'pause', 'ended', 'stop', 'canplay'] + +function startTimeUpdateTimer() { + stopTimeUpdateTimer() + timeUpdateTimer = setInterval(() => { + onBackgroundAudioStateChange({ state: 'timeUpdate' }) + }, TIME_UPDATE) +} + +function stopTimeUpdateTimer() { + if (timeUpdateTimer !== null) { + clearInterval(timeUpdateTimer) + } +} + +function initMusic() { + if (audio) { + return + } + const publish = UniServiceJSBridge.invokeOnCallback + audio = plus.audio.createPlayer({ + autoplay: true, + backgroundControl: true, + }) + audio.src = + audio.title = + audio.epname = + audio.singer = + audio.coverImgUrl = + audio.webUrl = + '' + audio.startTime = 0 + events.forEach((event) => { + audio.addEventListener(event as AudioEvents, () => { + // 添加 isStopped 属性是为了解决 安卓设备停止播放后获取播放进度不正确的问题 + if (event === 'play') { + audio.isStopped = false + startTimeUpdateTimer() + } else if (event === 'stop') { + audio.isStopped = true + } + + if (event === 'pause' || event === 'ended' || event === 'stop') { + stopTimeUpdateTimer() + } + + const eventName = `onMusic${event[0].toUpperCase() + event.substr(1)}` + publish(eventName, { + dataUrl: audio.src, + errMsg: `${eventName}:ok`, + }) + onBackgroundAudioStateChange({ + state: event, + dataUrl: audio.src, + }) + }) + }) + audio.addEventListener('waiting', () => { + stopTimeUpdateTimer() + onBackgroundAudioStateChange({ + state: 'waiting', + dataUrl: audio.src, + }) + }) + audio.addEventListener('error', (err) => { + stopTimeUpdateTimer() + publish('onMusicError', { + dataUrl: audio.src, + errMsg: 'Error:' + err.message, + }) + onBackgroundAudioStateChange({ + state: 'error', + dataUrl: audio.src, + errMsg: err.message, + errCode: err.code, + }) + }) + // @ts-ignore + audio.addEventListener('prev', () => publish('onBackgroundAudioPrev')) + // @ts-ignore + audio.addEventListener('next', () => publish('onBackgroundAudioNext')) +} + +function getBackgroundAudioState() { + let data = { + duration: 0, + currentTime: 0, + paused: false, + src: '', + buffered: 0, + title: '', + epname: '', + singer: '', + coverImgUrl: '', + webUrl: '', + startTime: 0, + errMsg: 'getBackgroundAudioState:ok', + } + if (audio) { + const newData = { + duration: audio.getDuration() || 0, + currentTime: audio.isStopped ? 0 : audio.getPosition(), + paused: audio.isPaused(), + src: audio.src, + buffered: audio.getBuffered(), + title: audio.title, + epname: audio.epname, + singer: audio.singer, + coverImgUrl: audio.coverImgUrl, + webUrl: audio.webUrl, + startTime: audio.startTime, + } + data = extend(data, newData) + } + return data +} + +function setMusicState(args: Partial