mirror of
https://github.com/advplyr/audiobookshelf-app.git
synced 2026-08-31 07:37:22 +02:00
Add android graceful fallback to transcode & onPlaybackFailed event
This commit is contained in:
@@ -48,6 +48,8 @@ class PlaybackSession(
|
|||||||
@get:JsonIgnore
|
@get:JsonIgnore
|
||||||
val isHLS get() = playMethod == PLAYMETHOD_TRANSCODE
|
val isHLS get() = playMethod == PLAYMETHOD_TRANSCODE
|
||||||
@get:JsonIgnore
|
@get:JsonIgnore
|
||||||
|
val isDirectPlay get() = playMethod == PLAYMETHOD_DIRECTPLAY
|
||||||
|
@get:JsonIgnore
|
||||||
val isLocal get() = playMethod == PLAYMETHOD_LOCAL
|
val isLocal get() = playMethod == PLAYMETHOD_LOCAL
|
||||||
@get:JsonIgnore
|
@get:JsonIgnore
|
||||||
val currentTimeMs get() = (currentTime * 1000L).toLong()
|
val currentTimeMs get() = (currentTime * 1000L).toLong()
|
||||||
|
|||||||
@@ -15,8 +15,9 @@ class PlayerListener(var playerNotificationService:PlayerNotificationService) :
|
|||||||
private var onSeekBack: Boolean = false
|
private var onSeekBack: Boolean = false
|
||||||
|
|
||||||
override fun onPlayerError(error: PlaybackException) {
|
override fun onPlayerError(error: PlaybackException) {
|
||||||
error.message?.let { Log.e(tag, it) }
|
var errorMessage = error.message ?: "Unknown Error"
|
||||||
error.localizedMessage?.let { Log.e(tag, it) }
|
Log.e(tag, "onPlayerError $errorMessage")
|
||||||
|
playerNotificationService.handlePlayerPlaybackError(errorMessage) // If was direct playing session, fallback to transcode
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onEvents(player: Player, events: Player.Events) {
|
override fun onEvents(player: Player, events: Player.Events) {
|
||||||
|
|||||||
+21
-1
@@ -55,6 +55,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
|||||||
fun onSleepTimerEnded(currentPosition: Long)
|
fun onSleepTimerEnded(currentPosition: Long)
|
||||||
fun onSleepTimerSet(sleepTimeRemaining: Int)
|
fun onSleepTimerSet(sleepTimeRemaining: Int)
|
||||||
fun onLocalMediaProgressUpdate(localMediaProgress: LocalMediaProgress)
|
fun onLocalMediaProgressUpdate(localMediaProgress: LocalMediaProgress)
|
||||||
|
fun onPlaybackFailed(errorMessage:String)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val tag = "PlayerService"
|
private val tag = "PlayerService"
|
||||||
@@ -289,7 +290,6 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
|||||||
mediaSession.setMetadata(metadata)
|
mediaSession.setMetadata(metadata)
|
||||||
var mediaItems = playbackSession.getMediaItems()
|
var mediaItems = playbackSession.getMediaItems()
|
||||||
if (mPlayer == currentPlayer) {
|
if (mPlayer == currentPlayer) {
|
||||||
|
|
||||||
var mediaSource:MediaSource
|
var mediaSource:MediaSource
|
||||||
|
|
||||||
if (playbackSession.isLocal) {
|
if (playbackSession.isLocal) {
|
||||||
@@ -333,6 +333,26 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
|||||||
currentPlayer.prepare()
|
currentPlayer.prepare()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun handlePlayerPlaybackError(errorMessage:String) {
|
||||||
|
// On error and was attempting to direct play - fallback to transcode
|
||||||
|
currentPlaybackSession?.let { playbackSession ->
|
||||||
|
if (playbackSession.isDirectPlay) {
|
||||||
|
Log.d(tag, "Fallback to transcode")
|
||||||
|
|
||||||
|
var libraryItemId = playbackSession.libraryItemId ?: "" // Must be true since direct play
|
||||||
|
var episodeId = playbackSession.episodeId
|
||||||
|
apiHandler.playLibraryItem(libraryItemId, episodeId, true) {
|
||||||
|
Handler(Looper.getMainLooper()).post() {
|
||||||
|
preparePlayer(it, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
clientEventEmitter?.onPlaybackFailed(errorMessage)
|
||||||
|
closePlayback()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun switchToPlayer(useCastPlayer: Boolean) {
|
fun switchToPlayer(useCastPlayer: Boolean) {
|
||||||
currentPlayer = if (useCastPlayer) {
|
currentPlayer = if (useCastPlayer) {
|
||||||
Log.d(tag, "switchToPlayer: Using Cast Player " + castPlayer?.deviceInfo)
|
Log.d(tag, "switchToPlayer: Using Cast Player " + castPlayer?.deviceInfo)
|
||||||
|
|||||||
@@ -68,6 +68,10 @@ class AbsAudioPlayer : Plugin() {
|
|||||||
override fun onLocalMediaProgressUpdate(localMediaProgress: LocalMediaProgress) {
|
override fun onLocalMediaProgressUpdate(localMediaProgress: LocalMediaProgress) {
|
||||||
notifyListeners("onLocalMediaProgressUpdate", JSObject(jacksonMapper.writeValueAsString(localMediaProgress)))
|
notifyListeners("onLocalMediaProgressUpdate", JSObject(jacksonMapper.writeValueAsString(localMediaProgress)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onPlaybackFailed(errorMessage: String) {
|
||||||
|
emit("onPlaybackFailed", errorMessage)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
mainActivity.pluginCallback = foregroundServiceReady
|
mainActivity.pluginCallback = foregroundServiceReady
|
||||||
|
|||||||
@@ -151,12 +151,11 @@ class ApiHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun playLibraryItem(libraryItemId:String, episodeId:String, forceTranscode:Boolean, cb: (PlaybackSession) -> Unit) {
|
fun playLibraryItem(libraryItemId:String, episodeId:String?, forceTranscode:Boolean, cb: (PlaybackSession) -> Unit) {
|
||||||
var payload = JSObject()
|
var payload = JSObject()
|
||||||
payload.put("mediaPlayer", "exo-player")
|
payload.put("mediaPlayer", "exo-player")
|
||||||
|
|
||||||
// Only if direct play fails do we force transcode
|
// Only if direct play fails do we force transcode
|
||||||
// TODO: Fallback to transcode
|
|
||||||
if (!forceTranscode) payload.put("forceDirectPlay", true)
|
if (!forceTranscode) payload.put("forceDirectPlay", true)
|
||||||
else payload.put("forceTranscode", true)
|
else payload.put("forceTranscode", true)
|
||||||
|
|
||||||
|
|||||||
@@ -535,6 +535,12 @@ export default {
|
|||||||
this.$refs.dropdownMenu.closeMenu()
|
this.$refs.dropdownMenu.closeMenu()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
closePlayback() {
|
||||||
|
this.$store.commit('setPlayerItem', null)
|
||||||
|
this.showFullscreen = false
|
||||||
|
this.isEnded = false
|
||||||
|
this.playbackSession = null
|
||||||
|
},
|
||||||
//
|
//
|
||||||
// Listeners from audio AbsAudioPlayer
|
// Listeners from audio AbsAudioPlayer
|
||||||
//
|
//
|
||||||
@@ -586,16 +592,20 @@ export default {
|
|||||||
},
|
},
|
||||||
onPlaybackClosed() {
|
onPlaybackClosed() {
|
||||||
console.log('Received onPlaybackClosed evt')
|
console.log('Received onPlaybackClosed evt')
|
||||||
this.$store.commit('setPlayerItem', null)
|
this.closePlayback()
|
||||||
this.showFullscreen = false
|
},
|
||||||
this.isEnded = false
|
onPlaybackFailed(data) {
|
||||||
this.playbackSession = null
|
console.log('Received onPlaybackFailed evt')
|
||||||
|
var errorMessage = data.value || 'Unknown Error'
|
||||||
|
this.$toast.error(`Playback Failed: ${errorMessage}`)
|
||||||
|
this.closePlayback()
|
||||||
},
|
},
|
||||||
async init() {
|
async init() {
|
||||||
this.useChapterTrack = await this.$localStore.getUseChapterTrack()
|
this.useChapterTrack = await this.$localStore.getUseChapterTrack()
|
||||||
|
|
||||||
this.onPlaybackSessionListener = AbsAudioPlayer.addListener('onPlaybackSession', this.onPlaybackSession)
|
this.onPlaybackSessionListener = AbsAudioPlayer.addListener('onPlaybackSession', this.onPlaybackSession)
|
||||||
this.onPlaybackClosedListener = AbsAudioPlayer.addListener('onPlaybackClosed', this.onPlaybackClosed)
|
this.onPlaybackClosedListener = AbsAudioPlayer.addListener('onPlaybackClosed', this.onPlaybackClosed)
|
||||||
|
this.onPlaybackFailedListener = AbsAudioPlayer.addListener('onPlaybackFailed', this.onPlaybackFailed)
|
||||||
this.onPlayingUpdateListener = AbsAudioPlayer.addListener('onPlayingUpdate', this.onPlayingUpdate)
|
this.onPlayingUpdateListener = AbsAudioPlayer.addListener('onPlayingUpdate', this.onPlayingUpdate)
|
||||||
this.onMetadataListener = AbsAudioPlayer.addListener('onMetadata', this.onMetadata)
|
this.onMetadataListener = AbsAudioPlayer.addListener('onMetadata', this.onMetadata)
|
||||||
}
|
}
|
||||||
@@ -615,6 +625,7 @@ export default {
|
|||||||
if (this.onMetadataListener) this.onMetadataListener.remove()
|
if (this.onMetadataListener) this.onMetadataListener.remove()
|
||||||
if (this.onPlaybackSessionListener) this.onPlaybackSessionListener.remove()
|
if (this.onPlaybackSessionListener) this.onPlaybackSessionListener.remove()
|
||||||
if (this.onPlaybackClosedListener) this.onPlaybackClosedListener.remove()
|
if (this.onPlaybackClosedListener) this.onPlaybackClosedListener.remove()
|
||||||
|
if (this.onPlaybackFailedListener) this.onPlaybackFailedListener.remove()
|
||||||
clearInterval(this.playInterval)
|
clearInterval(this.playInterval)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,11 +23,11 @@
|
|||||||
|
|
||||||
<div v-if="!hasCover" class="absolute top-0 left-0 right-0 bottom-0 w-full h-full flex items-center justify-center z-10" :style="{ padding: placeholderCoverPadding + 'rem' }">
|
<div v-if="!hasCover" class="absolute top-0 left-0 right-0 bottom-0 w-full h-full flex items-center justify-center z-10" :style="{ padding: placeholderCoverPadding + 'rem' }">
|
||||||
<div>
|
<div>
|
||||||
<p class="text-center font-book" style="color: rgb(247 223 187)" :style="{ fontSize: titleFontSize + 'rem' }">{{ titleCleaned }}</p>
|
<p class="text-center font-book truncate leading-none origin-center" style="color: rgb(247 223 187); font-size: 0.8rem" :style="{ transform: `scale(${sizeMultiplier})` }">{{ titleCleaned }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="!hasCover" class="absolute left-0 right-0 w-full flex items-center justify-center z-10" :style="{ padding: placeholderCoverPadding + 'rem', bottom: authorBottom + 'rem' }">
|
<div v-if="!hasCover" class="absolute left-0 right-0 w-full flex items-center justify-center z-10" :style="{ padding: placeholderCoverPadding + 'rem', bottom: authorBottom + 'rem' }">
|
||||||
<p class="text-center font-book" style="color: rgb(247 223 187); opacity: 0.75" :style="{ fontSize: authorFontSize + 'rem' }">{{ authorCleaned }}</p>
|
<p class="text-center font-book truncate leading-none origin-center" style="color: rgb(247 223 187); opacity: 0.75; font-size: 0.6rem" :style="{ transform: `scale(${sizeMultiplier})` }">{{ authorCleaned }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -123,7 +123,7 @@ export default {
|
|||||||
return !!this.media.coverPath || this.localCover || this.downloadCover
|
return !!this.media.coverPath || this.localCover || this.downloadCover
|
||||||
},
|
},
|
||||||
sizeMultiplier() {
|
sizeMultiplier() {
|
||||||
var baseSize = this.squareAspectRatio ? 192 : 120
|
var baseSize = this.squareAspectRatio ? 128 : 96
|
||||||
return this.width / baseSize
|
return this.width / baseSize
|
||||||
},
|
},
|
||||||
titleFontSize() {
|
titleFontSize() {
|
||||||
@@ -133,7 +133,8 @@ export default {
|
|||||||
return 0.6 * this.sizeMultiplier
|
return 0.6 * this.sizeMultiplier
|
||||||
},
|
},
|
||||||
placeholderCoverPadding() {
|
placeholderCoverPadding() {
|
||||||
return 0.8 * this.sizeMultiplier
|
if (this.sizeMultiplier < 0.5) return 0
|
||||||
|
return this.sizeMultiplier
|
||||||
},
|
},
|
||||||
authorBottom() {
|
authorBottom() {
|
||||||
return 0.75 * this.sizeMultiplier
|
return 0.75 * this.sizeMultiplier
|
||||||
|
|||||||
Reference in New Issue
Block a user