From 56c73c12dbbdb159993aac8dd68d123f490c8278 Mon Sep 17 00:00:00 2001 From: advplyr Date: Sun, 7 Aug 2022 17:52:37 -0500 Subject: [PATCH] Update:Android auto show progress bar --- android/app/build.gradle | 2 +- .../audiobookshelf/app/data/DataClasses.kt | 16 ++- .../app/data/LocalLibraryItem.kt | 7 +- .../app/data/LocalMediaProgress.kt | 8 +- .../app/data/PlaybackSession.kt | 2 - .../audiobookshelf/app/media/MediaManager.kt | 116 ++++++++++++++++-- .../audiobookshelf/app/player/BrowseTree.kt | 9 +- .../app/player/PlayerNotificationService.kt | 28 +++-- .../audiobookshelf/app/server/ApiHandler.kt | 36 +++++- 9 files changed, 176 insertions(+), 48 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 7f5e8ab6..3e37d1da 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -121,7 +121,7 @@ dependencies { implementation 'com.squareup.okhttp3:okhttp:4.9.2' // Jackson for JSON - implementation 'com.fasterxml.jackson.module:jackson-module-kotlin:2.12.1' + implementation 'com.fasterxml.jackson.module:jackson-module-kotlin:2.12.2' // FFMPEG-Kit implementation 'com.arthenica:ffmpeg-kit-min:4.5.1' 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 0e926874..9e4997f5 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 @@ -59,8 +59,6 @@ data class LibraryItem( putString(MediaMetadataCompat.METADATA_KEY_TITLE, title) putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, authorName) putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI, getCoverUri().toString()) - putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI, getCoverUri().toString()) - putString(MediaMetadataCompat.METADATA_KEY_ART_URI, getCoverUri().toString()) putString(MediaMetadataCompat.METADATA_KEY_AUTHOR, authorName) }.build() } @@ -309,7 +307,6 @@ data class PodcastEpisode( putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID, id) putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, title) putString(MediaMetadataCompat.METADATA_KEY_TITLE, title) - putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, podcast.metadata.getAuthorDisplayName()) putString(MediaMetadataCompat.METADATA_KEY_AUTHOR, podcast.metadata.getAuthorDisplayName()) putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI, coverUri.toString()) @@ -407,18 +404,25 @@ data class BookChapter( } @JsonIgnoreProperties(ignoreUnknown = true) -data class MediaProgress( +class MediaProgress( var id:String, var libraryItemId:String, var episodeId:String?, var duration:Double, // seconds - var progress:Double, // 0 to 1 + progress:Double, // 0 to 1 var currentTime:Double, - var isFinished:Boolean, + isFinished:Boolean, var lastUpdate:Long, var startedAt:Long, var finishedAt:Long? +) : MediaProgressWrapper(isFinished, progress) + +@JsonTypeInfo(use= JsonTypeInfo.Id.DEDUCTION, defaultImpl = MediaProgress::class) +@JsonSubTypes( + JsonSubTypes.Type(MediaProgress::class), + JsonSubTypes.Type(LocalMediaProgress::class) ) +open class MediaProgressWrapper(var isFinished:Boolean, var progress:Double) // Helper class data class LibraryItemWithEpisode( diff --git a/android/app/src/main/java/com/audiobookshelf/app/data/LocalLibraryItem.kt b/android/app/src/main/java/com/audiobookshelf/app/data/LocalLibraryItem.kt index 1536ac4f..c1b2e2ce 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/data/LocalLibraryItem.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/data/LocalLibraryItem.kt @@ -2,8 +2,11 @@ package com.audiobookshelf.app.data import android.content.Context import android.net.Uri +import android.os.Bundle +import android.support.v4.media.MediaDescriptionCompat import android.support.v4.media.MediaMetadataCompat import android.util.Log +import androidx.media.utils.MediaConstants import com.audiobookshelf.app.R import com.audiobookshelf.app.device.DeviceManager import com.fasterxml.jackson.annotation.JsonIgnore @@ -95,7 +98,7 @@ data class LocalLibraryItem( } @JsonIgnore - fun getMediaMetadata(ctx: Context): MediaMetadataCompat { + fun getMediaMetadata(): MediaMetadataCompat { val coverUri = getCoverUri() return MediaMetadataCompat.Builder().apply { @@ -104,8 +107,6 @@ data class LocalLibraryItem( putString(MediaMetadataCompat.METADATA_KEY_TITLE, title) putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, authorName) putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI, coverUri.toString()) - putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI, coverUri.toString()) - putString(MediaMetadataCompat.METADATA_KEY_ART_URI, coverUri.toString()) putString(MediaMetadataCompat.METADATA_KEY_AUTHOR, authorName) }.build() } diff --git a/android/app/src/main/java/com/audiobookshelf/app/data/LocalMediaProgress.kt b/android/app/src/main/java/com/audiobookshelf/app/data/LocalMediaProgress.kt index 3a5d7462..e6658fcc 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/data/LocalMediaProgress.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/data/LocalMediaProgress.kt @@ -5,14 +5,14 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties import kotlin.math.roundToInt @JsonIgnoreProperties(ignoreUnknown = true) -data class LocalMediaProgress( +class LocalMediaProgress( var id:String, var localLibraryItemId:String, var localEpisodeId:String?, var duration:Double, - var progress:Double, // 0 to 1 + progress:Double, // 0 to 1 var currentTime:Double, - var isFinished:Boolean, + isFinished:Boolean, var lastUpdate:Long, var startedAt:Long, var finishedAt:Long?, @@ -22,7 +22,7 @@ data class LocalMediaProgress( var serverUserId:String?, var libraryItemId:String?, var episodeId:String? -) { +) : MediaProgressWrapper(isFinished, progress) { @get:JsonIgnore val progressPercent get() = if (progress.isNaN()) 0 else (progress * 100).roundToInt() 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 eb38c9d1..2fdd9ee9 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 @@ -123,8 +123,6 @@ class PlaybackSession( .putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, displayTitle) .putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, displayAuthor) .putString(MediaMetadataCompat.METADATA_KEY_AUTHOR, displayAuthor) - .putString(MediaMetadataCompat.METADATA_KEY_ARTIST, displayAuthor) - .putString(MediaMetadataCompat.METADATA_KEY_ALBUM, "series") .putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID, id) return metadataBuilder.build() } diff --git a/android/app/src/main/java/com/audiobookshelf/app/media/MediaManager.kt b/android/app/src/main/java/com/audiobookshelf/app/media/MediaManager.kt index 945ab001..2ca65fb9 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/media/MediaManager.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/media/MediaManager.kt @@ -2,8 +2,12 @@ package com.audiobookshelf.app.media import android.app.Activity import android.content.Context +import android.os.Bundle import android.support.v4.media.MediaBrowserCompat +import android.support.v4.media.MediaDescriptionCompat +import android.support.v4.media.MediaMetadataCompat import android.util.Log +import androidx.media.utils.MediaConstants import com.audiobookshelf.app.data.* import com.audiobookshelf.app.device.DeviceManager import com.audiobookshelf.app.server.ApiHandler @@ -28,6 +32,8 @@ class MediaManager(var apiHandler: ApiHandler, var ctx: Context) { var serverLibraryCategories = listOf() var serverLibraries = listOf() var serverConfigIdUsed:String? = null + var serverConfigLastPing:Long = 0L + var serverUserMediaProgress:MutableList = mutableListOf() var userSettingsPlaybackRate:Float? = null @@ -132,7 +138,11 @@ class MediaManager(var apiHandler: ApiHandler, var ctx: Context) { val children = podcast.episodes?.map { podcastEpisode -> Log.d(tag, "Local Podcast Episode ${podcastEpisode.title} | ${podcastEpisode.id}") - MediaBrowserCompat.MediaItem(podcastEpisode.getMediaMetadata(libraryItemWrapper).description, MediaBrowserCompat.MediaItem.FLAG_PLAYABLE) + + val mediaMetadata = podcastEpisode.getMediaMetadata(libraryItemWrapper) + val progress = DeviceManager.dbManager.getLocalMediaProgress("${libraryItemWrapper.id}-${podcastEpisode.id}") + val description = getMediaDescriptionFromMediaMetadata(mediaMetadata, progress) + MediaBrowserCompat.MediaItem(description, MediaBrowserCompat.MediaItem.FLAG_PLAYABLE) } children?.let { cb(children as MutableList) } ?: cb(mutableListOf()) } @@ -147,7 +157,11 @@ class MediaManager(var apiHandler: ApiHandler, var ctx: Context) { selectedPodcast = podcast val children = podcast.episodes?.map { podcastEpisode -> - MediaBrowserCompat.MediaItem(podcastEpisode.getMediaMetadata(libraryItemWrapper).description, MediaBrowserCompat.MediaItem.FLAG_PLAYABLE) + + val mediaMetadata = podcastEpisode.getMediaMetadata(libraryItemWrapper) + val progress = serverUserMediaProgress.find { it.libraryItemId == libraryItemWrapper.id && it.episodeId == podcastEpisode.id } + val description = getMediaDescriptionFromMediaMetadata(mediaMetadata, progress) + MediaBrowserCompat.MediaItem(description, MediaBrowserCompat.MediaItem.FLAG_PLAYABLE) } children?.let { cb(children as MutableList) } ?: cb(mutableListOf()) } @@ -179,16 +193,49 @@ class MediaManager(var apiHandler: ApiHandler, var ctx: Context) { return successfulPing } - fun checkSetValidServerConnectionConfig(cb: (Boolean) -> Unit) = runBlocking { - if (!apiHandler.isOnline()) cb(false) - else { - coroutineScope { - var hasValidConn = false + suspend fun authorize(config:ServerConnectionConfig) : MutableList { + var mediaProgress:MutableList = mutableListOf() + suspendCoroutine> { cont -> + apiHandler.authorize(config) { + Log.d(tag, "authorize: Authorized server config ${config.address} result = $it") + if (!it.isNullOrEmpty()) { + mediaProgress = it + } + cont.resume(mediaProgress) + } + } + return mediaProgress + } - // First check if the current selected config is pingable - DeviceManager.serverConnectionConfig?.let { - hasValidConn = checkServerConnection(it) - Log.d(tag, "checkSetValidServerConnectionConfig: Current config ${DeviceManager.serverAddress} is pingable? $hasValidConn") + fun checkSetValidServerConnectionConfig(cb: (Boolean) -> Unit) = runBlocking { + Log.d(tag, "checkSetValidServerConnectionConfig | $serverConfigIdUsed") + + coroutineScope { + if (!apiHandler.isOnline()) { + serverUserMediaProgress = mutableListOf() + cb(false) + } else { + + var hasValidConn = false + var lookupMediaProgress = true + + if (!serverConfigIdUsed.isNullOrEmpty() && serverConfigLastPing > 0L && System.currentTimeMillis() - serverConfigLastPing < 5000) { + Log.d(tag, "checkSetValidServerConnectionConfig last ping less than a 5 seconds ago") + hasValidConn = true + lookupMediaProgress = false + } else { + serverUserMediaProgress = mutableListOf() + } + + if (!hasValidConn) { + // First check if the current selected config is pingable + DeviceManager.serverConnectionConfig?.let { + hasValidConn = checkServerConnection(it) + Log.d( + tag, + "checkSetValidServerConnectionConfig: Current config ${DeviceManager.serverAddress} is pingable? $hasValidConn" + ) + } } if (!hasValidConn) { @@ -205,9 +252,21 @@ class MediaManager(var apiHandler: ApiHandler, var ctx: Context) { } } + if (hasValidConn) { + serverConfigLastPing = System.currentTimeMillis() + + if (lookupMediaProgress) { + Log.d(tag, "Has valid conn now get user media progress") + DeviceManager.serverConnectionConfig?.let { + serverUserMediaProgress = authorize(it) + } + } + } + cb(hasValidConn) } } + } // TODO: Load currently listening category for local items @@ -328,6 +387,41 @@ class MediaManager(var apiHandler: ApiHandler, var ctx: Context) { } } + fun getMediaDescriptionFromMediaMetadata(item: MediaMetadataCompat, progress:MediaProgressWrapper?): MediaDescriptionCompat { + + val extras = Bundle() + if (progress != null) { + Log.d(tag, "Has media progress for ${item.description.title} | ${progress}") + if (progress.isFinished) { + extras.putInt( + MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS, + MediaConstants.DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_FULLY_PLAYED + ) + } else { + extras.putInt( + MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS, + MediaConstants.DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED + ) + extras.putDouble( + MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE, progress.progress + ) + } + } else { + Log.d(tag, "No media progress for ${item.description.title} | ${item.description.mediaId}") + extras.putInt( + MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS, + MediaConstants.DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_NOT_PLAYED + ) + } + + return MediaDescriptionCompat.Builder() + .setMediaId(item.description.mediaId) + .setTitle(item.description.title) + .setIconUri(item.description.iconUri) + .setSubtitle(item.description.subtitle) + .setExtras(extras).build() + } + private fun levenshtein(lhs : CharSequence, rhs : CharSequence) : Int { val lhsLength = lhs.length + 1 val rhsLength = rhs.length + 1 diff --git a/android/app/src/main/java/com/audiobookshelf/app/player/BrowseTree.kt b/android/app/src/main/java/com/audiobookshelf/app/player/BrowseTree.kt index 115f6b3d..cf519f8e 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/player/BrowseTree.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/player/BrowseTree.kt @@ -6,10 +6,7 @@ import android.net.Uri import android.support.v4.media.MediaMetadataCompat import androidx.annotation.AnyRes import com.audiobookshelf.app.R -import com.audiobookshelf.app.data.Library -import com.audiobookshelf.app.data.LibraryCategory -import com.audiobookshelf.app.data.LibraryItem -import com.audiobookshelf.app.data.LocalLibraryItem +import com.audiobookshelf.app.data.* class BrowseTree( val context: Context, @@ -85,7 +82,7 @@ class BrowseTree( localBooksCat.entities.forEach { libc -> val libraryItem = libc as LocalLibraryItem val children = mediaIdToChildren[DOWNLOADS_ROOT] ?: mutableListOf() - children += libraryItem.getMediaMetadata(context) + children += libraryItem.getMediaMetadata() mediaIdToChildren[DOWNLOADS_ROOT] = children } } @@ -94,7 +91,7 @@ class BrowseTree( localPodcastsCat.entities.forEach { libc -> val libraryItem = libc as LocalLibraryItem val children = mediaIdToChildren[DOWNLOADS_ROOT] ?: mutableListOf() - children += libraryItem.getMediaMetadata(context) + children += libraryItem.getMediaMetadata() mediaIdToChildren[DOWNLOADS_ROOT] = children } } diff --git a/android/app/src/main/java/com/audiobookshelf/app/player/PlayerNotificationService.kt b/android/app/src/main/java/com/audiobookshelf/app/player/PlayerNotificationService.kt index 86822961..6afe9c84 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/player/PlayerNotificationService.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/player/PlayerNotificationService.kt @@ -816,7 +816,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() { override fun onLoadChildren(parentMediaId: String, result: Result>) { Log.d(tag, "ON LOAD CHILDREN $parentMediaId") - var flag = if (parentMediaId == AUTO_MEDIA_ROOT || parentMediaId == LIBRARIES_ROOT) MediaBrowserCompat.MediaItem.FLAG_BROWSABLE else MediaBrowserCompat.MediaItem.FLAG_PLAYABLE + val flag = if (parentMediaId == AUTO_MEDIA_ROOT || parentMediaId == LIBRARIES_ROOT) MediaBrowserCompat.MediaItem.FLAG_BROWSABLE else MediaBrowserCompat.MediaItem.FLAG_PLAYABLE result.detach() @@ -832,10 +832,12 @@ class PlayerNotificationService : MediaBrowserServiceCompat() { val libraryItemMediaMetadata = libraryItem.getMediaMetadata() if (libraryItem.mediaType == "podcast") { // Podcasts are browseable - flag = MediaBrowserCompat.MediaItem.FLAG_BROWSABLE + MediaBrowserCompat.MediaItem(libraryItemMediaMetadata.description, MediaBrowserCompat.MediaItem.FLAG_BROWSABLE) + } else { + val progress = mediaManager.serverUserMediaProgress.find { it.libraryItemId == libraryItemMediaMetadata.description.mediaId } + val description = mediaManager.getMediaDescriptionFromMediaMetadata(libraryItemMediaMetadata, progress) + MediaBrowserCompat.MediaItem(description, MediaBrowserCompat.MediaItem.FLAG_PLAYABLE) } - - MediaBrowserCompat.MediaItem(libraryItemMediaMetadata.description, flag) } result.sendResult(children as MutableList?) } @@ -846,26 +848,34 @@ class PlayerNotificationService : MediaBrowserServiceCompat() { val localBrowseItems:MutableList = mutableListOf() localBooks.forEach { localLibraryItem -> - val mediaMetadata = localLibraryItem.getMediaMetadata(ctx) - localBrowseItems += MediaBrowserCompat.MediaItem(mediaMetadata.description, MediaBrowserCompat.MediaItem.FLAG_PLAYABLE) + val mediaMetadata = localLibraryItem.getMediaMetadata() + val progress = DeviceManager.dbManager.getLocalMediaProgress(mediaMetadata.description.mediaId ?: "") + val description = mediaManager.getMediaDescriptionFromMediaMetadata(mediaMetadata, progress) + + localBrowseItems += MediaBrowserCompat.MediaItem(description, MediaBrowserCompat.MediaItem.FLAG_PLAYABLE) } localPodcasts.forEach { localLibraryItem -> - val mediaMetadata = localLibraryItem.getMediaMetadata(ctx) + val mediaMetadata = localLibraryItem.getMediaMetadata() localBrowseItems += MediaBrowserCompat.MediaItem(mediaMetadata.description, MediaBrowserCompat.MediaItem.FLAG_BROWSABLE) } result.sendResult(localBrowseItems) } else { // Load categories - mediaManager.loadAndroidAutoItems() { libraryCategories -> browseTree = BrowseTree(this, libraryCategories, mediaManager.serverLibraries) val children = browseTree[parentMediaId]?.map { item -> Log.d(tag, "Loading Browser Media Item ${item.description.title} $flag") - MediaBrowserCompat.MediaItem(item.description, flag) + if (flag == MediaBrowserCompat.MediaItem.FLAG_PLAYABLE) { + val progress = mediaManager.serverUserMediaProgress.find { it.libraryItemId == item.description.mediaId } + val description = mediaManager.getMediaDescriptionFromMediaMetadata(item, progress) + MediaBrowserCompat.MediaItem(description, flag) + } else { + MediaBrowserCompat.MediaItem(item.description, flag) + } } result.sendResult(children as MutableList?) } diff --git a/android/app/src/main/java/com/audiobookshelf/app/server/ApiHandler.kt b/android/app/src/main/java/com/audiobookshelf/app/server/ApiHandler.kt index b55910f5..3893b558 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/server/ApiHandler.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/server/ApiHandler.kt @@ -16,6 +16,7 @@ import com.getcapacitor.JSObject import okhttp3.* import okhttp3.MediaType.Companion.toMediaType import okhttp3.RequestBody.Companion.toRequestBody +import org.json.JSONArray import org.json.JSONException import org.json.JSONObject import java.io.IOException @@ -43,11 +44,13 @@ class ApiHandler(var ctx:Context) { makeRequest(request, httpClient, cb) } - fun postRequest(endpoint:String, payload: JSObject, cb: (JSObject) -> Unit) { + fun postRequest(endpoint:String, payload: JSObject, config:ServerConnectionConfig?, cb: (JSObject) -> Unit) { + val address = config?.address ?: DeviceManager.serverAddress + val token = config?.token ?: DeviceManager.token val mediaType = "application/json; charset=utf-8".toMediaType() val requestBody = payload.toString().toRequestBody(mediaType) val request = Request.Builder().post(requestBody) - .url("${DeviceManager.serverAddress}$endpoint").addHeader("Authorization", "Bearer ${DeviceManager.token}") + .url("${address}$endpoint").addHeader("Authorization", "Bearer ${token}") .build() makeRequest(request, null, cb) } @@ -211,7 +214,7 @@ class ApiHandler(var ctx:Context) { val payload = JSObject(jacksonMapper.writeValueAsString(playItemRequestPayload)) val endpoint = if (episodeId.isNullOrEmpty()) "/api/items/$libraryItemId/play" else "/api/items/$libraryItemId/play/$episodeId" - postRequest(endpoint, payload) { + postRequest(endpoint, payload, null) { if (it.has("error")) { Log.e(tag, it.getString("error") ?: "Play Library Item Failed") cb(null) @@ -227,7 +230,7 @@ class ApiHandler(var ctx:Context) { fun sendProgressSync(sessionId:String, syncData: MediaProgressSyncData, cb: (Boolean) -> Unit) { val payload = JSObject(jacksonMapper.writeValueAsString(syncData)) - postRequest("/api/session/$sessionId/sync", payload) { + postRequest("/api/session/$sessionId/sync", payload, null) { if (!it.getString("error").isNullOrEmpty()) { cb(false) } else { @@ -239,7 +242,7 @@ class ApiHandler(var ctx:Context) { fun sendLocalProgressSync(playbackSession:PlaybackSession, cb: (Boolean) -> Unit) { val payload = JSObject(jacksonMapper.writeValueAsString(playbackSession)) - postRequest("/api/session/local", payload) { + postRequest("/api/session/local", payload, null) { if (!it.getString("error").isNullOrEmpty()) { cb(false) } else { @@ -265,7 +268,7 @@ class ApiHandler(var ctx:Context) { if (localMediaProgress.isNotEmpty()) { Log.d(tag, "Sending sync local progress request with ${localMediaProgress.size} progress items") val payload = JSObject(jacksonMapper.writeValueAsString(LocalMediaProgressSyncPayload(localMediaProgress))) - postRequest("/api/me/sync-local-progress", payload) { + postRequest("/api/me/sync-local-progress", payload, null) { Log.d(tag, "Media Progress Sync payload $payload - response ${it}") if (it.toString() == "{}") { @@ -343,4 +346,25 @@ class ApiHandler(var ctx:Context) { } } } + + fun authorize(config:ServerConnectionConfig, cb: (MutableList?) -> Unit) { + Log.d(tag, "authorize: Authorizing ${config.address}") + postRequest("/api/authorize", JSObject(), config) { + val error = it.getString("error") + if (!error.isNullOrEmpty()) { + Log.d(tag, "authorize: Authorize ${config.address} Failed: $error") + cb(null) + } else { + val mediaProgressList:MutableList = mutableListOf() + val user = it.getJSObject("user") + val mediaProgress = user?.getJSONArray("mediaProgress") ?: JSONArray() + for (i in 0 until mediaProgress.length()) { + val mediaProg = jacksonMapper.readValue(mediaProgress.getJSONObject(i).toString()) + mediaProgressList.add(mediaProg) + } + Log.d(tag, "authorize: Authorize ${config.address} Successful") + cb(mediaProgressList) + } + } + } }