diff --git a/android/app/src/main/java/com/audiobookshelf/app/MyNativeAudio.kt b/android/app/src/main/java/com/audiobookshelf/app/MyNativeAudio.kt index 533526cf..de655757 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/MyNativeAudio.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/MyNativeAudio.kt @@ -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()) } diff --git a/android/app/src/main/java/com/audiobookshelf/app/PlayerNotificationService.kt b/android/app/src/main/java/com/audiobookshelf/app/PlayerNotificationService.kt index cd95a341..6787c4fb 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/PlayerNotificationService.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/PlayerNotificationService.kt @@ -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() } diff --git a/android/app/src/main/java/com/audiobookshelf/app/data/DataClasses.kt b/android/app/src/main/java/com/audiobookshelf/app/data/DataClasses.kt index d871c107..1e30846a 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/data/DataClasses.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/data/DataClasses.kt @@ -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 ) : MediaTypeMetadata() diff --git a/android/app/src/main/java/com/audiobookshelf/app/data/DeviceClasses.kt b/android/app/src/main/java/com/audiobookshelf/app/data/DeviceClasses.kt index 54259c75..7c61fecd 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/data/DeviceClasses.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/data/DeviceClasses.kt @@ -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, var localFiles:MutableList, 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( diff --git a/android/app/src/main/java/com/audiobookshelf/app/data/PlaybackSession.kt b/android/app/src/main/java/com/audiobookshelf/app/data/PlaybackSession.kt index 0390f3f7..777e1487 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/data/PlaybackSession.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/data/PlaybackSession.kt @@ -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, 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") } diff --git a/android/app/src/main/java/com/audiobookshelf/app/device/FolderScanner.kt b/android/app/src/main/java/com/audiobookshelf/app/device/FolderScanner.kt index 5aea2ff3..0ec78863 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/device/FolderScanner.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/device/FolderScanner.kt @@ -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 } } }