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) {
var localMediaItemId = call.getString("localMediaItemId", "").toString()
var playWhenReady = call.getBoolean("playWhenReady") == true
Log.d(tag, "playLocalLibraryItem $playWhenReady")
DeviceManager.dbManager.loadLocalMediaItem(localMediaItemId)?.let {
Handler(Looper.getMainLooper()).post() {
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())
}
@@ -603,7 +603,6 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
if (currentPlayer.playbackState == Player.STATE_READY) {
Log.d(tag, "STATE_READY : " + mPlayer.duration.toString())
// currentAudiobookStreamData!!.hasPlayerLoaded = true
if (lastPauseTime == 0L) {
sendClientMetadata("ready_no_sync")
lastPauseTime = -1;
@@ -673,42 +672,35 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
mediaSession.setMetadata(metadata)
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()
// var mimeType = playbackSession.getMimeType()
// 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) {
var mediaSource:MediaSource
// if (mPlayer == currentPlayer) {
// var mediaSource:MediaSource
//
// if (currentAudiobookStreamData!!.isLocal) {
// Log.d(tag, "Playing Local File")
// var dataSourceFactory = DefaultDataSourceFactory(ctx, channelId)
// mediaSource = ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(mediaItem)
// } else {
// Log.d(tag, "Playing HLS File")
// var dataSourceFactory = DefaultHttpDataSource.Factory()
// dataSourceFactory.setUserAgent(channelId)
// dataSourceFactory.setDefaultRequestProperties(hashMapOf("Authorization" to "Bearer ${currentAudiobookStreamData!!.token}"))
// mediaSource = HlsMediaSource.Factory(dataSourceFactory).createMediaSource(mediaItem)
// }
// mPlayer.setMediaSource(mediaSource, currentAudiobookStreamData!!.startTime)
// } else if (castPlayer != null) {
if (currentPlaybackSession?.isLocal == true) {
Log.d(tag, "Playing Local File")
var dataSourceFactory = DefaultDataSourceFactory(ctx, channelId)
mediaSource = ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(mediaItem)
} else {
Log.d(tag, "Playing HLS File")
var dataSourceFactory = DefaultHttpDataSource.Factory()
dataSourceFactory.setUserAgent(channelId)
dataSourceFactory.setDefaultRequestProperties(hashMapOf("Authorization" to "Bearer ${playbackSession.token}"))
mediaSource = HlsMediaSource.Factory(dataSourceFactory).createMediaSource(mediaItem)
}
Log.d(tag, "Playback Session CURRENT TIME ${playbackSession.currentTime} | ${playbackSession.currentTimeMs}")
mPlayer.setMediaSource(mediaSource, playbackSession.currentTimeMs)
} else if (castPlayer != null) {
//// var mediaQueue = currentAudiobookStreamData!!.getCastQueue()
// // TODO: Start position will need to be adjusted if using multi-track queue
//// castPlayer?.setMediaItems(mediaQueue, 0, 0)
// }
}
currentPlayer.prepare()
currentPlayer.playWhenReady = playWhenReady
currentPlayer.setPlaybackSpeed(1f) // TODO: Playback speed should come from settings
}
fun playLocalMediaItem(localMediaItem: LocalMediaItem, playWhenReady:Boolean) {
Log.d(tag, "playLocalMediaItem ${localMediaItem}")
currentPlayer.prepare()
}
fun initPlayer(audiobookStreamData: AudiobookStreamData) {
@@ -727,10 +719,10 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
var deviceMan = Build.MANUFACTURER
var deviceModel = Build.MODEL
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")
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")
currentAudiobookStreamData?.clearCover()
}
@@ -83,7 +83,7 @@ data class BookMetadata(
data class PodcastMetadata(
var title:String,
var author:String?,
var feedUrl:String,
var feedUrl:String?,
var genres:MutableList<String>
) : MediaTypeMetadata()
@@ -1,8 +1,8 @@
package com.audiobookshelf.app.data
import android.net.Uri
import com.fasterxml.jackson.annotation.JsonIgnore
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
import com.getcapacitor.JSObject
import java.util.*
data class ServerConfig(
var id:String,
@@ -30,7 +30,32 @@ data class LocalMediaItem(
var audioTracks:MutableList<AudioTrack>,
var localFiles:MutableList<LocalFile>,
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)
data class LocalFile(
@@ -7,6 +7,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
import com.google.android.exoplayer2.MediaMetadata
// TODO: enum or something in kotlin?
val PLAYMETHOD_DIRECTPLAY = 0
val PLAYMETHOD_DIRECTSTREAM = 1
val PLAYMETHOD_TRANSCODE = 2
@@ -15,10 +16,10 @@ val PLAYMETHOD_LOCAL = 3
@JsonIgnoreProperties(ignoreUnknown = true)
class PlaybackSession(
var id:String,
var userId:String,
var libraryItemId:String,
var episodeId:String,
var mediaEntityId:String,
var userId:String?,
var libraryItemId:String?,
var episodeId:String?,
var mediaEntityId:String?,
var mediaType:String,
var mediaMetadata:MediaTypeMetadata,
var coverPath:String?,
@@ -26,15 +27,15 @@ class PlaybackSession(
var playMethod:Int,
var audioTracks:MutableList<AudioTrack>,
var currentTime:Double,
var libraryItem:LibraryItem,
var serverUrl:String,
var token:String
var libraryItem:LibraryItem?,
var localMediaItem:LocalMediaItem?,
var serverUrl:String?,
var token:String?
) {
@JsonIgnore
fun getIsHls():Boolean {
return playMethod == PLAYMETHOD_TRANSCODE
}
val isHLS get() = playMethod == PLAYMETHOD_TRANSCODE
val isLocal get() = playMethod == PLAYMETHOD_LOCAL
val currentTimeMs get() = (currentTime * 1000L).toLong()
@JsonIgnore
fun getTitle():String {
@@ -47,11 +48,13 @@ class PlaybackSession(
fun getAuthor():String {
if (mediaMetadata == null) return "Unset"
var metadata = mediaMetadata as BookMetadata
return metadata.authors.joinToString(",") { it.name }
return metadata.authorName ?: "Unset"
}
@JsonIgnore
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)
return Uri.parse("$serverUrl/api/items/$libraryItemId/cover?token=$token")
}
@@ -59,6 +62,7 @@ class PlaybackSession(
@JsonIgnore
fun getContentUri(): Uri {
var audioTrack = audioTracks[0]
if (isLocal) return Uri.parse(audioTrack.contentUrl) // Local content url
return Uri.parse("$serverUrl${audioTrack.contentUrl}?token=$token")
}
@@ -153,7 +153,7 @@ class FolderScanner(var ctx: Context) {
// First image file use as cover path
if (coverPath == null) {
coverPath = localFile.absolutePath
coverPath = localFile.contentUrl
}
}
}