mirror of
https://github.com/advplyr/audiobookshelf-app.git
synced 2026-07-29 07:58:43 +02:00
Add:Android auto show currently reading
This commit is contained in:
@@ -84,6 +84,7 @@ class Audiobook {
|
||||
putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI, getCover().toString())
|
||||
putString(MediaMetadataCompat.METADATA_KEY_ART_URI, getCover().toString())
|
||||
putString(MediaMetadataCompat.METADATA_KEY_AUTHOR, book.authorFL)
|
||||
|
||||
// val extras = Bundle()
|
||||
// if (isDownloaded) {
|
||||
// extras.putLong(
|
||||
|
||||
@@ -10,6 +10,7 @@ import com.getcapacitor.JSArray
|
||||
import com.getcapacitor.JSObject
|
||||
import com.jeep.plugin.capacitor.capacitordatastoragesqlite.CapacitorDataStorageSqlite
|
||||
import okhttp3.*
|
||||
import org.json.JSONArray
|
||||
import java.io.IOException
|
||||
|
||||
class AudiobookManager {
|
||||
@@ -29,6 +30,7 @@ class AudiobookManager {
|
||||
lateinit var localMediaManager:LocalMediaManager
|
||||
|
||||
var audiobooks:MutableList<Audiobook> = mutableListOf()
|
||||
var audiobooksInProgress:MutableList<Audiobook> = mutableListOf()
|
||||
|
||||
constructor(_ctx:Context, _client:OkHttpClient) {
|
||||
ctx = _ctx
|
||||
@@ -45,19 +47,9 @@ class AudiobookManager {
|
||||
Log.d(tag, "SHARED PREF TOKEN $token")
|
||||
}
|
||||
|
||||
fun loadAudiobooks(cb: (() -> Unit)) {
|
||||
Log.d(tag, "LOAD AUDIBOOOSK $serverUrl | $token")
|
||||
if (serverUrl == "" || token == "") {
|
||||
Log.d(tag, "No Server or Token set")
|
||||
cb()
|
||||
return
|
||||
} else if (!serverUrl.startsWith("http")) {
|
||||
Log.e(tag, "Invalid server url $serverUrl")
|
||||
cb()
|
||||
return
|
||||
}
|
||||
|
||||
var url = "$serverUrl/api/libraries/main/books/all"
|
||||
fun loadCategories(cb: (() -> Unit)) {
|
||||
Log.d(tag, "LOAD Categories $serverUrl | $token")
|
||||
var url = "$serverUrl/api/libraries/main/categories"
|
||||
val request = Request.Builder()
|
||||
.url(url).addHeader("Authorization", "Bearer $token")
|
||||
.build()
|
||||
@@ -74,37 +66,108 @@ class AudiobookManager {
|
||||
if (!response.isSuccessful) throw IOException("Unexpected code $response")
|
||||
|
||||
var bodyString = response.body!!.string()
|
||||
var resJson = JSObject(bodyString)
|
||||
var results = resJson.getJSONArray("results")
|
||||
var results = JSONArray(bodyString)
|
||||
// var results = resJson.getJSONArray("results")
|
||||
|
||||
var totalBooks = results.length() - 1
|
||||
for (i in 0..totalBooks) {
|
||||
var abobj = results.get(i)
|
||||
var jsobj = JSObject(abobj.toString())
|
||||
|
||||
jsobj.put("isDownloaded", false)
|
||||
var audiobook = Audiobook(jsobj, serverUrl, token)
|
||||
|
||||
if (audiobook.isMissing || audiobook.isInvalid) {
|
||||
Log.d(tag, "Audiobook ${audiobook.book.title} is missing or invalid")
|
||||
} else if (audiobook.numTracks <= 0) {
|
||||
Log.d(tag, "Audiobook ${audiobook.book.title} has audio tracks")
|
||||
} else {
|
||||
var audiobookExists = audiobooks.find { it.id == audiobook.id }
|
||||
if (audiobookExists == null) {
|
||||
audiobooks.add(audiobook)
|
||||
} else {
|
||||
Log.d(tag, "Audiobook already there from downloaded")
|
||||
var totalShelves = results.length() - 1
|
||||
Log.d(tag, "Got categories $totalShelves")
|
||||
for (i in 0..totalShelves) {
|
||||
var shelfobj = results.get(i)
|
||||
var jsobj = JSObject(shelfobj.toString())
|
||||
var shelfId = jsobj.getString("id", "")
|
||||
Log.d(tag, "Category shelf id $shelfId")
|
||||
if (shelfId == "continue-reading") {
|
||||
var entities = jsobj.getJSONArray("entities")
|
||||
var totalEntities = entities.length() - 1
|
||||
Log.d(tag, "Shelf total entities $totalEntities")
|
||||
for (y in 0..totalEntities) {
|
||||
var abobj = entities.get(y)
|
||||
Log.d(tag, "Shelf category ab id $y = ${abobj.toString()}")
|
||||
var abjsobj = JSObject(abobj.toString())
|
||||
abjsobj.put("isDownloaded", false)
|
||||
var audiobook = Audiobook(abjsobj, serverUrl, token)
|
||||
if (audiobook.isMissing || audiobook.isInvalid || audiobook.numTracks <= 0) {
|
||||
Log.d(tag, "Not an audiobook or invalid/missing")
|
||||
} else {
|
||||
var audiobookExists = audiobooksInProgress.find { it.id == audiobook.id }
|
||||
if (audiobookExists == null) {
|
||||
audiobooksInProgress.add(audiobook)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Log.d(tag, "${audiobooks.size} Audiobooks Loaded")
|
||||
Log.d(tag, "${audiobooksInProgress.size} Audiobooks In Progress Loaded")
|
||||
cb()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun loadAudiobooks(cb: (() -> Unit)) {
|
||||
Log.d(tag, "LOAD AUDIBOOOSK $serverUrl | $token")
|
||||
if (serverUrl == "" || token == "") {
|
||||
Log.d(tag, "No Server or Token set")
|
||||
cb()
|
||||
return
|
||||
} else if (!serverUrl.startsWith("http")) {
|
||||
Log.e(tag, "Invalid server url $serverUrl")
|
||||
cb()
|
||||
return
|
||||
}
|
||||
|
||||
// First load currently reading
|
||||
loadCategories() {
|
||||
// Then load all
|
||||
var url = "$serverUrl/api/libraries/main/books/all"
|
||||
val request = Request.Builder()
|
||||
.url(url).addHeader("Authorization", "Bearer $token")
|
||||
.build()
|
||||
|
||||
client.newCall(request).enqueue(object : Callback {
|
||||
override fun onFailure(call: Call, e: IOException) {
|
||||
Log.d(tag, "FAILURE TO CONNECT")
|
||||
e.printStackTrace()
|
||||
cb()
|
||||
}
|
||||
|
||||
override fun onResponse(call: Call, response: Response) {
|
||||
response.use {
|
||||
if (!response.isSuccessful) throw IOException("Unexpected code $response")
|
||||
|
||||
var bodyString = response.body!!.string()
|
||||
var resJson = JSObject(bodyString)
|
||||
var results = resJson.getJSONArray("results")
|
||||
|
||||
var totalBooks = results.length() - 1
|
||||
for (i in 0..totalBooks) {
|
||||
var abobj = results.get(i)
|
||||
var jsobj = JSObject(abobj.toString())
|
||||
|
||||
jsobj.put("isDownloaded", false)
|
||||
var audiobook = Audiobook(jsobj, serverUrl, token)
|
||||
|
||||
if (audiobook.isMissing || audiobook.isInvalid) {
|
||||
Log.d(tag, "Audiobook ${audiobook.book.title} is missing or invalid")
|
||||
} else if (audiobook.numTracks <= 0) {
|
||||
Log.d(tag, "Audiobook ${audiobook.book.title} has audio tracks")
|
||||
} else {
|
||||
var audiobookExists = audiobooks.find { it.id == audiobook.id }
|
||||
if (audiobookExists == null) {
|
||||
audiobooks.add(audiobook)
|
||||
} else {
|
||||
Log.d(tag, "Audiobook already there from downloaded")
|
||||
}
|
||||
}
|
||||
}
|
||||
Log.d(tag, "${audiobooks.size} Audiobooks Loaded")
|
||||
cb()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fun load() {
|
||||
isLoading = true
|
||||
hasLoaded = true
|
||||
|
||||
@@ -10,6 +10,7 @@ import androidx.annotation.AnyRes
|
||||
|
||||
class BrowseTree(
|
||||
val context: Context,
|
||||
val audiobooksInProgress: List<Audiobook>,
|
||||
val audiobooks: List<Audiobook>,
|
||||
val localAudio: List<LocalMediaManager.LocalAudio>,
|
||||
val recentMediaId: String? = null
|
||||
@@ -33,6 +34,13 @@ class BrowseTree(
|
||||
init {
|
||||
val rootList = mediaIdToChildren[AUTO_BROWSE_ROOT] ?: mutableListOf()
|
||||
|
||||
|
||||
val continueReadingMetadata = MediaMetadataCompat.Builder().apply {
|
||||
putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID, CONTINUE_ROOT)
|
||||
putString(MediaMetadataCompat.METADATA_KEY_TITLE, "Reading")
|
||||
putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI, getUriToDrawable(context, R.drawable.exo_icon_localaudio).toString())
|
||||
}.build()
|
||||
|
||||
val allMetadata = MediaMetadataCompat.Builder().apply {
|
||||
putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID, ALL_ROOT)
|
||||
putString(MediaMetadataCompat.METADATA_KEY_TITLE, "Audiobooks")
|
||||
@@ -42,6 +50,7 @@ class BrowseTree(
|
||||
putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI, resource)
|
||||
}.build()
|
||||
|
||||
|
||||
val downloadsMetadata = MediaMetadataCompat.Builder().apply {
|
||||
putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID, DOWNLOADS_ROOT)
|
||||
putString(MediaMetadataCompat.METADATA_KEY_TITLE, "Downloads")
|
||||
@@ -54,11 +63,20 @@ class BrowseTree(
|
||||
putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI, getUriToDrawable(context, R.drawable.exo_icon_localaudio).toString())
|
||||
}.build()
|
||||
|
||||
if (audiobooksInProgress.isNotEmpty()) {
|
||||
rootList += continueReadingMetadata
|
||||
}
|
||||
rootList += allMetadata
|
||||
rootList += downloadsMetadata
|
||||
rootList += localsMetadata
|
||||
mediaIdToChildren[AUTO_BROWSE_ROOT] = rootList
|
||||
|
||||
audiobooksInProgress.forEach { audiobook ->
|
||||
val children = mediaIdToChildren[CONTINUE_ROOT] ?: mutableListOf()
|
||||
children += audiobook.toMediaMetadata()
|
||||
mediaIdToChildren[CONTINUE_ROOT] = children
|
||||
}
|
||||
|
||||
audiobooks.forEach { audiobook ->
|
||||
if (audiobook.isDownloaded) {
|
||||
val downloadsChildren = mediaIdToChildren[DOWNLOADS_ROOT] ?: mutableListOf()
|
||||
@@ -83,5 +101,6 @@ class BrowseTree(
|
||||
|
||||
const val AUTO_BROWSE_ROOT = "/"
|
||||
const val ALL_ROOT = "__ALL__"
|
||||
const val CONTINUE_ROOT = "__CONTINUE__"
|
||||
const val DOWNLOADS_ROOT = "__DOWNLOADS__"
|
||||
const val LOCAL_ROOT = "__LOCAL__"
|
||||
|
||||
@@ -31,6 +31,7 @@ import com.bumptech.glide.request.RequestOptions
|
||||
import com.getcapacitor.JSObject
|
||||
import com.getcapacitor.PluginCall
|
||||
import com.google.android.exoplayer2.*
|
||||
import com.google.android.exoplayer2.MediaMetadata
|
||||
import com.google.android.exoplayer2.audio.AudioAttributes
|
||||
import com.google.android.exoplayer2.ext.cast.CastPlayer
|
||||
import com.google.android.exoplayer2.ext.cast.SessionAvailabilityListener
|
||||
@@ -42,10 +43,10 @@ import com.google.android.exoplayer2.source.hls.HlsMediaSource
|
||||
import com.google.android.exoplayer2.ui.PlayerNotificationManager
|
||||
import com.google.android.exoplayer2.upstream.*
|
||||
import com.google.android.exoplayer2.util.MimeTypes
|
||||
import com.google.android.gms.cast.*
|
||||
import com.google.android.gms.cast.Cast.MessageReceivedCallback
|
||||
import com.google.android.gms.cast.CastDevice
|
||||
import com.google.android.gms.cast.CastMediaControlIntent
|
||||
import com.google.android.gms.cast.framework.*
|
||||
import com.google.android.gms.cast.framework.media.MediaQueue
|
||||
import kotlinx.coroutines.*
|
||||
import okhttp3.OkHttpClient
|
||||
import org.json.JSONObject
|
||||
@@ -69,7 +70,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
||||
fun onMetadata(metadata: JSObject)
|
||||
fun onPrepare(audiobookId: String, playWhenReady: Boolean)
|
||||
fun onSleepTimerEnded(currentPosition: Long)
|
||||
fun onSleepTimerSet(sleepTimerEndTime:Long)
|
||||
fun onSleepTimerSet(sleepTimerEndTime: Long)
|
||||
}
|
||||
|
||||
private val tag = "PlayerService"
|
||||
@@ -303,7 +304,6 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
||||
}
|
||||
|
||||
|
||||
|
||||
Log.d(tag, "Media Session Set")
|
||||
|
||||
val mediaController = MediaControllerCompat(ctx, mediaSession.sessionToken)
|
||||
@@ -916,10 +916,10 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
||||
MediaConstants.BROWSER_SERVICE_EXTRAS_KEY_SEARCH_SUPPORTED, true)
|
||||
extras.putInt(
|
||||
MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE,
|
||||
MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM)
|
||||
MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM)
|
||||
extras.putInt(
|
||||
MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE,
|
||||
MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM)
|
||||
MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM)
|
||||
|
||||
BrowserRoot(AUTO_MEDIA_ROOT, extras)
|
||||
}
|
||||
@@ -937,7 +937,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
||||
audiobookManager.isLoading = false
|
||||
|
||||
Log.d(tag, "LOADED AUDIOBOOKS")
|
||||
browseTree = BrowseTree(this, audiobookManager.audiobooks, audiobookManager.localMediaManager.localAudioFiles, null)
|
||||
browseTree = BrowseTree(this, audiobookManager.audiobooksInProgress, audiobookManager.audiobooks, audiobookManager.localMediaManager.localAudioFiles, null)
|
||||
val children = browseTree[parentMediaId]?.map { item ->
|
||||
MediaBrowserCompat.MediaItem(item.description, flag)
|
||||
}
|
||||
@@ -990,7 +990,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
||||
audiobookManager.isLoading = false
|
||||
|
||||
Log.d(tag, "LOADED AUDIOBOOKS")
|
||||
browseTree = BrowseTree(this, audiobookManager.audiobooks, audiobookManager.localMediaManager.localAudioFiles, null)
|
||||
browseTree = BrowseTree(this, audiobookManager.audiobooksInProgress, audiobookManager.audiobooks, audiobookManager.localMediaManager.localAudioFiles, null)
|
||||
val children = browseTree[ALL_ROOT]?.map { item ->
|
||||
MediaBrowserCompat.MediaItem(item.description, MediaBrowserCompat.MediaItem.FLAG_PLAYABLE)
|
||||
}
|
||||
@@ -1149,7 +1149,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
||||
if (sleepTimerRunning || sleepTimerFinishedAt > 0L) checkShouldExtendSleepTimer()
|
||||
}
|
||||
|
||||
fun increaseSleepTime(time:Long) {
|
||||
fun increaseSleepTime(time: Long) {
|
||||
Log.d(tag, "Increase Sleep time $time")
|
||||
if (!sleepTimerRunning) return
|
||||
var newSleepEndTime = sleepTimerEndTime + time
|
||||
@@ -1162,7 +1162,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
||||
listener?.onSleepTimerSet(sleepTimerEndTime)
|
||||
}
|
||||
|
||||
fun decreaseSleepTime(time:Long) {
|
||||
fun decreaseSleepTime(time: Long) {
|
||||
Log.d(tag, "Decrease Sleep time $time")
|
||||
if (!sleepTimerRunning) return
|
||||
var newSleepEndTime = sleepTimerEndTime - time
|
||||
@@ -1488,20 +1488,6 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
||||
setSessionAvailabilityListener(CastSessionAvailabilityListener())
|
||||
addListener(getPlayerListener())
|
||||
}
|
||||
|
||||
currentPlayer = castPlayer as CastPlayer
|
||||
|
||||
if (currentAudiobookStreamData != null) {
|
||||
var mimeType = MimeTypes.AUDIO_AAC
|
||||
|
||||
val mediaItem: MediaItem = MediaItem.Builder()
|
||||
.setUri(currentAudiobookStreamData!!.contentUri)
|
||||
.setMediaId(currentAudiobookStreamData!!.id).setMimeType(mimeType)
|
||||
// .setTag(metadata)
|
||||
.build()
|
||||
|
||||
castPlayer?.setMediaItem(mediaItem, currentAudiobookStreamData!!.startTime)
|
||||
}
|
||||
Log.d(tag, "CAST Cast Player Applied")
|
||||
} catch (e: Exception) {
|
||||
// We wouldn't normally catch the generic `Exception` however
|
||||
|
||||
Reference in New Issue
Block a user