Android playback session for local media items

This commit is contained in:
advplyr
2022-04-01 19:38:23 -05:00
parent 94b9dbb8b3
commit 314dc960f2
6 changed files with 72 additions and 49 deletions
@@ -85,11 +85,13 @@ class MyNativeAudio : Plugin() {
fun playLocalLibraryItem(call:PluginCall) { fun playLocalLibraryItem(call:PluginCall) {
var localMediaItemId = call.getString("localMediaItemId", "").toString() var localMediaItemId = call.getString("localMediaItemId", "").toString()
var playWhenReady = call.getBoolean("playWhenReady") == true var playWhenReady = call.getBoolean("playWhenReady") == true
Log.d(tag, "playLocalLibraryItem $playWhenReady")
DeviceManager.dbManager.loadLocalMediaItem(localMediaItemId)?.let { DeviceManager.dbManager.loadLocalMediaItem(localMediaItemId)?.let {
Handler(Looper.getMainLooper()).post() { Handler(Looper.getMainLooper()).post() {
Log.d(tag, "Preparing Local Media item ${jacksonObjectMapper().writeValueAsString(it)}") Log.d(tag, "Preparing Local Media item ${jacksonObjectMapper().writeValueAsString(it)}")
playerNotificationService.playLocalMediaItem(it, playWhenReady) var playbackSession = it.getPlaybackSession()
playerNotificationService.preparePlayer(playbackSession, playWhenReady)
} }
return call.resolve(JSObject()) return call.resolve(JSObject())
} }
@@ -603,7 +603,6 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
if (currentPlayer.playbackState == Player.STATE_READY) { if (currentPlayer.playbackState == Player.STATE_READY) {
Log.d(tag, "STATE_READY : " + mPlayer.duration.toString()) Log.d(tag, "STATE_READY : " + mPlayer.duration.toString())
// currentAudiobookStreamData!!.hasPlayerLoaded = true
if (lastPauseTime == 0L) { if (lastPauseTime == 0L) {
sendClientMetadata("ready_no_sync") sendClientMetadata("ready_no_sync")
lastPauseTime = -1; lastPauseTime = -1;
@@ -673,42 +672,35 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
mediaSession.setMetadata(metadata) mediaSession.setMetadata(metadata)
var mediaMetadata = playbackSession.getExoMediaMetadata() var mediaMetadata = playbackSession.getExoMediaMetadata()
var mediaUri = playbackSession.getContentUri()
var mimeType = playbackSession.getMimeType()
var mediaItem = MediaItem.Builder().setUri(mediaUri).setMediaMetadata(mediaMetadata).setMimeType(mimeType).build()
// var mediaUri = playbackSession.getContentUri() if (mPlayer == currentPlayer) {
// var mimeType = playbackSession.getMimeType() var mediaSource:MediaSource
// var mediaItem = MediaItem.Builder().setUri(mediaUri).setMediaMetadata(mediaMetadata).setMimeType(mimeType).build()
// var dataSourceFactory = DefaultDataSourceFactory(ctx, channelId)
// var mediaSource = ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(mediaItem)
// mPlayer.setMediaSource(mediaSource, 0L)
// if (mPlayer == currentPlayer) { if (currentPlaybackSession?.isLocal == true) {
// var mediaSource:MediaSource Log.d(tag, "Playing Local File")
// var dataSourceFactory = DefaultDataSourceFactory(ctx, channelId)
// if (currentAudiobookStreamData!!.isLocal) { mediaSource = ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(mediaItem)
// Log.d(tag, "Playing Local File") } else {
// var dataSourceFactory = DefaultDataSourceFactory(ctx, channelId) Log.d(tag, "Playing HLS File")
// mediaSource = ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(mediaItem) var dataSourceFactory = DefaultHttpDataSource.Factory()
// } else { dataSourceFactory.setUserAgent(channelId)
// Log.d(tag, "Playing HLS File") dataSourceFactory.setDefaultRequestProperties(hashMapOf("Authorization" to "Bearer ${playbackSession.token}"))
// var dataSourceFactory = DefaultHttpDataSource.Factory() mediaSource = HlsMediaSource.Factory(dataSourceFactory).createMediaSource(mediaItem)
// dataSourceFactory.setUserAgent(channelId) }
// dataSourceFactory.setDefaultRequestProperties(hashMapOf("Authorization" to "Bearer ${currentAudiobookStreamData!!.token}")) Log.d(tag, "Playback Session CURRENT TIME ${playbackSession.currentTime} | ${playbackSession.currentTimeMs}")
// mediaSource = HlsMediaSource.Factory(dataSourceFactory).createMediaSource(mediaItem) mPlayer.setMediaSource(mediaSource, playbackSession.currentTimeMs)
// } } else if (castPlayer != null) {
// mPlayer.setMediaSource(mediaSource, currentAudiobookStreamData!!.startTime)
// } else if (castPlayer != null) {
//// var mediaQueue = currentAudiobookStreamData!!.getCastQueue() //// var mediaQueue = currentAudiobookStreamData!!.getCastQueue()
// // TODO: Start position will need to be adjusted if using multi-track queue // // TODO: Start position will need to be adjusted if using multi-track queue
//// castPlayer?.setMediaItems(mediaQueue, 0, 0) //// castPlayer?.setMediaItems(mediaQueue, 0, 0)
// } }
currentPlayer.prepare()
currentPlayer.playWhenReady = playWhenReady currentPlayer.playWhenReady = playWhenReady
currentPlayer.setPlaybackSpeed(1f) // TODO: Playback speed should come from settings currentPlayer.setPlaybackSpeed(1f) // TODO: Playback speed should come from settings
} currentPlayer.prepare()
fun playLocalMediaItem(localMediaItem: LocalMediaItem, playWhenReady:Boolean) {
Log.d(tag, "playLocalMediaItem ${localMediaItem}")
} }
fun initPlayer(audiobookStreamData: AudiobookStreamData) { fun initPlayer(audiobookStreamData: AudiobookStreamData) {
@@ -727,10 +719,10 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
var deviceMan = Build.MANUFACTURER var deviceMan = Build.MANUFACTURER
var deviceModel = Build.MODEL var deviceModel = Build.MODEL
Log.d(tag, "Checking device $deviceName | Model $deviceModel | Manufacturer $deviceMan") Log.d(tag, "Checking device $deviceName | Model $deviceModel | Manufacturer $deviceMan")
if (deviceMan.toLowerCase().contains("oneplus") || deviceName.toLowerCase().contains("oneplus")) { if (deviceMan.lowercase(Locale.getDefault()).contains("oneplus") || deviceName.lowercase(Locale.getDefault()).contains("oneplus")) {
Log.d(tag, "Detected OnePlus device - removing local cover") Log.d(tag, "Detected OnePlus device - removing local cover")
currentAudiobookStreamData?.clearCover() currentAudiobookStreamData?.clearCover()
} else if (deviceName.toLowerCase().contains("xperia") || deviceModel.toLowerCase().contains("xperia")) { } else if (deviceName.lowercase(Locale.getDefault()).contains("xperia") || deviceModel.lowercase(Locale.getDefault()).contains("xperia")) {
Log.d(tag, "Detected Sony Xperia device - removing local cover") Log.d(tag, "Detected Sony Xperia device - removing local cover")
currentAudiobookStreamData?.clearCover() currentAudiobookStreamData?.clearCover()
} }
@@ -83,7 +83,7 @@ data class BookMetadata(
data class PodcastMetadata( data class PodcastMetadata(
var title:String, var title:String,
var author:String?, var author:String?,
var feedUrl:String, var feedUrl:String?,
var genres:MutableList<String> var genres:MutableList<String>
) : MediaTypeMetadata() ) : MediaTypeMetadata()
@@ -1,8 +1,8 @@
package com.audiobookshelf.app.data package com.audiobookshelf.app.data
import android.net.Uri import com.fasterxml.jackson.annotation.JsonIgnore
import com.fasterxml.jackson.annotation.JsonIgnoreProperties import com.fasterxml.jackson.annotation.JsonIgnoreProperties
import com.getcapacitor.JSObject import java.util.*
data class ServerConfig( data class ServerConfig(
var id:String, var id:String,
@@ -30,7 +30,32 @@ data class LocalMediaItem(
var audioTracks:MutableList<AudioTrack>, var audioTracks:MutableList<AudioTrack>,
var localFiles:MutableList<LocalFile>, var localFiles:MutableList<LocalFile>,
var coverPath:String? var coverPath:String?
) ) {
@JsonIgnore
fun getDuration():Double {
var total = 0.0
audioTracks.forEach{ total += it.duration }
return total
}
@JsonIgnore
fun getMediaMetadata():MediaTypeMetadata {
return if (mediaType == "book") {
BookMetadata(name,null, mutableListOf(), mutableListOf(), mutableListOf(),null,null,null,null,null,null,null,false,null,null,null,null)
} else {
PodcastMetadata(name,null,null, mutableListOf())
}
}
@JsonIgnore
fun getPlaybackSession():PlaybackSession {
var sessionId = "play-${UUID.randomUUID()}"
var mediaMetadata = getMediaMetadata()
return PlaybackSession(sessionId,null,null,null,null,mediaType,mediaMetadata,null,getDuration(),PLAYMETHOD_LOCAL,audioTracks,0.0,null,this,null,null)
}
}
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
data class LocalFile( data class LocalFile(
@@ -7,6 +7,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore
import com.fasterxml.jackson.annotation.JsonIgnoreProperties import com.fasterxml.jackson.annotation.JsonIgnoreProperties
import com.google.android.exoplayer2.MediaMetadata import com.google.android.exoplayer2.MediaMetadata
// TODO: enum or something in kotlin?
val PLAYMETHOD_DIRECTPLAY = 0 val PLAYMETHOD_DIRECTPLAY = 0
val PLAYMETHOD_DIRECTSTREAM = 1 val PLAYMETHOD_DIRECTSTREAM = 1
val PLAYMETHOD_TRANSCODE = 2 val PLAYMETHOD_TRANSCODE = 2
@@ -15,10 +16,10 @@ val PLAYMETHOD_LOCAL = 3
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
class PlaybackSession( class PlaybackSession(
var id:String, var id:String,
var userId:String, var userId:String?,
var libraryItemId:String, var libraryItemId:String?,
var episodeId:String, var episodeId:String?,
var mediaEntityId:String, var mediaEntityId:String?,
var mediaType:String, var mediaType:String,
var mediaMetadata:MediaTypeMetadata, var mediaMetadata:MediaTypeMetadata,
var coverPath:String?, var coverPath:String?,
@@ -26,15 +27,15 @@ class PlaybackSession(
var playMethod:Int, var playMethod:Int,
var audioTracks:MutableList<AudioTrack>, var audioTracks:MutableList<AudioTrack>,
var currentTime:Double, var currentTime:Double,
var libraryItem:LibraryItem, var libraryItem:LibraryItem?,
var serverUrl:String, var localMediaItem:LocalMediaItem?,
var token:String var serverUrl:String?,
var token:String?
) { ) {
@JsonIgnore val isHLS get() = playMethod == PLAYMETHOD_TRANSCODE
fun getIsHls():Boolean { val isLocal get() = playMethod == PLAYMETHOD_LOCAL
return playMethod == PLAYMETHOD_TRANSCODE val currentTimeMs get() = (currentTime * 1000L).toLong()
}
@JsonIgnore @JsonIgnore
fun getTitle():String { fun getTitle():String {
@@ -47,11 +48,13 @@ class PlaybackSession(
fun getAuthor():String { fun getAuthor():String {
if (mediaMetadata == null) return "Unset" if (mediaMetadata == null) return "Unset"
var metadata = mediaMetadata as BookMetadata var metadata = mediaMetadata as BookMetadata
return metadata.authors.joinToString(",") { it.name } return metadata.authorName ?: "Unset"
} }
@JsonIgnore @JsonIgnore
fun getCoverUri(): Uri { fun getCoverUri(): Uri {
if (localMediaItem?.coverPath != null) return Uri.parse(localMediaItem?.coverPath) ?: Uri.parse("android.resource://com.audiobookshelf.app/" + R.drawable.icon)
if (coverPath == null) return Uri.parse("android.resource://com.audiobookshelf.app/" + R.drawable.icon) if (coverPath == null) return Uri.parse("android.resource://com.audiobookshelf.app/" + R.drawable.icon)
return Uri.parse("$serverUrl/api/items/$libraryItemId/cover?token=$token") return Uri.parse("$serverUrl/api/items/$libraryItemId/cover?token=$token")
} }
@@ -59,6 +62,7 @@ class PlaybackSession(
@JsonIgnore @JsonIgnore
fun getContentUri(): Uri { fun getContentUri(): Uri {
var audioTrack = audioTracks[0] var audioTrack = audioTracks[0]
if (isLocal) return Uri.parse(audioTrack.contentUrl) // Local content url
return Uri.parse("$serverUrl${audioTrack.contentUrl}?token=$token") return Uri.parse("$serverUrl${audioTrack.contentUrl}?token=$token")
} }
@@ -153,7 +153,7 @@ class FolderScanner(var ctx: Context) {
// First image file use as cover path // First image file use as cover path
if (coverPath == null) { if (coverPath == null) {
coverPath = localFile.absolutePath coverPath = localFile.contentUrl
} }
} }
} }