Change: laying the groundwork for android auto

This commit is contained in:
advplyr 2021-11-10 20:39:25 -06:00
parent 44f535020d
commit 52e3ea0a99
11 changed files with 423 additions and 175 deletions

View file

@ -39,16 +39,18 @@ repositories {
dependencies {
implementation "com.anggrayudi:storage:0.13.0"
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
implementation project(':capacitor-android')
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
implementation project(':capacitor-android')
implementation 'androidx.coordinatorlayout:coordinatorlayout:1.1.0'
implementation 'com.squareup.okhttp3:okhttp:4.9.2'
testImplementation "junit:junit:$junitVersion"
androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
implementation project(':capacitor-cordova-android-plugins')
androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
implementation project(':capacitor-cordova-android-plugins')
implementation "androidx.core:core-ktx:1.6.0"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"

View file

@ -38,9 +38,9 @@
android:resource="@drawable/icon" />
<!-- Android auto rejected the update, removing this so it can be published on the store -->
<!-- <meta-data-->
<!-- android:name="com.google.android.gms.car.application"-->
<!-- android:resource="@xml/automotive_app_desc"/>-->
<meta-data
android:name="com.google.android.gms.car.application"
android:resource="@xml/automotive_app_desc"/>
<!-- TODO: Can remove in future -->
<!-- <provider-->

View file

@ -4,66 +4,42 @@ import android.net.Uri
import com.getcapacitor.JSObject
class Audiobook {
var id:String = "audiobook"
var token:String = ""
var playlistUrl:String = ""
var title:String = "No Title"
var author:String = "Unknown"
var series:String = ""
var cover:String = ""
var playWhenReady:Boolean = false
var startTime:Long = 0
var playbackSpeed:Float = 1f
var duration:Long = 0
var id:String
var ino:String
var libraryId:String
var folderId:String
var book:Book
var duration:Float
var size:Long
var numTracks:Int
var isMissing:Boolean
var isInvalid:Boolean
var path:String
var isLocal:Boolean = false
var contentUrl:String = ""
var fallbackCover:Uri
var fallbackUri:Uri
var hasPlayerLoaded:Boolean = false
constructor(jsobj: JSObject) {
id = jsobj.getString("id", "").toString()
ino = jsobj.getString("ino", "").toString()
libraryId = jsobj.getString("libraryId", "").toString()
folderId = jsobj.getString("folderId", "").toString()
var playlistUri:Uri = Uri.EMPTY
var coverUri:Uri = Uri.EMPTY
var contentUri:Uri = Uri.EMPTY // For Local only
var bookJsObj = jsobj.getJSObject("book")
book = bookJsObj?.let { Book(it) }!!
constructor(jsondata:JSObject) {
id = jsondata.getString("id", "audiobook").toString()
title = jsondata.getString("title", "No Title").toString()
token = jsondata.getString("token", "").toString()
author = jsondata.getString("author", "Unknown").toString()
series = jsondata.getString("series", "").toString()
cover = jsondata.getString("cover", "").toString()
playlistUrl = jsondata.getString("playlistUrl", "").toString()
playWhenReady = jsondata.getBoolean("playWhenReady", false) == true
duration = jsobj.getDouble("duration").toFloat()
size = jsobj.getLong("size")
numTracks = jsobj.getInteger("numTracks")!!
isMissing = jsobj.getBoolean("isMissing")
isInvalid = jsobj.getBoolean("isInvalid")
path = jsobj.getString("path", "").toString()
if (jsondata.has("startTime")) {
startTime = jsondata.getString("startTime", "0")!!.toLong()
}
fallbackUri = Uri.parse("http://fallback.com/run.mp3")
fallbackCover = Uri.parse("android.resource://com.audiobookshelf.app/" + R.drawable.icon)
}
if (jsondata.has("duration")) {
duration = jsondata.getString("duration", "0")!!.toLong()
}
if (jsondata.has("playbackSpeed")) {
playbackSpeed = jsondata.getDouble("playbackSpeed")!!.toFloat()
}
// Local data
isLocal = jsondata.getBoolean("isLocal", false) == true
contentUrl = jsondata.getString("contentUrl", "").toString()
if (playlistUrl != "") {
playlistUri = Uri.parse(playlistUrl)
}
if (cover != "") {
coverUri = Uri.parse(cover)
} else {
coverUri = Uri.parse("android.resource://com.audiobookshelf.app/" + R.drawable.icon)
cover = coverUri.toString()
}
if (contentUrl != "") {
contentUri = Uri.parse(contentUrl)
}
fun getCover(serverUrl:String, token:String):Uri {
return Uri.parse("$serverUrl/${book.cover}?token=$token")
}
}

View file

@ -0,0 +1,105 @@
package com.audiobookshelf.app
import android.app.Activity
import android.content.Context
import android.os.Handler
import android.os.Looper
import android.support.v4.media.MediaBrowserCompat
import android.support.v4.media.MediaDescriptionCompat
import android.util.Log
import androidx.media.MediaBrowserServiceCompat
import com.getcapacitor.JSArray
import com.getcapacitor.JSObject
import com.jeep.plugin.capacitor.capacitordatastoragesqlite.CapacitorDataStorageSqlite
import okhttp3.*
import java.io.IOException
import java.net.URL
class AudiobookManager {
var tag = "AudiobookManager"
var hasLoaded = false
var ctx: Context
var serverUrl = ""
var token = ""
private var client:OkHttpClient
var audiobooks:MutableList<Audiobook> = mutableListOf()
constructor(_ctx:Context, _client:OkHttpClient) {
ctx = _ctx
client = _client
}
fun init() {
var sharedPreferences = ctx.getSharedPreferences("CapacitorStorage", Activity.MODE_PRIVATE)
serverUrl = sharedPreferences.getString("serverUrl", null).toString()
Log.d(tag, "SHARED PREF SERVERURL $serverUrl")
token = sharedPreferences.getString("token", null).toString()
Log.d(tag, "SHARED PREF TOKEN $token")
}
fun fetchAudiobooks(result: MediaBrowserServiceCompat.Result<MutableList<MediaBrowserCompat.MediaItem>>) {
var url = "$serverUrl/api/library/main/audiobooks"
Log.d(tag, "RUNNING SAMPLER $url")
val request = Request.Builder()
.url(url).addHeader("Authorization", "Bearer $token")
.build()
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
e.printStackTrace()
}
override fun onResponse(call: Call, response: Response) {
response.use {
if (!response.isSuccessful) throw IOException("Unexpected code $response")
// for ((name, value) in response.headers) {
// Log.d(tag, "HEADER $name: $value")
// }
var bodyString = response.body!!.string()
var json = JSArray(bodyString)
var totalBooks = json.length() - 1
for (i in 0..totalBooks) {
var abobj = json.get(i)
var jsobj = JSObject(abobj.toString())
var audiobook = Audiobook(jsobj)
audiobooks.add(audiobook)
Log.d(tag, "Audiobook: ${audiobook.toString()}")
}
Log.d(tag, "Audiobooks Loaded")
val mediaItems: MutableList<MediaBrowserCompat.MediaItem> = mutableListOf()
audiobooks.forEach {
var builder = MediaDescriptionCompat.Builder()
.setMediaId(it.id)
.setTitle(it.book.title)
.setSubtitle(it.book.authorFL)
.setMediaUri(it.fallbackUri)
.setIconUri(it.fallbackCover)
var mediaDescription = builder.build()
var newMediaItem = MediaBrowserCompat.MediaItem(mediaDescription, MediaBrowserCompat.MediaItem.FLAG_PLAYABLE)
mediaItems.add(newMediaItem)
}
Log.d(tag, "AudiobookManager: Sending ${mediaItems.size} Aduiobooks")
result.sendResult(mediaItems)
}
}
})
}
fun load() {
hasLoaded = true
var db = CapacitorDataStorageSqlite(ctx)
db.openStore("storage", "downloads", false, "no-encryption", 1)
Log.d(tag, "CHECK IF DB IS OPEN ${db.isStoreOpen("storage")}")
var keyvalues = db.keysvalues()
Log.d(tag, "KEY VALUES $keyvalues")
keyvalues.forEach { Log.d(tag, "keyvalue ${it.getString("key")} | ${it.getString("value")}") }
}
}

