Updates to audio player plugin to method naming and using seconds for time values. Adding web audio player to run in browser.

This commit is contained in:
advplyr 2022-04-11 18:38:01 -05:00
parent c94e57f55e
commit 6e8d84496b
10 changed files with 367 additions and 119 deletions

View file

@ -0,0 +1,11 @@
package com.audiobookshelf.app.data
enum class PlayerState {
IDLE, BUFFERING, READY, ENDED
}
data class PlaybackMetadata(
val duration:Double,
val currentTime:Double,
val playerState:PlayerState
)

View file

@ -141,7 +141,7 @@ class MediaSessionCallback(var playerNotificationService:PlayerNotificationServi
playerNotificationService.seekBackward(seekAmount)
}
KeyEvent.KEYCODE_MEDIA_STOP -> {
playerNotificationService.terminateStream()
playerNotificationService.closePlayback()
}
KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE -> {
if (playerNotificationService.mPlayer.isPlaying) {

View file

@ -1,6 +1,7 @@
package com.audiobookshelf.app.player
import android.util.Log
import com.audiobookshelf.app.data.PlayerState
import com.google.android.exoplayer2.PlaybackException
import com.google.android.exoplayer2.Player
@ -32,22 +33,21 @@ class PlayerListener(var playerNotificationService:PlayerNotificationService) :
Log.d(tag, "STATE_READY : " + playerNotificationService.mPlayer.duration.toString())
if (lastPauseTime == 0L) {
playerNotificationService.sendClientMetadata("ready_no_sync")
lastPauseTime = -1;
} else playerNotificationService.sendClientMetadata("ready")
}
playerNotificationService.sendClientMetadata(PlayerState.READY)
}
if (playerNotificationService.currentPlayer.playbackState == Player.STATE_BUFFERING) {
Log.d(tag, "STATE_BUFFERING : " + playerNotificationService.mPlayer.currentPosition.toString())
if (lastPauseTime == 0L) playerNotificationService.sendClientMetadata("buffering_no_sync")
else playerNotificationService.sendClientMetadata("buffering")
playerNotificationService.sendClientMetadata(PlayerState.BUFFERING)
}
if (playerNotificationService.currentPlayer.playbackState == Player.STATE_ENDED) {
Log.d(tag, "STATE_ENDED")
playerNotificationService.sendClientMetadata("ended")
playerNotificationService.sendClientMetadata(PlayerState.ENDED)
}
if (playerNotificationService.currentPlayer.playbackState == Player.STATE_IDLE) {
Log.d(tag, "STATE_IDLE")
playerNotificationService.sendClientMetadata("idle")
playerNotificationService.sendClientMetadata(PlayerState.IDLE)
}
}

View file

