first commit
This commit is contained in:
@@ -0,0 +1,220 @@
|
||||
/**
|
||||
* 早安电台 — 音频管理器
|
||||
* 封装 wx.getBackgroundAudioManager(),管理全局背景播放
|
||||
* 确保小程序切后台后音频不断
|
||||
* 播放时自动上报收听历史
|
||||
*/
|
||||
|
||||
const api = require('./api')
|
||||
|
||||
let bgAudioManager = null
|
||||
let appInstance = null
|
||||
let _switching = false // 切换音频时的锁,防止 onStop 事件干扰
|
||||
|
||||
/**
|
||||
* 初始化音频管理器
|
||||
* @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(() => {
|
||||
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
|
||||
|
||||
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 {
|
||||
// 检查 bgAudioManager 是否还有有效的 src
|
||||
// 音频结束/被系统回收后,src 会变为空,此时 play() 无效
|
||||
// 需要重新设置 src 来恢复播放
|
||||
var currentSrc = ''
|
||||
try {
|
||||
currentSrc = bgAudioManager.src
|
||||
} catch (e) {
|
||||
currentSrc = ''
|
||||
}
|
||||
|
||||
if (!currentSrc) {
|
||||
// src 已被清空(音频结束、系统回收等),重新播放
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user