/** * 早安电台 — 音频管理器 * 封装 wx.getBackgroundAudioManager(),管理全局背景播放 * 确保小程序切后台后音频不断 * 播放时自动上报收听历史 */ const api = require('./api') let bgAudioManager = null let appInstance = null let _switching = false // 切换音频时的锁,防止 onStop 事件干扰 let _ended = false // 标记音频是否已自然播放完毕 /** * 初始化音频管理器 * @param {Object} app - App 实例 */ function init(app) { appInstance = app bgAudioManager = wx.getBackgroundAudioManager() // 绑定音频事件 bgAudioManager.onPlay(() => { _switching = false // 新音频开始播放,释放锁 updatePlayState(true) }) bgAudioManager.onPause(() => { updatePlayState(false) // 暂停时上报进度 reportHistory() }) bgAudioManager.onStop(() => { // 如果正在切换音频,不处理 onStop(新的 src 设置会触发旧的 onStop) if (_switching) return reportHistory() updatePlayState(false) appInstance.globalData.currentTime = 0 appInstance.emit('playerStateChange', getState()) }) bgAudioManager.onEnded(() => { _ended = true reportHistory() updatePlayState(false) appInstance.globalData.currentTime = appInstance.globalData.duration appInstance.emit('playerStateChange', getState()) }) // 进度更新回调 bgAudioManager.onTimeUpdate(() => { if (_switching) return const ct = Math.floor(bgAudioManager.currentTime || 0) const dur = Math.floor(bgAudioManager.duration || 0) appInstance.globalData.currentTime = ct if (dur > 0) { appInstance.globalData.duration = dur } appInstance.emit('timeUpdate', { currentTime: ct, duration: dur }) }) bgAudioManager.onError((err) => { console.error('[AudioManager] 播放出错:', err) _switching = false updatePlayState(false) }) bgAudioManager.onWaiting(() => { console.log('[AudioManager] 缓冲中...') }) } /** * 更新播放状态并通知 */ function updatePlayState(isPlaying) { appInstance.globalData.isPlaying = isPlaying appInstance.emit('playerStateChange', getState()) } /** * 获取当前播放器状态 */ function getState() { return { activeContent: appInstance.globalData.activeContent, isPlaying: appInstance.globalData.isPlaying, currentTime: appInstance.globalData.currentTime, duration: appInstance.globalData.duration, playbackRate: appInstance.globalData.playbackRate } } /** * 获取音频 URL */ function getAudioUrl(content) { if (!content) return '' if (content.audio && content.audio.url) return content.audio.url if (content.audioUrl) return content.audioUrl return '' } /** * 上报收听历史到后端 */ function reportHistory() { const content = appInstance.globalData.activeContent if (!content || !content.id) return api.addHistory({ programId: content.id, progress: appInstance.globalData.currentTime || 0, duration: appInstance.globalData.duration || content.duration || 0 }).catch(function (err) { console.warn('[AudioManager] 上报历史失败:', err) }) } /** * 播放指定内容 * @param {Object} content - 音频内容对象 { id, title, duration, audioUrl/audio.url, ... } */ function playContent(content) { if (!bgAudioManager || !content) return var audioUrl = getAudioUrl(content) if (!audioUrl) { console.error('[AudioManager] 音频内容缺少 audioUrl:', content) wx.showToast({ title: '音频地址无效', icon: 'none' }) return } // 如果当前有播放内容,先上报历史 var oldContent = appInstance.globalData.activeContent if (oldContent && oldContent.id && oldContent.id !== content.id) { reportHistory() } // 标记正在切换,防止旧音频的 onStop 干扰 _switching = true _ended = false // 重置结束标记 appInstance.globalData.activeContent = content appInstance.globalData.currentTime = 0 appInstance.globalData.duration = content.duration || 0 // 设置背景音频属性(必须设置 title,否则 iOS 上不允许播放) bgAudioManager.title = content.title || '早安电台' bgAudioManager.singer = '早安电台' bgAudioManager.epname = content.title || '早安电台' bgAudioManager.coverImgUrl = (content.cover && content.cover.url) || content.coverUrl || '' // 设置音频源(这会触发旧音频的 onStop,然后自动开始播放新音频) bgAudioManager.src = audioUrl appInstance.emit('playerStateChange', getState()) } /** * 切换播放/暂停 * 关键:如果音频已结束或 src 被回收,需要重新设置 src 才能恢复播放 */ function togglePlay() { if (!bgAudioManager || !appInstance.globalData.activeContent) return if (appInstance.globalData.isPlaying) { bgAudioManager.pause() } else { // 音频已自然播放完毕,或 src 被系统回收 → 从头重新播放 var srcEmpty = false try { srcEmpty = !bgAudioManager.src } catch (e) { srcEmpty = true } if (_ended || srcEmpty) { _ended = false playContent(appInstance.globalData.activeContent) } else { bgAudioManager.play() } } } /** * 跳转到指定时间(秒) */ function seekTo(time) { if (!bgAudioManager) return bgAudioManager.seek(time) appInstance.globalData.currentTime = time appInstance.emit('timeUpdate', { currentTime: time, duration: appInstance.globalData.duration }) } /** * 设置播放速率 * @param {number} rate - 0.5 / 0.75 / 1.0 / 1.25 / 1.5 / 2.0 */ function setPlaybackRate(rate) { if (!bgAudioManager) return bgAudioManager.playbackRate = rate appInstance.globalData.playbackRate = rate appInstance.emit('playerStateChange', getState()) } module.exports = { init, getState, playContent, togglePlay, seekTo, setPlaybackRate }