@ -18,9 +18,7 @@ import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
import androidx.media.MediaBrowserServiceCompat
import androidx.media.utils.MediaConstants
import com.audiobookshelf.app.data.LibraryItem
import com.audiobookshelf.app.data.LocalMediaProgress
import com.audiobookshelf.app.data.PlaybackSession
import com.audiobookshelf.app.data.*
import com.audiobookshelf.app.device.DeviceManager
import com.audiobookshelf.app.media.MediaManager
import com.audiobookshelf.app.server.ApiHandler
@ -52,7 +50,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
fun onPlaybackSession(playbackSession:PlaybackSession)
fun onPlaybackClosed()
fun onPlayingUpdate(isPlaying: Boolean)
fun onMetadata(metadata: JSObject)
fun onMetadata(metadata: PlaybackMetadata)
fun onPrepare(audiobookId: String, playWhenReady: Boolean)
fun onSleepTimerEnded(currentPosition: Long)
fun onSleepTimerSet(sleepTimeRemaining: Int)
@ -375,6 +373,10 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
}
}
fun getBufferedTimeSeconds() : Double {
return getBufferedTime() / 1000.0
}
fun getDuration() : Long {
return currentPlayer.duration
}
@ -441,20 +443,16 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
currentPlayer.setPlaybackSpeed(speed)
}
fun terminateStream() {
fun closePlayback() {
currentPlayer.clearMediaItems()
currentPlaybackSession = null
clientEventEmitter?.onPlaybackClosed()
PlayerListener.lastPauseTime = 0
}
fun sendClientMetadata(stateName: String) {
var metadata = JSObject()
var duration = currentPlaybackSession?.getTotalDuration() ?: 0
metadata.put("duration", duration)
metadata.put("currentTime", getCurrentTime())
metadata.put("stateName", stateName)
clientEventEmitter?.onMetadata(metadata)
fun sendClientMetadata(playerState: PlayerState) {
var duration = currentPlaybackSession?.getTotalDuration() ?: 0.0
clientEventEmitter?.onMetadata(PlaybackMetadata(duration, getCurrentTimeSeconds(), playerState))
}
//

View file

@ -7,6 +7,7 @@ import android.util.Log
import androidx.core.content.ContextCompat
import com.audiobookshelf.app.MainActivity
import com.audiobookshelf.app.data.LocalMediaProgress
import com.audiobookshelf.app.data.PlaybackMetadata
import com.audiobookshelf.app.data.PlaybackSession
import com.audiobookshelf.app.device.DeviceManager
import com.audiobookshelf.app.player.CastManager
@ -46,8 +47,8 @@ class AbsAudioPlayer : Plugin() {
emit("onPlayingUpdate", isPlaying)
}
override fun onMetadata(metadata: JSObject) {
notifyListeners("onMetadata", metadata)
override fun onMetadata(metadata: PlaybackMetadata) {
notifyListeners("onMetadata", JSObject(jacksonObjectMapper().writeValueAsString(metadata)))
}
override fun onPrepare(audiobookId: String, playWhenReady: Boolean) {
@ -123,8 +124,8 @@ class AbsAudioPlayer : Plugin() {
@PluginMethod
fun getCurrentTime(call: PluginCall) {
Handler(Looper.getMainLooper()).post() {
var currentTime = playerNotificationService.getCurrentTime()
var bufferedTime = playerNotificationService.getBufferedTime()
var currentTime = playerNotificationService.getCurrentTimeSeconds()
var bufferedTime = playerNotificationService.getBufferedTimeSeconds()
val ret = JSObject()
ret.put("value", currentTime)
ret.put("bufferedTime", bufferedTime)
@ -157,35 +158,35 @@ class AbsAudioPlayer : Plugin() {
}
@PluginMethod
fun seekPlayer(call: PluginCall) {
var time:Long = call.getString("timeMs", "0")!!.toLong()
fun seek(call: PluginCall) {
var time:Int = call.getInt("value", 0) ?: 0 // Value in seconds
Handler(Looper.getMainLooper()).post() {
playerNotificationService.seekPlayer(time)
playerNotificationService.seekPlayer(time * 1000L) // convert to ms
call.resolve()
}
}
@PluginMethod
fun seekForward(call: PluginCall) {
var amount:Long = call.getString("amount", "0")!!.toLong()
var amount:Int = call.getInt("value", 0) ?: 0
Handler(Looper.getMainLooper()).post() {
playerNotificationService.seekForward(amount)
playerNotificationService.seekForward(amount * 1000L) // convert to ms
call.resolve()
}
}
@PluginMethod
fun seekBackward(call: PluginCall) {
var amount:Long = call.getString("amount", "0")!!.toLong()
var amount:Int = call.getInt("value", 0) ?: 0 // Value in seconds
Handler(Looper.getMainLooper()).post() {
playerNotificationService.seekBackward(amount)
playerNotificationService.seekBackward(amount * 1000L) // convert to ms
call.resolve()
}
}
@PluginMethod
fun setPlaybackSpeed(call: PluginCall) {
var playbackSpeed:Float = call.getFloat("speed", 1.0f)!!
var playbackSpeed:Float = call.getFloat("value", 1.0f) ?: 1.0f
Handler(Looper.getMainLooper()).post() {
playerNotificationService.setPlaybackSpeed(playbackSpeed)
@ -194,9 +195,9 @@ class AbsAudioPlayer : Plugin() {
}
@PluginMethod
fun terminateStream(call: PluginCall) {
fun closePlayback(call: PluginCall) {
Handler(Looper.getMainLooper()).post() {
playerNotificationService.terminateStream()
playerNotificationService.closePlayback()
call.resolve()
}
}

View file

@ -61,7 +61,7 @@
<span v-show="showFullscreen" class="material-icons next-icon text-white text-opacity-75 cursor-pointer" :class="isLoading ? 'text-opacity-10' : 'text-opacity-75'" @click.stop="jumpChapterStart">first_page</span>
<span class="material-icons jump-icon text-white cursor-pointer" :class="isLoading ? 'text-opacity-10' : 'text-opacity-75'" @click.stop="backward10">replay_10</span>
<div class="play-btn cursor-pointer shadow-sm bg-accent flex items-center justify-center rounded-full text-primary mx-4" :class="seekLoading ? 'animate-spin' : ''" @mousedown.prevent @mouseup.prevent @click.stop="playPauseClick">
<span v-if="!isLoading" class="material-icons">{{ seekLoading ? 'autorenew' : isPaused ? 'play_arrow' : 'pause' }}</span>
<span v-if="!isLoading" class="material-icons">{{ seekLoading ? 'autorenew' : !isPlaying ? 'play_arrow' : 'pause' }}</span>
<widgets-spinner-icon v-else class="h-8 w-8" />
</div>
<span class="material-icons jump-icon text-white cursor-pointer" :class="isLoading ? 'text-opacity-10' : 'text-opacity-75'" @click.stop="forward10">forward_10</span>
@ -95,7 +95,6 @@ import { AbsAudioPlayer } from '@/plugins/capacitor'
export default {
props: {
playing: Boolean,
bookmarks: {
type: Array,
default: () => []
@ -113,12 +112,10 @@ export default {
currentPlaybackRate: 1,
currentTime: 0,
bufferedTime: 0,
isResetting: false,
stateName: 'idle',
playInterval: null,
trackWidth: 0,
isPaused: true,
src: null,
isPlaying: false,
isEnded: false,
volume: 0.5,
readyTrackWidth: 0,
playedTrackWidth: 0,
@ -136,14 +133,6 @@ export default {
}
},
computed: {
isPlaying: {
get() {
return this.playing
},
set(val) {
this.$emit('update:playing', val)
}
},
menuItems() {
var items = []
items.push({
@ -306,18 +295,18 @@ export default {
setPlaybackSpeed(speed) {
console.log(`[AudioPlayer] Set Playback Rate: ${speed}`)
this.currentPlaybackRate = speed
AbsAudioPlayer.setPlaybackSpeed({ speed: speed })
AbsAudioPlayer.setPlaybackSpeed({ value: speed })
},
restart() {
this.seek(0)
},
backward10() {
if (this.isLoading) return
AbsAudioPlayer.seekBackward({ amount: '10000' })
AbsAudioPlayer.seekBackward({ value: 10 })
},
forward10() {
if (this.isLoading) return
AbsAudioPlayer.seekForward({ amount: '10000' })
AbsAudioPlayer.seekForward({ value: 10 })
},
setStreamReady() {
this.readyTrackWidth = this.trackWidth
@ -387,6 +376,7 @@ export default {
this.updateTrack()
},
updateTrack() {
// Update progress track UI
var percentDone = this.currentTime / this.totalDuration
var totalPercentDone = percentDone
var bufferedPercent = this.bufferedTime / this.totalDuration
@ -419,7 +409,8 @@ export default {
this.seekedTime = time
this.seekLoading = true
AbsAudioPlayer.seekPlayer({ timeMs: String(Math.floor(time * 1000)) })
AbsAudioPlayer.seek({ value: Math.floor(time) })
if (this.$refs.playedTrack) {
var perc = time / this.totalDuration
@ -455,7 +446,9 @@ export default {
},
async playPauseClick() {
if (this.isLoading) return
this.isPlaying = !!((await AbsAudioPlayer.playPause()) || {}).playing
this.isEnded = false
},
play() {
AbsAudioPlayer.playPlayer()
@ -471,8 +464,8 @@ export default {
clearInterval(this.playInterval)
this.playInterval = setInterval(async () => {
var data = await AbsAudioPlayer.getCurrentTime()
this.currentTime = Number((data.value / 1000).toFixed(2))
this.bufferedTime = Number((data.bufferedTime / 1000).toFixed(2))
this.currentTime = Number(data.value.toFixed(2))
this.bufferedTime = Number(data.bufferedTime.toFixed(2))
console.log('[AudioPlayer] Got Current Time', this.currentTime)
this.timeupdate()
}, 1000)
@ -481,67 +474,11 @@ export default {
clearInterval(this.playInterval)
},
resetStream(startTime) {
this.isResetting = true
this.terminateStream()
this.closePlayback()
},
terminateStream() {
closePlayback() {
if (!this.playbackSession) return
AbsAudioPlayer.terminateStream()
},
onPlayingUpdate(data) {
console.log('onPlayingUpdate', JSON.stringify(data))
this.isPaused = !data.value
this.$store.commit('setPlayerPlaying', !this.isPaused)
if (!this.isPaused) {
this.startPlayInterval()
} else {
this.stopPlayInterval()
}
},
onMetadata(data) {
console.log('onMetadata', JSON.stringify(data))
this.isLoading = false
// this.totalDuration = Number((data.duration / 1000).toFixed(2))
this.totalDuration = Number(data.duration.toFixed(2))
this.currentTime = Number((data.currentTime / 1000).toFixed(2))
this.stateName = data.stateName
if (this.stateName === 'ended' && this.isResetting) {
this.setFromObj()
}
this.timeupdate()
},
// When a playback session is started the native android/ios will send the session
onPlaybackSession(playbackSession) {
console.log('onPlaybackSession received', JSON.stringify(playbackSession))
this.playbackSession = playbackSession
this.$store.commit('setPlayerItem', this.playbackSession)
// Set track width
this.$nextTick(() => {
if (this.$refs.track) {
this.trackWidth = this.$refs.track.clientWidth
} else {
console.error('Track not loaded', this.$refs)
}
})
},
onPlaybackClosed() {
console.log('Received onPlaybackClosed evt')
this.$store.commit('setPlayerItem', null)
this.showFullscreen = false
this.playbackSession = null
},
async init() {
this.useChapterTrack = await this.$localStore.getUseChapterTrack()
this.onPlaybackSessionListener = AbsAudioPlayer.addListener('onPlaybackSession', this.onPlaybackSession)
this.onPlaybackClosedListener = AbsAudioPlayer.addListener('onPlaybackClosed', this.onPlaybackClosed)
this.onPlayingUpdateListener = AbsAudioPlayer.addListener('onPlayingUpdate', this.onPlayingUpdate)
this.onMetadataListener = AbsAudioPlayer.addListener('onMetadata', this.onMetadata)
AbsAudioPlayer.closePlayback()
},
handleGesture() {
var touchDistance = this.touchEndY - this.touchStartY
@ -581,13 +518,73 @@ export default {
})
this.$localStore.setUseChapterTrack(this.useChapterTrack)
} else if (action === 'close') {
this.terminateStream()
this.closePlayback()
}
},
forceCloseDropdownMenu() {
if (this.$refs.dropdownMenu && this.$refs.dropdownMenu.closeMenu) {
this.$refs.dropdownMenu.closeMenu()
}
},
//
// Listeners from audio AbsAudioPlayer
//
onPlayingUpdate(data) {
console.log('onPlayingUpdate', JSON.stringify(data))
this.isPlaying = !!data.value
this.$store.commit('setPlayerPlaying', this.isPlaying)
if (this.isPlaying) {
this.startPlayInterval()
} else {
this.stopPlayInterval()
}
},
onMetadata(data) {
console.log('onMetadata', JSON.stringify(data))
this.isLoading = false
this.totalDuration = Number(data.duration.toFixed(2))
this.currentTime = Number(data.currentTime.toFixed(2))
// Also includes player state data.playerState
if (data.playerState == this.$constants.PlayerState.ENDED) {
console.log('[AudioPlayer] Playback ended')
}
this.isEnded = data.playerState == this.$constants.PlayerState.ENDED
this.timeupdate()
},
// When a playback session is started the native android/ios will send the session
onPlaybackSession(playbackSession) {
console.log('onPlaybackSession received', JSON.stringify(playbackSession))
this.playbackSession = playbackSession
this.isEnded = false
this.$store.commit('setPlayerItem', this.playbackSession)
// Set track width
this.$nextTick(() => {
if (this.$refs.track) {
this.trackWidth = this.$refs.track.clientWidth
} else {
console.error('Track not loaded', this.$refs)
}
})
},
onPlaybackClosed() {
console.log('Received onPlaybackClosed evt')
this.$store.commit('setPlayerItem', null)
this.showFullscreen = false
this.isEnded = false
this.playbackSession = null
},
async init() {
this.useChapterTrack = await this.$localStore.getUseChapterTrack()
this.onPlaybackSessionListener = AbsAudioPlayer.addListener('onPlaybackSession', this.onPlaybackSession)
this.onPlaybackClosedListener = AbsAudioPlayer.addListener('onPlaybackClosed', this.onPlaybackClosed)
this.onPlayingUpdateListener = AbsAudioPlayer.addListener('onPlayingUpdate', this.onPlayingUpdate)
this.onMetadataListener = AbsAudioPlayer.addListener('onMetadata', this.onMetadata)
}
},
mounted() {

View file

@ -1,6 +1,6 @@
<template>
<div>
<app-audio-player ref="audioPlayer" :playing.sync="isPlaying" :bookmarks="bookmarks" :sleep-timer-running="isSleepTimerRunning" :sleep-time-remaining="sleepTimeRemaining" @selectPlaybackSpeed="showPlaybackSpeedModal = true" @updateTime="(t) => (currentTime = t)" @showSleepTimer="showSleepTimer" @showBookmarks="showBookmarks" />
<app-audio-player ref="audioPlayer" :bookmarks="bookmarks" :sleep-timer-running="isSleepTimerRunning" :sleep-time-remaining="sleepTimeRemaining" @selectPlaybackSpeed="showPlaybackSpeedModal = true" @updateTime="(t) => (currentTime = t)" @showSleepTimer="showSleepTimer" @showBookmarks="showBookmarks" />
<modals-playback-speed-modal v-model="showPlaybackSpeedModal" :playback-rate.sync="playbackSpeed" @update:playbackRate="updatePlaybackSpeed" @change="changePlaybackSpeed" />
<modals-sleep-timer-modal v-model="showSleepTimerModal" :current-time="sleepTimeRemaining" :sleep-timer-running="isSleepTimerRunning" :current-end-of-chapter-time="currentEndOfChapterTime" @change="selectSleepTimeout" @cancel="cancelSleepTimer" @increase="increaseSleepTimer" @decrease="decreaseSleepTimer" />
@ -14,7 +14,6 @@ import { AbsAudioPlayer } from '@/plugins/capacitor'
export default {
data() {
return {
isPlaying: false,
audioPlayerReady: false,
stream: null,
download: null,
@ -163,7 +162,7 @@ export default {
closeStreamOnly() {
// If user logs out or disconnects from server and not playing local
if (this.$refs.audioPlayer && !this.$refs.audioPlayer.isLocalPlayMethod) {
this.$refs.audioPlayer.terminateStream()
this.$refs.audioPlayer.closePlayback()
}
},
async playLibraryItem(payload) {

View file

@ -41,6 +41,7 @@ export default {
'@/plugins/init.client.js',
'@/plugins/axios.js',
'@/plugins/capacitor/index.js',
'@/plugins/capacitor/AbsAudioPlayer.js',
'@/plugins/toast.js',
'@/plugins/constants.js',
'@/plugins/haptics.js'

View file

@ -1,8 +1,232 @@
import { registerPlugin, WebPlugin } from '@capacitor/core';
const { PlayerState } = require('../constants')
var $axios = null
var vuexStore = null
class AbsAudioPlayerWeb extends WebPlugin {
constructor() {
super()
super() \
this.player = null
this.playWhenReady = false
this.playableMimeTypes = {}
this.playbackSession = null
this.audioTracks = []
this.startTime = 0
this.trackStartTime = 0
}
// Use startTime to find current track index
get currentTrackIndex() {
return Math.max(0, this.audioTracks.findIndex(t => Math.floor(t.startOffset) <= this.startTime && Math.floor(t.startOffset + t.duration) > this.startTime))
}
get currentTrack() {
return this.audioTracks[this.currentTrackIndex]
}
get playerCurrentTime() {
return this.player ? this.player.currentTime : 0
}
get currentTrackStartOffset() {
return this.currentTrack ? this.currentTrack.startOffset : 0
}
get overallCurrentTime() {
return this.currentTrackStartOffset + this.playerCurrentTime
}
get totalDuration() {
var total = 0
this.audioTracks.forEach(at => total += at.duration)
return total
}
get playerPlaying() {
return this.player && !this.player.paused
}
// PluginMethod
async prepareLibraryItem({ libraryItemId, episodeId, playWhenReady }) {
console.log('[AbsAudioPlayer] Prepare library item', libraryItemId)
if (libraryItemId.startsWith('local_')) {
// Fetch Local - local not implemented on web
} else {
var route = !episodeId ? `/api/items/${libraryItemId}/play` : `/api/items/${libraryItemId}/play/${episodeId}`
var playbackSession = await $axios.$post(route, { mediaPlayer: 'html5-mobile', forceDirectPlay: true })
if (playbackSession) {
this.setAudioPlayer(playbackSession, true)
}
}
return false
}
// PluginMethod
async playPause() {
if (!this.player) return
if (this.player.ended) {
this.startTime = 0
this.playWhenReady = true
this.loadCurrentTrack()
return
}
if (this.player.paused) this.player.play()
else this.player.pause()
return {
playing: !this.player.paused
}
}
// PluginMethod
playPlayer() {
if (this.player) this.player.play()
}
// PluginMethod
pausePlayer() {
if (this.player) this.player.pause()
}
// PluginMethod
async closePlayback() {
this.playbackSession = null
this.audioTracks = []
this.playWhenReady = false
if (this.player) {
this.player.remove()
this.player = null
}
this.notifyListeners('onClosePlayback')
}
// PluginMethod
=]ikolp;awqsxcdz ffvb34seek({ val\
ue }) {
this.startTime = value
this.playWhenReady = this.playerPlaying
this.loadCurrentTrack()
}
// PluginMethod
seekForward({ value }) {
this.startTime = Math.min(this.overallCurrentTime + value, this.totalDuration)
this.playWhenReady = this.playerPlaying
this.loadCurrentTrack()
}
// PluginMethod
seekBackward({ value }) {
this.startTime = Math.max(0, this.overallCurrentTime - value)
this.playWhenReady = this.playerPlaying
this.loadCurrentTrack()
}
// PluginMethod
setPlaybackSpeed({ value }) {
if (this.player) this.player.playbackRate = value
}
// PluginMethod
async getCurrentTime() {
return {
value: this.overallCurrentTime,
bufferedTime: 0
}
}
initializePlayer() {
if (document.getElementById('audio-player')) {
document.getElementById('audio-player').remove()
}
var audioEl = document.createElement('audio')
audioEl.id = 'audio-player'
audioEl.style.display = 'none'
document.body.appendChild(audioEl)
this.player = audioEl
this.player.addEventListener('play', this.evtPlay.bind(this))
this.player.addEventListener('pause', this.evtPause.bind(this))
this.player.addEventListener('progress', this.evtProgress.bind(this))
this.player.addEventListener('ended', this.evtEnded.bind(this))
this.player.addEventListener('error', this.evtError.bind(this))
this.player.addEventListener('loadedmetadata', this.evtLoadedMetadata.bind(this))
this.player.addEventListener('timeupdate', this.evtTimeupdate.bind(this))
var mimeTypes = ['audio/flac', 'audio/mpeg', 'audio/mp4', 'audio/ogg', 'audio/aac']
mimeTypes.forEach((mt) => {
this.playableMimeTypes[mt] = this.player.canPlayType(mt)
})
console.log(`[LocalPlayer] Supported mime types`, this.playableMimeTypes)
}
evtPlay() {
this.notifyListeners('onPlayingUpdate', { value: true })
}
evtPause() {
this.notifyListeners('onPlayingUpdate', { value: false })
}
evtProgress() {
// var lastBufferTime = this.getLastBufferedTime()
}
evtEnded() {
if (this.currentTrackIndex < this.audioTracks.length - 1) { // Has next track
console.log(`[AbsAudioPlayer] Track ended - loading next track ${this.currentTrackIndex + 1}`)
var nextTrack = this.audioTracks[this.currentTrackIndex + 1]
this.playWhenReady = true
this.startTime = Math.floor(nextTrack.startOffset)
this.loadCurrentTrack()
} else {
console.log(`[LocalPlayer] Ended`)
this.sendPlaybackMetadata(PlayerState.ENDED)
}
}
evtError(error) {
console.error('Player error', error)
}
evtLoadedMetadata(data) {
console.log(`[AbsAudioPlayer] Loaded metadata`, data)
if (!this.player) {
console.error('[AbsAudioPlayer] evtLoadedMetadata player not set')
return
}
this.player.currentTime = this.trackStartTime
this.sendPlaybackMetadata(PlayerState.READY)
if (this.playWhenReady) {
this.player.play()
}
}
evtTimeupdate() { }
sendPlaybackMetadata(playerState) {
var currentTime = this.player ? this.player.currentTime || 0 : 0
this.notifyListeners('onMetadata', {
duration: this.totalDuration,
currentTime,
playerState
})
}
loadCurrentTrack() {
if (!this.currentTrack) return
// When direct play track is loaded current time needs to be set
this.trackStartTime = Math.max(0, this.startTime - (this.currentTrack.startOffset || 0))
this.player.src = `${vuexStore.getters['user/getServerAddress']}${this.currentTrack.contentUrl}?token=${vuexStore.getters['user/getToken']}`
console.log(`[AbsAudioPlayer] Loading track src ${this.player.src}`)
this.player.load()
}
setAudioPlayer(playbackSession, playWhenReady = false) {
if (!this.player) {
this.initializePlayer()
}
// Notify client playback session set
this.notifyListeners('onPlaybackSession', playbackSession)
this.playbackSession = playbackSession
this.playWhenReady = playWhenReady
this.audioTracks = playbackSession.audioTracks || []
this.startTime = playbackSession.currentTime
this.loadCurrentTrack()
}
}
@ -11,3 +235,8 @@ const AbsAudioPlayer = registerPlugin('AbsAudioPlayer', {
})
export { AbsAudioPlayer }
export default ({ app, store }, inject) => {
$axios = app.$axios
vuexStore = store
}

View file

@ -22,11 +22,23 @@ const PlayMethod = {
LOCAL: 3
}
const PlayerState = {
IDLE: 0,
BUFFERING: 1,
READY: 2,
ENDED: 3
}
const Constants = {
DownloadStatus,
CoverDestination,
BookCoverAspectRatio,
PlayMethod
PlayMethod,
PlayerState
}
export {
PlayerState
}
export default ({ app }, inject) => {