View file

@ -0,0 +1,69 @@
package com.audiobookshelf.app
import android.net.Uri
import com.getcapacitor.JSObject
class AudiobookStreamData {
var id:String = "audiobook"
var token:String = ""
var playlistUrl:String = ""
var title:String = "No Title"
var author:String = "Unknown"
var series:String = ""
var cover:String = ""
var playWhenReady:Boolean = false
var startTime:Long = 0
var playbackSpeed:Float = 1f
var duration:Long = 0
var isLocal:Boolean = false
var contentUrl:String = ""
var hasPlayerLoaded:Boolean = false
var playlistUri:Uri = Uri.EMPTY
var coverUri:Uri = Uri.EMPTY
var contentUri:Uri = Uri.EMPTY // For Local only
constructor(jsondata:JSObject) {
id = jsondata.getString("id", "audiobook").toString()
title = jsondata.getString("title", "No Title").toString()
token = jsondata.getString("token", "").toString()
author = jsondata.getString("author", "Unknown").toString()
series = jsondata.getString("series", "").toString()
cover = jsondata.getString("cover", "").toString()
playlistUrl = jsondata.getString("playlistUrl", "").toString()
playWhenReady = jsondata.getBoolean("playWhenReady", false) == true
if (jsondata.has("startTime")) {
startTime = jsondata.getString("startTime", "0")!!.toLong()
}
if (jsondata.has("duration")) {
duration = jsondata.getString("duration", "0")!!.toLong()
}
if (jsondata.has("playbackSpeed")) {
playbackSpeed = jsondata.getDouble("playbackSpeed")!!.toFloat()
}
// Local data
isLocal = jsondata.getBoolean("isLocal", false) == true
contentUrl = jsondata.getString("contentUrl", "").toString()
if (playlistUrl != "") {
playlistUri = Uri.parse(playlistUrl)
}
if (cover != "") {
coverUri = Uri.parse(cover)
} else {
coverUri = Uri.parse("android.resource://com.audiobookshelf.app/" + R.drawable.icon)
cover = coverUri.toString()
}
if (contentUrl != "") {
contentUri = Uri.parse(contentUrl)
}
}
}

