mirror of
https://github.com/advplyr/audiobookshelf-app.git
synced 2025-08-29 06:18:51 +02:00
Android cleaning up chromecast player and more failed attempts
This commit is contained in:
parent
e5c8d5d4d4
commit
ef65b4c278
7 changed files with 112 additions and 51 deletions
|
@ -11,6 +11,7 @@ import com.google.android.gms.cast.framework.media.CastMediaOptions
|
||||||
class CastOptionsProvider : OptionsProvider {
|
class CastOptionsProvider : OptionsProvider {
|
||||||
override fun getCastOptions(context: Context): CastOptions {
|
override fun getCastOptions(context: Context): CastOptions {
|
||||||
Log.d("CastOptionsProvider", "getCastOptions")
|
Log.d("CastOptionsProvider", "getCastOptions")
|
||||||
|
var appId = "FD1F76C5"
|
||||||
return CastOptions.Builder()
|
return CastOptions.Builder()
|
||||||
.setReceiverApplicationId(CastMediaControlIntent.DEFAULT_MEDIA_RECEIVER_APPLICATION_ID).setCastMediaOptions(
|
.setReceiverApplicationId(CastMediaControlIntent.DEFAULT_MEDIA_RECEIVER_APPLICATION_ID).setCastMediaOptions(
|
||||||
CastMediaOptions.Builder()
|
CastMediaOptions.Builder()
|
||||||
|
|
|
@ -9,6 +9,9 @@ import com.fasterxml.jackson.annotation.JsonIgnore
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
||||||
import com.google.android.exoplayer2.MediaItem
|
import com.google.android.exoplayer2.MediaItem
|
||||||
import com.google.android.exoplayer2.MediaMetadata
|
import com.google.android.exoplayer2.MediaMetadata
|
||||||
|
import com.google.android.gms.cast.MediaInfo
|
||||||
|
import com.google.android.gms.cast.MediaQueueItem
|
||||||
|
import com.google.android.gms.common.images.WebImage
|
||||||
|
|
||||||
// TODO: enum or something in kotlin?
|
// TODO: enum or something in kotlin?
|
||||||
val PLAYMETHOD_DIRECTPLAY = 0
|
val PLAYMETHOD_DIRECTPLAY = 0
|
||||||
|
@ -135,12 +138,41 @@ class PlaybackSession(
|
||||||
var mediaMetadata = this.getExoMediaMetadata(audioTrack)
|
var mediaMetadata = this.getExoMediaMetadata(audioTrack)
|
||||||
var mediaUri = this.getContentUri(audioTrack)
|
var mediaUri = this.getContentUri(audioTrack)
|
||||||
var mimeType = audioTrack.mimeType
|
var mimeType = audioTrack.mimeType
|
||||||
var mediaItem = MediaItem.Builder().setUri(mediaUri).setMediaMetadata(mediaMetadata).setMimeType(mimeType).build()
|
|
||||||
|
var queueItem = getQueueItem(audioTrack) // Queue item used in exo player CastManager
|
||||||
|
var mediaItem = MediaItem.Builder().setUri(mediaUri).setTag(queueItem).setMediaMetadata(mediaMetadata).setMimeType(mimeType).build()
|
||||||
mediaItems.add(mediaItem)
|
mediaItems.add(mediaItem)
|
||||||
}
|
}
|
||||||
return mediaItems
|
return mediaItems
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
fun getCastMediaMetadata(audioTrack:AudioTrack):com.google.android.gms.cast.MediaMetadata {
|
||||||
|
var castMetadata = com.google.android.gms.cast.MediaMetadata(com.google.android.gms.cast.MediaMetadata.MEDIA_TYPE_AUDIOBOOK_CHAPTER)
|
||||||
|
castMetadata.addImage(WebImage(getCoverUri()))
|
||||||
|
castMetadata.putString(com.google.android.gms.cast.MediaMetadata.KEY_TITLE, displayTitle)
|
||||||
|
castMetadata.putString(com.google.android.gms.cast.MediaMetadata.KEY_ARTIST, displayAuthor)
|
||||||
|
castMetadata.putInt(com.google.android.gms.cast.MediaMetadata.KEY_TRACK_NUMBER, audioTrack.index)
|
||||||
|
return castMetadata
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
fun getQueueItem(audioTrack:AudioTrack):MediaQueueItem {
|
||||||
|
var castMetadata = getCastMediaMetadata(audioTrack)
|
||||||
|
|
||||||
|
var mediaUri = getContentUri(audioTrack)
|
||||||
|
var mediaInfoBuilder = MediaInfo.Builder(mediaUri.toString())
|
||||||
|
mediaInfoBuilder.setContentUrl(mediaUri.toString())
|
||||||
|
mediaInfoBuilder.setMetadata(castMetadata)
|
||||||
|
mediaInfoBuilder.setContentType(audioTrack.mimeType)
|
||||||
|
var mediaInfo = mediaInfoBuilder.build()
|
||||||
|
|
||||||
|
var queueItem = MediaQueueItem.Builder(mediaInfo)
|
||||||
|
queueItem.setItemId(audioTrack.index)
|
||||||
|
queueItem.setPlaybackDuration(audioTrack.duration)
|
||||||
|
return queueItem.build()
|
||||||
|
}
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
fun clone():PlaybackSession {
|
fun clone():PlaybackSession {
|
||||||
return PlaybackSession(id,userId,libraryItemId,episodeId,mediaType,mediaMetadata,chapters,displayTitle,displayAuthor,coverPath,duration,playMethod,startedAt,updatedAt,timeListening,audioTracks,currentTime,libraryItem,localLibraryItem,serverConnectionConfigId,serverAddress)
|
return PlaybackSession(id,userId,libraryItemId,episodeId,mediaType,mediaMetadata,chapters,displayTitle,displayAuthor,coverPath,duration,playMethod,startedAt,updatedAt,timeListening,audioTracks,currentTime,libraryItem,localLibraryItem,serverConnectionConfigId,serverAddress)
|
||||||
|
|
|
@ -9,17 +9,19 @@ import androidx.mediarouter.app.MediaRouteChooserDialog
|
||||||
import androidx.mediarouter.media.MediaRouteSelector
|
import androidx.mediarouter.media.MediaRouteSelector
|
||||||
import androidx.mediarouter.media.MediaRouter
|
import androidx.mediarouter.media.MediaRouter
|
||||||
import com.getcapacitor.PluginCall
|
import com.getcapacitor.PluginCall
|
||||||
|
import com.google.android.exoplayer2.MediaItem
|
||||||
import com.google.android.exoplayer2.ext.cast.CastPlayer
|
import com.google.android.exoplayer2.ext.cast.CastPlayer
|
||||||
|
import com.google.android.exoplayer2.ext.cast.MediaItemConverter
|
||||||
import com.google.android.exoplayer2.ext.cast.SessionAvailabilityListener
|
import com.google.android.exoplayer2.ext.cast.SessionAvailabilityListener
|
||||||
import com.google.android.gms.cast.Cast
|
import com.google.android.gms.cast.Cast
|
||||||
import com.google.android.gms.cast.CastDevice
|
import com.google.android.gms.cast.CastDevice
|
||||||
import com.google.android.gms.cast.CastMediaControlIntent
|
import com.google.android.gms.cast.CastMediaControlIntent
|
||||||
|
import com.google.android.gms.cast.MediaQueueItem
|
||||||
import com.google.android.gms.cast.framework.*
|
import com.google.android.gms.cast.framework.*
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
import java.util.ArrayList
|
|
||||||
|
|
||||||
class CastManager constructor(playerNotificationService:PlayerNotificationService) {
|
class CastManager constructor(playerNotificationService:PlayerNotificationService) {
|
||||||
private val tag = "SleepTimerManager"
|
private val tag = "CastManager"
|
||||||
private val playerNotificationService:PlayerNotificationService = playerNotificationService
|
private val playerNotificationService:PlayerNotificationService = playerNotificationService
|
||||||
|
|
||||||
private var newConnectionListener: SessionListener? = null
|
private var newConnectionListener: SessionListener? = null
|
||||||
|
@ -291,6 +293,22 @@ class CastManager constructor(playerNotificationService:PlayerNotificationServic
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inner class CustomConverter : MediaItemConverter {
|
||||||
|
override fun toMediaQueueItem(mediaItem: MediaItem): MediaQueueItem {
|
||||||
|
// The MediaQueueItem you build is expected to be in the tag.
|
||||||
|
var queueItem = (mediaItem.playbackProperties!!.tag as MediaQueueItem?)!!
|
||||||
|
Log.d(tag, "Test toMediaQueueItem ${queueItem.media!!.contentUrl} | ${queueItem.playbackDuration} | ${queueItem.itemId}")
|
||||||
|
return queueItem
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toMediaItem(mediaQueueItem: MediaQueueItem): MediaItem {
|
||||||
|
return MediaItem.Builder()
|
||||||
|
.setUri(mediaQueueItem.media!!.contentUrl)
|
||||||
|
.setTag(mediaQueueItem)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun listenForConnection(callback: ConnectionCallback) {
|
private fun listenForConnection(callback: ConnectionCallback) {
|
||||||
// We should only ever have one of these listeners active at a time, so remove previous
|
// We should only ever have one of these listeners active at a time, so remove previous
|
||||||
getSessionManager()?.removeSessionManagerListener(newConnectionListener, CastSession::class.java)
|
getSessionManager()?.removeSessionManagerListener(newConnectionListener, CastSession::class.java)
|
||||||
|
@ -302,7 +320,8 @@ class CastManager constructor(playerNotificationService:PlayerNotificationServic
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val castContext = CastContext.getSharedInstance(mainActivity)
|
val castContext = CastContext.getSharedInstance(mainActivity)
|
||||||
playerNotificationService.castPlayer = CastPlayer(castContext).apply {
|
|
||||||
|
playerNotificationService.castPlayer = CastPlayer(castContext, CustomConverter()).apply {
|
||||||
setSessionAvailabilityListener(CastSessionAvailabilityListener())
|
setSessionAvailabilityListener(CastSessionAvailabilityListener())
|
||||||
addListener(PlayerListener(playerNotificationService))
|
addListener(PlayerListener(playerNotificationService))
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,7 @@ import com.google.android.exoplayer2.ext.mediasession.TimelineQueueNavigator
|
||||||
import com.google.android.exoplayer2.source.MediaSource
|
import com.google.android.exoplayer2.source.MediaSource
|
||||||
import com.google.android.exoplayer2.source.ProgressiveMediaSource
|
import com.google.android.exoplayer2.source.ProgressiveMediaSource
|
||||||
import com.google.android.exoplayer2.source.hls.HlsMediaSource
|
import com.google.android.exoplayer2.source.hls.HlsMediaSource
|
||||||
|
import com.google.android.exoplayer2.ui.PlayerControlView
|
||||||
import com.google.android.exoplayer2.ui.PlayerNotificationManager
|
import com.google.android.exoplayer2.ui.PlayerNotificationManager
|
||||||
import com.google.android.exoplayer2.upstream.*
|
import com.google.android.exoplayer2.upstream.*
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
|
@ -184,8 +185,6 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
||||||
|
|
||||||
currentPlayer = mPlayer
|
currentPlayer = mPlayer
|
||||||
|
|
||||||
var client: OkHttpClient = OkHttpClient()
|
|
||||||
|
|
||||||
// Initialize API
|
// Initialize API
|
||||||
apiHandler = ApiHandler(ctx)
|
apiHandler = ApiHandler(ctx)
|
||||||
|
|
||||||
|
@ -220,9 +219,6 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
||||||
isActive = true
|
isActive = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Log.d(tag, "Media Session Set")
|
|
||||||
|
|
||||||
val mediaController = MediaControllerCompat(ctx, mediaSession.sessionToken)
|
val mediaController = MediaControllerCompat(ctx, mediaSession.sessionToken)
|
||||||
|
|
||||||
// This is for Media Browser
|
// This is for Media Browser
|
||||||
|
@ -293,10 +289,9 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
||||||
|
|
||||||
var metadata = playbackSession.getMediaMetadataCompat()
|
var metadata = playbackSession.getMediaMetadataCompat()
|
||||||
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) {
|
||||||
|
@ -316,24 +311,25 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
||||||
}
|
}
|
||||||
mPlayer.setMediaSource(mediaSource)
|
mPlayer.setMediaSource(mediaSource)
|
||||||
|
|
||||||
|
} else if (castPlayer != null) {
|
||||||
|
castPlayer?.addMediaItem(mediaItems[0]) // TODO: Media items never actually get added, not sure what is going on....
|
||||||
|
Log.d(tag, "Cast Player ADDED MEDIA ITEM ${castPlayer?.currentMediaItem} | ${castPlayer?.duration} | ${castPlayer?.mediaItemCount}")
|
||||||
|
}
|
||||||
|
|
||||||
// Add remaining media items if multi-track
|
// Add remaining media items if multi-track
|
||||||
if (mediaItems.size > 1) {
|
if (mediaItems.size > 1) {
|
||||||
mPlayer.addMediaItems(mediaItems.subList(1, mediaItems.size))
|
currentPlayer.addMediaItems(mediaItems.subList(1, mediaItems.size))
|
||||||
Log.d(tag, "mPlayer total media items ${mPlayer.mediaItemCount}")
|
Log.d(tag, "currentPlayer total media items ${currentPlayer.mediaItemCount}")
|
||||||
|
|
||||||
var currentTrackIndex = playbackSession.getCurrentTrackIndex()
|
var currentTrackIndex = playbackSession.getCurrentTrackIndex()
|
||||||
var currentTrackTime = playbackSession.getCurrentTrackTimeMs()
|
var currentTrackTime = playbackSession.getCurrentTrackTimeMs()
|
||||||
Log.d(tag, "mPlayer current track index $currentTrackIndex & current track time $currentTrackTime")
|
Log.d(tag, "currentPlayer current track index $currentTrackIndex & current track time $currentTrackTime")
|
||||||
mPlayer.seekTo(currentTrackIndex, currentTrackTime)
|
currentPlayer.seekTo(currentTrackIndex, currentTrackTime)
|
||||||
} else {
|
} else {
|
||||||
mPlayer.seekTo(playbackSession.currentTimeMs)
|
currentPlayer.seekTo(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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Log.d(tag, "Prepare complete for session ${currentPlaybackSession?.displayTitle} | ${currentPlayer.mediaItemCount}")
|
||||||
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()
|
currentPlayer.prepare()
|
||||||
|
@ -349,9 +345,9 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
||||||
mediaSessionConnector.setPlayer(mPlayer)
|
mediaSessionConnector.setPlayer(mPlayer)
|
||||||
mPlayer
|
mPlayer
|
||||||
}
|
}
|
||||||
if (currentPlaybackSession != null) {
|
currentPlaybackSession?.let {
|
||||||
Log.d(tag, "switchToPlayer: Initing current ab stream data")
|
Log.d(tag, "switchToPlayer: Preparing current playback session ${it.displayTitle}")
|
||||||
preparePlayer(currentPlaybackSession!!, false)
|
preparePlayer(it, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -404,6 +400,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
||||||
if (currentPlayer == castPlayer) {
|
if (currentPlayer == castPlayer) {
|
||||||
Log.d(tag, "CAST Player set on play ${currentPlayer.isLoading} || ${currentPlayer.duration} | ${currentPlayer.currentPosition}")
|
Log.d(tag, "CAST Player set on play ${currentPlayer.isLoading} || ${currentPlayer.duration} | ${currentPlayer.currentPosition}")
|
||||||
}
|
}
|
||||||
|
|
||||||
currentPlayer.play()
|
currentPlayer.play()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -411,6 +408,16 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
||||||
currentPlayer.pause()
|
currentPlayer.pause()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun playPause():Boolean {
|
||||||
|
return if (currentPlayer.isPlaying) {
|
||||||
|
pause()
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
play()
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun seekPlayer(time: Long) {
|
fun seekPlayer(time: Long) {
|
||||||
if (currentPlayer.mediaItemCount > 1) {
|
if (currentPlayer.mediaItemCount > 1) {
|
||||||
currentPlaybackSession?.currentTime = time / 1000.0
|
currentPlaybackSession?.currentTime = time / 1000.0
|
||||||
|
|
|
@ -147,6 +147,14 @@ class AbsAudioPlayer : Plugin() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PluginMethod
|
||||||
|
fun playPause(call: PluginCall) {
|
||||||
|
Handler(Looper.getMainLooper()).post() {
|
||||||
|
var playing = playerNotificationService.playPause()
|
||||||
|
call.resolve(JSObject("{\"playing\":$playing}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@PluginMethod
|
@PluginMethod
|
||||||
fun seekPlayer(call: PluginCall) {
|
fun seekPlayer(call: PluginCall) {
|
||||||
var time:Long = call.getString("timeMs", "0")!!.toLong()
|
var time:Long = call.getString("timeMs", "0")!!.toLong()
|
||||||
|
@ -246,7 +254,7 @@ class AbsAudioPlayer : Plugin() {
|
||||||
@PluginMethod
|
@PluginMethod
|
||||||
fun requestSession(call: PluginCall) {
|
fun requestSession(call: PluginCall) {
|
||||||
Log.d(tag, "CAST REQUEST SESSION PLUGIN")
|
Log.d(tag, "CAST REQUEST SESSION PLUGIN")
|
||||||
|
call.resolve()
|
||||||
playerNotificationService.castManager.requestSession(mainActivity, object : CastManager.RequestSessionCallback() {
|
playerNotificationService.castManager.requestSession(mainActivity, object : CastManager.RequestSessionCallback() {
|
||||||
override fun onError(errorCode: Int) {
|
override fun onError(errorCode: Int) {
|
||||||
Log.e(tag, "CAST REQUEST SESSION CALLBACK ERROR $errorCode")
|
Log.e(tag, "CAST REQUEST SESSION CALLBACK ERROR $errorCode")
|
||||||
|
|
|
@ -109,7 +109,7 @@ export default {
|
||||||
playbackSession: null,
|
playbackSession: null,
|
||||||
// Others
|
// Others
|
||||||
showChapterModal: false,
|
showChapterModal: false,
|
||||||
showCastBtn: false,
|
showCastBtn: true,
|
||||||
showFullscreen: false,
|
showFullscreen: false,
|
||||||
totalDuration: 0,
|
totalDuration: 0,
|
||||||
currentPlaybackRate: 1,
|
currentPlaybackRate: 1,
|
||||||
|
@ -455,15 +455,9 @@ export default {
|
||||||
}
|
}
|
||||||
this.seek(time)
|
this.seek(time)
|
||||||
},
|
},
|
||||||
playPauseClick() {
|
async playPauseClick() {
|
||||||
if (this.isLoading) return
|
if (this.isLoading) return
|
||||||
if (this.isPaused) {
|
this.isPlaying = !!((await AbsAudioPlayer.playPause()) || {}).playing
|
||||||
console.log('playPause PLAY')
|
|
||||||
this.play()
|
|
||||||
} else {
|
|
||||||
console.log('playPause PAUSE')
|
|
||||||
this.pause()
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
play() {
|
play() {
|
||||||
AbsAudioPlayer.playPlayer()
|
AbsAudioPlayer.playPlayer()
|
||||||
|
|
|
@ -120,7 +120,7 @@ export default {
|
||||||
return this.media.coverPath || this.placeholderUrl
|
return this.media.coverPath || this.placeholderUrl
|
||||||
},
|
},
|
||||||
hasCover() {
|
hasCover() {
|
||||||
return !!this.media.coverPath
|
return !!this.media.coverPath || this.localCover
|
||||||
},
|
},
|
||||||
sizeMultiplier() {
|
sizeMultiplier() {
|
||||||
var baseSize = this.squareAspectRatio ? 192 : 120
|
var baseSize = this.squareAspectRatio ? 192 : 120
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue