diff --git a/android/app/build.gradle b/android/app/build.gradle index 803a7ec5..7becd87d 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -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" diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index ffb76a80..f67b82b5 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -38,9 +38,9 @@ android:resource="@drawable/icon" /> - - - + diff --git a/android/app/src/main/java/com/audiobookshelf/app/Audiobook.kt b/android/app/src/main/java/com/audiobookshelf/app/Audiobook.kt index baf5fd83..0c9b75cf 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/Audiobook.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/Audiobook.kt @@ -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") } } diff --git a/android/app/src/main/java/com/audiobookshelf/app/AudiobookManager.kt b/android/app/src/main/java/com/audiobookshelf/app/AudiobookManager.kt new file mode 100644 index 00000000..9793d134 --- /dev/null +++ b/android/app/src/main/java/com/audiobookshelf/app/AudiobookManager.kt @@ -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 = 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>) { + 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 = 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")}") } + } +} diff --git a/android/app/src/main/java/com/audiobookshelf/app/AudiobookStreamData.kt b/android/app/src/main/java/com/audiobookshelf/app/AudiobookStreamData.kt new file mode 100644 index 00000000..463d5bdf --- /dev/null +++ b/android/app/src/main/java/com/audiobookshelf/app/AudiobookStreamData.kt @@ -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) + } + } +} diff --git a/android/app/src/main/java/com/audiobookshelf/app/Book.kt b/android/app/src/main/java/com/audiobookshelf/app/Book.kt new file mode 100644 index 00000000..2ff4e714 --- /dev/null +++ b/android/app/src/main/java/com/audiobookshelf/app/Book.kt @@ -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") + } +} diff --git a/android/app/src/main/java/com/audiobookshelf/app/MainActivity.kt b/android/app/src/main/java/com/audiobookshelf/app/MainActivity.kt index dce50c76..c7cdad2e 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/MainActivity.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/MainActivity.kt @@ -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) 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 69b537de..d794ebaf 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/MyNativeAudio.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/MyNativeAudio.kt @@ -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() - - 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() +// +// 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) { 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 81bf3f8c..e6589223 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/PlayerNotificationService.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/PlayerNotificationService.kt @@ -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() +// private var audiobooks = mutableListOf() 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) { - 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>) { val mediaItems: MutableList = 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) } diff --git a/plugins/store.js b/plugins/store.js index 74f22b71..4d891ae6 100644 --- a/plugins/store.js +++ b/plugins/store.js @@ -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 = [] diff --git a/store/audiobooks.js b/store/audiobooks.js index 78df737e..e046974b 100644 --- a/store/audiobooks.js +++ b/store/audiobooks.js @@ -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 }) } }