View file

@ -0,0 +1,39 @@
package com.audiobookshelf.app
import com.getcapacitor.JSObject
class Book {
var title:String
var subtitle:String
var author:String
var authorFL:String
var narrator:String
var series:String
var volumeNumber:String
var publisher:String
var description:String
var publishYear:String
var language:String
var cover:String
var coverFullPath:String
var genres:String
var lastUpdate:Long
constructor(jsobj: JSObject) {
title = jsobj.getString("title", "").toString()
subtitle = jsobj.getString("subtitle", "").toString()
author = jsobj.getString("author", "").toString()
authorFL = jsobj.getString("authorFL", "").toString()
narrator = jsobj.getString("narrator", "").toString()
series = jsobj.getString("series", "").toString()
volumeNumber = jsobj.getString("volumeNumber", "").toString()
publisher = jsobj.getString("publisher", "").toString()
description = jsobj.getString("description", "").toString()
publishYear = jsobj.getString("publishYear", "").toString()
language = jsobj.getString("language", "").toString()
cover = jsobj.getString("cover", "").toString()
coverFullPath = jsobj.getString("coverFullPath", "").toString()
genres = jsobj.getString("genres", "").toString()
lastUpdate = jsobj.getLong("lastUpdate")
}
}

View file

@ -1,5 +1,6 @@
package com.audiobookshelf.app
import android.app.Activity
import android.app.DownloadManager
import android.content.*
import android.os.*
@ -7,6 +8,11 @@ import android.util.Log
import com.anggrayudi.storage.SimpleStorage
import com.anggrayudi.storage.SimpleStorageHelper
import com.getcapacitor.BridgeActivity
import com.getcapacitor.JSObject
import com.jeep.plugin.capacitor.capacitordatastoragesqlite.CapacitorDataStorageSqlite
import okhttp3.OkHttpClient
import okhttp3.Request
import java.net.URL
class MainActivity : BridgeActivity() {
@ -40,6 +46,13 @@ class MainActivity : BridgeActivity() {
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// REMOVE FOR TESTING
Log.d(tag, "STARTING UP APP")
// var client: OkHttpClient = OkHttpClient()
// var abManager = AudiobookManager(this, client)
// abManager.init()
// abManager.fetchAudiobooks()
Log.d(tag, "onCreate")
registerPlugin(MyNativeAudio::class.java)
registerPlugin(AudioDownloader::class.java)

View file

@ -8,8 +8,6 @@ import androidx.core.content.ContextCompat
import com.getcapacitor.*
import com.getcapacitor.annotation.CapacitorPlugin
import org.json.JSONObject
import java.util.*
import kotlin.concurrent.schedule
@CapacitorPlugin(name = "MyNativeAudio")
class MyNativeAudio : Plugin() {
@ -64,8 +62,8 @@ class MyNativeAudio : Plugin() {
}
var jsobj = JSObject()
var audiobook:Audiobook = Audiobook(call.data)
if (audiobook.playlistUrl == "" && audiobook.contentUrl == "") {
var audiobookStreamData:AudiobookStreamData = AudiobookStreamData(call.data)
if (audiobookStreamData.playlistUrl == "" && audiobookStreamData.contentUrl == "") {
Log.e(tag, "Invalid URL for init audio player")
jsobj.put("success", false)
@ -73,7 +71,7 @@ class MyNativeAudio : Plugin() {
}
Handler(Looper.getMainLooper()).post() {
playerNotificationService.initPlayer(audiobook)
playerNotificationService.initPlayer(audiobookStreamData)
jsobj.put("success", true)
call.resolve(jsobj)
}
@ -174,38 +172,38 @@ class MyNativeAudio : Plugin() {
}
}
@PluginMethod
fun setAudiobooks(call: PluginCall) {
var audiobooks = call.getArray("audiobooks", JSArray())
if (audiobooks == null) {
Log.w(tag, "setAudiobooks IS NULL")
call.resolve()
return
}
var audiobookObjs = mutableListOf<Audiobook>()
var len = audiobooks.length()
(0 until len).forEach { _it ->
var jsonobj = audiobooks.get(_it) as JSONObject
var _names = Array(jsonobj.names().length()) {
jsonobj.names().getString(it)
}
var jsobj = JSObject(jsonobj, _names)
if (jsobj.has("duration")) {
var dur = jsobj.getDouble("duration")
var duration = Math.floor(dur * 1000L).toLong()
jsobj.put("duration", duration)
}
var audiobook = Audiobook(jsobj)
audiobookObjs.add(audiobook)
}
Log.d(tag, "Setting Audiobooks ${audiobookObjs.size}")
playerNotificationService.setAudiobooks(audiobookObjs)
}
// @PluginMethod
// fun setAudiobooks(call: PluginCall) {
// var audiobooks = call.getArray("audiobooks", JSArray())
// if (audiobooks == null) {
// Log.w(tag, "setAudiobooks IS NULL")
// call.resolve()
// return
// }
//
// var audiobookObjs = mutableListOf<AudiobookStreamData>()
//
// var len = audiobooks.length()
// (0 until len).forEach { _it ->
// var jsonobj = audiobooks.get(_it) as JSONObject
//
// var _names = Array(jsonobj.names().length()) {
// jsonobj.names().getString(it)
// }
// var jsobj = JSObject(jsonobj, _names)
//
// if (jsobj.has("duration")) {
// var dur = jsobj.getDouble("duration")
// var duration = Math.floor(dur * 1000L).toLong()
// jsobj.put("duration", duration)
// }
//
// var audiobook = AudiobookStreamData(jsobj)
// audiobookObjs.add(audiobook)
// }
// Log.d(tag, "Setting Audiobooks ${audiobookObjs.size}")
// playerNotificationService.setAudiobooks(audiobookObjs)
// }
@PluginMethod
fun setSleepTimer(call: PluginCall) {

View file

@ -33,10 +33,10 @@ import com.google.android.exoplayer2.ui.PlayerNotificationManager
import com.google.android.exoplayer2.upstream.*
import kotlinx.coroutines.*
import android.view.KeyEvent
import java.io.File
import java.util.*
import kotlin.concurrent.schedule
import android.annotation.SuppressLint
import okhttp3.OkHttpClient
const val NOTIFICATION_LARGE_ICON_SIZE = 144 // px
@ -74,9 +74,9 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
private var channelId = "audiobookshelf_channel"
private var channelName = "Audiobookshelf Channel"
private var currentAudiobook:Audiobook? = null
private var currentAudiobookStreamData:AudiobookStreamData? = null
private var audiobooks = mutableListOf<Audiobook>()
// private var audiobooks = mutableListOf<AudiobookStreamData>()
private var mediaButtonClickCount: Int = 0
var mediaButtonClickTimeout: Long = 1000 //ms
@ -88,6 +88,8 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
private var sleepTimerTask:TimerTask? = null
private var sleepChapterTime:Long = 0L
private lateinit var audiobookManager:AudiobookManager
fun setCustomObjectListener(mylistener: MyCustomObjectListener) {
listener = mylistener
}
@ -160,6 +162,9 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
override fun onCreate() {
super.onCreate()
ctx = this
var client: OkHttpClient = OkHttpClient()
audiobookManager = AudiobookManager(ctx, client)
audiobookManager.init()
channelId = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createNotificationChannel(channelId, channelName)
@ -252,11 +257,11 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
val queueNavigator: TimelineQueueNavigator = object : TimelineQueueNavigator(mediaSession) {
override fun getMediaDescription(player: Player, windowIndex: Int): MediaDescriptionCompat {
var builder = MediaDescriptionCompat.Builder()
.setMediaId(currentAudiobook!!.id)
.setTitle(currentAudiobook!!.title)
.setSubtitle(currentAudiobook!!.author)
.setMediaUri(currentAudiobook!!.playlistUri)
.setIconUri(currentAudiobook!!.coverUri)
.setMediaId(currentAudiobookStreamData!!.id)
.setTitle(currentAudiobookStreamData!!.title)
.setSubtitle(currentAudiobookStreamData!!.author)
.setMediaUri(currentAudiobookStreamData!!.playlistUri)
.setIconUri(currentAudiobookStreamData!!.coverUri)
return builder.build()
}
}
@ -277,7 +282,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
override fun onPrepare(playWhenReady: Boolean) {
Log.d(tag, "ON PREPARE $playWhenReady")
var audiobook = audiobooks[0]
var audiobook = audiobookManager.audiobooks[0]
if (audiobook == null) {
Log.e(tag, "Audiobook NOT FOUND")
return
@ -287,7 +292,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
override fun onPrepareFromMediaId(mediaId: String, playWhenReady: Boolean, extras: Bundle?) {
Log.d(tag, "ON PREPARE FROM MEDIA ID $mediaId $playWhenReady")
var audiobook = audiobooks.find { it.id == mediaId }
var audiobook = audiobookManager.audiobooks.find { it.id == mediaId }
if (audiobook == null) {
Log.e(tag, "Audiobook NOT FOUND")
return
@ -391,7 +396,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
mPlayer.seekTo(currentAudiobook!!.startTime)
}*/
currentAudiobook!!.hasPlayerLoaded = true
currentAudiobookStreamData!!.hasPlayerLoaded = true
if (lastPauseTime == 0L) {
sendClientMetadata("ready_no_sync")
lastPauseTime = -1;
@ -448,27 +453,27 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
*/
// fun initPlayer(token: String, playlistUri: String, playWhenReady: Boolean, currentTime: Long, title: String, artist: String, albumArt: String) {
fun initPlayer(audiobook: Audiobook) {
currentAudiobook = audiobook
fun initPlayer(audiobookStreamData: AudiobookStreamData) {
currentAudiobookStreamData = audiobookStreamData
Log.d(tag, "Init Player Audiobook ${currentAudiobook!!.playlistUrl} | ${currentAudiobook!!.title} | ${currentAudiobook!!.author}")
Log.d(tag, "Init Player Audiobook ${currentAudiobookStreamData!!.playlistUrl} | ${currentAudiobookStreamData!!.title} | ${currentAudiobookStreamData!!.author}")
if (mPlayer.isPlaying) {
Log.d(tag, "Init Player audiobook already playing")
}
var metadataBuilder = MediaMetadataCompat.Builder()
.putString(MediaMetadataCompat.METADATA_KEY_TITLE, currentAudiobook!!.title)
.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, currentAudiobook!!.title)
.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, currentAudiobook!!.author)
.putString(MediaMetadataCompat.METADATA_KEY_AUTHOR, currentAudiobook!!.author)
.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, currentAudiobook!!.author)
.putString(MediaMetadataCompat.METADATA_KEY_ALBUM, currentAudiobook!!.series)
.putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID, currentAudiobook!!.id)
.putString(MediaMetadataCompat.METADATA_KEY_TITLE, currentAudiobookStreamData!!.title)
.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, currentAudiobookStreamData!!.title)
.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, currentAudiobookStreamData!!.author)
.putString(MediaMetadataCompat.METADATA_KEY_AUTHOR, currentAudiobookStreamData!!.author)
.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, currentAudiobookStreamData!!.author)
.putString(MediaMetadataCompat.METADATA_KEY_ALBUM, currentAudiobookStreamData!!.series)
.putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID, currentAudiobookStreamData!!.id)
if (currentAudiobook!!.cover != "") {
metadataBuilder.putString(MediaMetadataCompat.METADATA_KEY_ART_URI, currentAudiobook!!.cover)
metadataBuilder.putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI, currentAudiobook!!.cover)
if (currentAudiobookStreamData!!.cover != "") {
metadataBuilder.putString(MediaMetadataCompat.METADATA_KEY_ART_URI, currentAudiobookStreamData!!.cover)
metadataBuilder.putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI, currentAudiobookStreamData!!.cover)
}
var metadata = metadataBuilder.build()
@ -478,27 +483,27 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
var mediaSource:MediaSource
if (currentAudiobook!!.isLocal) {
if (currentAudiobookStreamData!!.isLocal) {
Log.d(tag, "Playing Local File")
var mediaItem = MediaItem.Builder().setUri(currentAudiobook!!.contentUri).setMediaMetadata(mediaMetadata).build()
var mediaItem = MediaItem.Builder().setUri(currentAudiobookStreamData!!.contentUri).setMediaMetadata(mediaMetadata).build()
var dataSourceFactory = DefaultDataSourceFactory(ctx, channelId)
mediaSource = ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(mediaItem)
} else {
Log.d(tag, "Playing HLS File")
var mediaItem = MediaItem.Builder().setUri(currentAudiobook!!.playlistUri).setMediaMetadata(mediaMetadata).build()
var mediaItem = MediaItem.Builder().setUri(currentAudiobookStreamData!!.playlistUri).setMediaMetadata(mediaMetadata).build()
var dataSourceFactory = DefaultHttpDataSource.Factory()
dataSourceFactory.setUserAgent(channelId)
dataSourceFactory.setDefaultRequestProperties(hashMapOf("Authorization" to "Bearer ${currentAudiobook!!.token}"))
dataSourceFactory.setDefaultRequestProperties(hashMapOf("Authorization" to "Bearer ${currentAudiobookStreamData!!.token}"))
mediaSource = HlsMediaSource.Factory(dataSourceFactory).createMediaSource(mediaItem)
}
//mPlayer.setMediaSource(mediaSource, true)
mPlayer.setMediaSource(mediaSource, currentAudiobook!!.startTime)
mPlayer.setMediaSource(mediaSource, currentAudiobookStreamData!!.startTime)
mPlayer.prepare()
mPlayer.playWhenReady = currentAudiobook!!.playWhenReady
mPlayer.setPlaybackSpeed(audiobook.playbackSpeed)
mPlayer.playWhenReady = currentAudiobookStreamData!!.playWhenReady
mPlayer.setPlaybackSpeed(audiobookStreamData.playbackSpeed)
lastPauseTime = 0
}
@ -534,7 +539,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
}
fun getCurrentAudiobookId() : String {
return currentAudiobook?.id.toString()
return currentAudiobookStreamData?.id.toString()
}
fun play() {
@ -569,7 +574,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
if (mPlayer.playbackState == Player.STATE_READY) {
mPlayer.clearMediaItems()
}
currentAudiobook?.id = ""
currentAudiobookStreamData?.id = ""
lastPauseTime = 0
}
@ -589,9 +594,6 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
//
private val MY_MEDIA_ROOT_ID = "audiobookshelf"
fun setAudiobooks(_audiobooks:MutableList<Audiobook>) {
audiobooks = _audiobooks
}
private fun isValid(packageName:String, uid:Int) : Boolean {
Log.d(tag, "Check package $packageName is valid with uid $uid")
@ -620,25 +622,27 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
override fun onLoadChildren(parentMediaId: String, result: Result<MutableList<MediaBrowserCompat.MediaItem>>) {
val mediaItems: MutableList<MediaBrowserCompat.MediaItem> = mutableListOf()
if (audiobooks.size == 0) {
if (!audiobookManager.hasLoaded) {
Log.d(tag, "audiobook manager loading")
result.detach()
audiobookManager.load()
audiobookManager.fetchAudiobooks(result)
return
}
if (audiobookManager.audiobooks.size == 0) {
Log.d(tag, "AudiobookManager: Sending no items")
result.sendResult(mediaItems)
return
}
audiobooks.forEach {
audiobookManager.audiobooks.forEach {
var builder = MediaDescriptionCompat.Builder()
.setMediaId(it.id)
.setTitle(it.title)
.setSubtitle(it.author)
.setMediaUri(it.playlistUri)
.setIconUri(it.coverUri)
// val extras = Bundle()
// var startsWithA = it.title.toLowerCase().startsWith("a")
// var groupTitle = "test group
// extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, groupTitle)
// builder.setExtras(extras)\
// Log.d(tag, "Load Media Item for AUTO ${it.title} - ${it.author}")
.setTitle(it.book.title)
.setSubtitle(it.book.authorFL)
.setMediaUri(it.fallbackUri)
.setIconUri(it.getCover(audiobookManager.serverUrl, audiobookManager.token))
var mediaDescription = builder.build()
var newMediaItem = MediaBrowserCompat.MediaItem(mediaDescription, MediaBrowserCompat.MediaItem.FLAG_PLAYABLE)
@ -654,6 +658,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
// examine the passed parentMediaId to see which submenu we're at,
// and put the children of that menu in the mediaItems list
}
Log.d(tag, "AudiobookManager: Sending ${mediaItems.size} Aduiobooks")
result.sendResult(mediaItems)
}

View file

@ -7,7 +7,6 @@ class StoreService {
isService = false
platform
isOpen = false
tableName = 'downloads'
constructor() {
this.init()
@ -323,31 +322,75 @@ class StoreService {
}
}
async getDownload(id) {
async setServerConfig(config) {
if (!this.isOpen) {
var success = await this.openStore('storage', this.tableName)
var success = await this.openStore('storage', 'serverConfig')
if (!success) {
console.error('Store failed to open')
return null
return false
}
}
try {
var value = await this.getItem(id)
return JSON.parse(value)
await this.setTable('serverConfig')
} catch (error) {
console.error('Failed to get download from store', error)
console.error('Failed to set table', error)
return
}
try {
await this.setItem('config', JSON.stringify(config))
console.log(`[STORE] Set Server Config`)
return true
} catch (error) {
console.error('Failed to set server config in store', error)
return false
}
}
async getServerConfig() {
if (!this.isOpen) {
var success = await this.openStore('storage', 'serverConfig')
if (!success) {
console.error('Store failed to open')
return false
}
}
try {
await this.setTable('serverConfig')
} catch (error) {
console.error('Failed to set table', error)
return
}
try {
var configVal = await this.getItem('config')
if (!configVal) {
console.log(`[STORE] server config not available`)
return null
}
var config = JSON.parse(configVal)
console.log(`[STORE] Got Server Config`, JSON.stringify(config))
return config
} catch (error) {
console.error('Failed to set server config in store', error)
return null
}
}
async setDownload(download) {
if (!this.isOpen) {
var success = await this.openStore('storage', this.tableName)
var success = await this.openStore('storage', 'downloads')
if (!success) {
console.error('Store failed to open')
return false
}
}
try {
await this.setTable('downloads')
} catch (error) {
console.error('Failed to set table', error)
return
}
try {
await this.setItem(download.id, JSON.stringify(download))
@ -361,12 +404,18 @@ class StoreService {
async removeDownload(id) {
if (!this.isOpen) {
var success = await this.openStore('storage', this.tableName)
var success = await this.openStore('storage', 'downloads')
if (!success) {
console.error('Store failed to open')
return false
}
}
try {
await this.setTable('downloads')
} catch (error) {
console.error('Failed to set table', error)
return
}
try {
await this.removeItem(id)
@ -380,12 +429,19 @@ class StoreService {
async getAllDownloads() {
if (!this.isOpen) {
var success = await this.openStore('storage', this.tableName)
var success = await this.openStore('storage', 'downloads')
if (!success) {
console.error('Store failed to open')
return []
}
}
try {
await this.setTable('downloads')
} catch (error) {
console.error('Failed to set table', error)
return
}
var keysvalues = await this.getAllKeysValues()
var downloads = []

View file

@ -145,21 +145,6 @@ export const actions = {
},
useDownloaded({ commit, rootGetters }) {
commit('set', rootGetters['downloads/getAudiobooks'])
},
setNativeAudiobooks({ state }) {
var audiobooks = state.audiobooks.map(ab => {
var _book = ab.book
return {
id: ab.id,
title: _book.title,
author: _book.author,
duration: ab.duration,
size: ab.size,
cover: _book.cover || '',
series: _book.series || ''
}
})
MyNativeAudio.setAudiobooks({ audiobooks: audiobooks })
}
}