Implement start time override

This commit is contained in:
advplyr 2023-01-15 16:33:51 -06:00
parent 8f6dd72df2
commit acf85f4f09
5 changed files with 77 additions and 40 deletions

View file

@ -94,7 +94,10 @@ class MediaProgressSyncer(val playerNotificationService:PlayerNotificationServic
} }
fun stop(shouldSync:Boolean? = true, cb: () -> Unit) { fun stop(shouldSync:Boolean? = true, cb: () -> Unit) {
if (!listeningTimerRunning) return if (!listeningTimerRunning) {
reset()
return cb()
}
listeningTimerTask?.cancel() listeningTimerTask?.cancel()
listeningTimerTask = null listeningTimerTask = null
@ -255,6 +258,7 @@ class MediaProgressSyncer(val playerNotificationService:PlayerNotificationServic
} }
} }
} else if (shouldSyncServer) { } else if (shouldSyncServer) {
Log.d(tag, "sync: currentSessionId=$currentSessionId")
apiHandler.sendProgressSync(currentSessionId, syncData) { syncSuccess, errorMsg -> apiHandler.sendProgressSync(currentSessionId, syncData) { syncSuccess, errorMsg ->
if (syncSuccess) { if (syncSuccess) {
Log.d(tag, "Progress sync data sent to server $currentDisplayTitle for time $currentTime") Log.d(tag, "Progress sync data sent to server $currentDisplayTitle for time $currentTime")

View file

@ -14,10 +14,9 @@ class PlayerListener(var playerNotificationService:PlayerNotificationService) :
companion object { companion object {
var lastPauseTime: Long = 0 //ms var lastPauseTime: Long = 0 //ms
var lazyIsPlaying: Boolean = false
} }
private var lazyIsPlaying: Boolean = false
override fun onPlayerError(error: PlaybackException) { override fun onPlayerError(error: PlaybackException) {
val errorMessage = error.message ?: "Unknown Error" val errorMessage = error.message ?: "Unknown Error"
Log.e(tag, "onPlayerError $errorMessage") Log.e(tag, "onPlayerError $errorMessage")

View file

@ -9,6 +9,7 @@ import com.audiobookshelf.app.device.DeviceManager
import com.audiobookshelf.app.media.MediaEventManager import com.audiobookshelf.app.media.MediaEventManager
import com.audiobookshelf.app.player.CastManager import com.audiobookshelf.app.player.CastManager
import com.audiobookshelf.app.player.MediaProgressSyncer import com.audiobookshelf.app.player.MediaProgressSyncer
import com.audiobookshelf.app.player.PlayerListener
import com.audiobookshelf.app.player.PlayerNotificationService import com.audiobookshelf.app.player.PlayerNotificationService
import com.audiobookshelf.app.server.ApiHandler import com.audiobookshelf.app.server.ApiHandler
import com.fasterxml.jackson.core.json.JsonReadFeature import com.fasterxml.jackson.core.json.JsonReadFeature
@ -168,6 +169,8 @@ class AbsAudioPlayer : Plugin() {
val episodeId = call.getString("episodeId", "").toString() val episodeId = call.getString("episodeId", "").toString()
val playWhenReady = call.getBoolean("playWhenReady") == true val playWhenReady = call.getBoolean("playWhenReady") == true
val playbackRate = call.getFloat("playbackRate",1f) ?: 1f val playbackRate = call.getFloat("playbackRate",1f) ?: 1f
val startTimeOverride = call.getDouble("startTime")
Log.d(tag, "prepareLibraryItem lid=$libraryItemId, startTimeOverride=$startTimeOverride")
if (libraryItemId.isEmpty()) { if (libraryItemId.isEmpty()) {
Log.e(tag, "Invalid call to play library item no library item id") Log.e(tag, "Invalid call to play library item no library item id")
@ -189,10 +192,16 @@ class AbsAudioPlayer : Plugin() {
Handler(Looper.getMainLooper()).post { Handler(Looper.getMainLooper()).post {
Log.d(tag, "prepareLibraryItem: Preparing Local Media item ${jacksonMapper.writeValueAsString(it)}") Log.d(tag, "prepareLibraryItem: Preparing Local Media item ${jacksonMapper.writeValueAsString(it)}")
val playbackSession = it.getPlaybackSession(episode) val playbackSession = it.getPlaybackSession(episode)
if (startTimeOverride != null) {
Log.d(tag, "prepareLibraryItem: Using start time override $startTimeOverride")
playbackSession.currentTime = startTimeOverride
}
if (playerNotificationService.mediaProgressSyncer.listeningTimerRunning) { // If progress syncing then first stop before preparing next if (playerNotificationService.mediaProgressSyncer.listeningTimerRunning) { // If progress syncing then first stop before preparing next
playerNotificationService.mediaProgressSyncer.stop { playerNotificationService.mediaProgressSyncer.stop {
Log.d(tag, "Media progress syncer was already syncing - stopped") Log.d(tag, "Media progress syncer was already syncing - stopped")
PlayerListener.lazyIsPlaying = false
Handler(Looper.getMainLooper()).post { // TODO: This was needed again which is probably a design a flaw Handler(Looper.getMainLooper()).post { // TODO: This was needed again which is probably a design a flaw
playerNotificationService.preparePlayer( playerNotificationService.preparePlayer(
playbackSession, playbackSession,
@ -202,6 +211,7 @@ class AbsAudioPlayer : Plugin() {
} }
} }
} else { } else {
playerNotificationService.mediaProgressSyncer.reset()
playerNotificationService.preparePlayer(playbackSession, playWhenReady, playbackRate) playerNotificationService.preparePlayer(playbackSession, playWhenReady, playbackRate)
} }
} }
@ -209,28 +219,25 @@ class AbsAudioPlayer : Plugin() {
} }
} else { // Play library item from server } else { // Play library item from server
val playItemRequestPayload = playerNotificationService.getPlayItemRequestPayload(false) val playItemRequestPayload = playerNotificationService.getPlayItemRequestPayload(false)
Handler(Looper.getMainLooper()).post {
apiHandler.playLibraryItem(libraryItemId, episodeId, playItemRequestPayload) { playerNotificationService.mediaProgressSyncer.stop {
if (it == null) { apiHandler.playLibraryItem(libraryItemId, episodeId, playItemRequestPayload) {
call.resolve(JSObject("{\"error\":\"Server play request failed\"}")) if (it == null) {
} else { call.resolve(JSObject("{\"error\":\"Server play request failed\"}"))
Handler(Looper.getMainLooper()).post {
Log.d(tag, "Preparing Player playback session ${jacksonMapper.writeValueAsString(it)}")
if (playerNotificationService.mediaProgressSyncer.listeningTimerRunning) { // If progress syncing then first stop before preparing next
playerNotificationService.mediaProgressSyncer.stop {
Log.d(tag, "Media progress syncer was already syncing - stopped")
Handler(Looper.getMainLooper()).post { // TODO: This was needed again which is probably a design a flaw
playerNotificationService.preparePlayer(it, playWhenReady, playbackRate)
}
}
} else { } else {
playerNotificationService.preparePlayer(it, playWhenReady, playbackRate) if (startTimeOverride != null) {
Log.d(tag, "prepareLibraryItem: Using start time override $startTimeOverride")
it.currentTime = startTimeOverride
}
Handler(Looper.getMainLooper()).post {
Log.d(tag, "Preparing Player playback session ${jacksonMapper.writeValueAsString(it)}")
PlayerListener.lazyIsPlaying = false
playerNotificationService.preparePlayer(it, playWhenReady, playbackRate)
}
call.resolve(JSObject(jacksonMapper.writeValueAsString(it)))
} }
} }
call.resolve(JSObject(jacksonMapper.writeValueAsString(it)))
} }
} }
} }

View file

@ -185,23 +185,34 @@ export default {
}, },
computed: { computed: {
menuItems() { menuItems() {
var items = [ const items = []
{ // TODO: Implement on iOS
text: 'Chapter Track', if (this.$platform !== 'ios' && !this.isPodcast && this.mediaId) {
value: 'chapter_track', items.push({
icon: this.useChapterTrack ? 'check_box' : 'check_box_outline_blank' text: 'History',
}, value: 'history'
{ })
text: this.lockUi ? 'Unlock Player' : 'Lock Player', }
value: 'lock',
icon: this.lockUi ? 'lock' : 'lock_open' items.push(
}, ...[
{ {
text: 'Close Player', text: 'Chapter Track',
value: 'close', value: 'chapter_track',
icon: 'close' icon: this.useChapterTrack ? 'check_box' : 'check_box_outline_blank'
} },
] {
text: this.lockUi ? 'Unlock Player' : 'Lock Player',
value: 'lock',
icon: this.lockUi ? 'lock' : 'lock_open'
},
{
text: 'Close Player',
value: 'close',
icon: 'close'
}
]
)
return items return items
}, },
@ -348,6 +359,16 @@ export default {
}, },
networkConnected() { networkConnected() {
return this.$store.state.networkConnected return this.$store.state.networkConnected
},
mediaId() {
if (this.isPodcast || !this.playbackSession) return null
if (this.playbackSession.libraryItemId) {
return this.playbackSession.episodeId ? `${this.playbackSession.libraryItemId}-${this.playbackSession.episodeId}` : this.playbackSession.libraryItemId
}
const localLibraryItem = this.playbackSession.localLibraryItem
if (!localLibraryItem) return null
return this.playbackSession.localEpisodeId ? `${localLibraryItem.id}-${this.playbackSession.localEpisodeId}` : localLibraryItem.id
} }
}, },
methods: { methods: {
@ -692,7 +713,10 @@ export default {
await this.$hapticsImpact() await this.$hapticsImpact()
this.showMoreMenuDialog = false this.showMoreMenuDialog = false
this.$nextTick(() => { this.$nextTick(() => {
if (action === 'lock') { if (action === 'history') {
this.$router.push(`/media/${this.mediaId}/history?title=${this.title}`)
this.showFullscreen = false
} else if (action === 'lock') {
this.lockUi = !this.lockUi this.lockUi = !this.lockUi
this.$localStore.setPlayerLock(this.lockUi) this.$localStore.setPlayerLock(this.lockUi)
} else if (action === 'chapter_track') { } else if (action === 'chapter_track') {

View file

@ -198,6 +198,9 @@ export default {
console.error('Invalid media item history', mediaItemHistory) console.error('Invalid media item history', mediaItemHistory)
return return
} }
if (mediaItemHistory.id !== this.mediaItemHistory.id) {
return
}
console.log('Media Item History updated') console.log('Media Item History updated')
this.mediaItemHistory = mediaItemHistory this.mediaItemHistory = mediaItemHistory