mirror of
https://github.com/sudoxnym/audiobookshelf-atv.git
synced 2026-07-09 21:58:52 +00:00
Android Auto: Fixed and improved search
Search now queries data from server. Results are grouped by books, series and authors.
This commit is contained in:
parent
8134ec84c6
commit
eedcd188c3
7 changed files with 119 additions and 11 deletions
|
|
@ -404,3 +404,22 @@ data class LibraryItemWithEpisode(
|
|||
var libraryItemWrapper:LibraryItemWrapper,
|
||||
var episode:PodcastEpisode
|
||||
)
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
data class LibraryItemSearchResultSeriesItemType(
|
||||
var series: LibrarySeriesItem,
|
||||
var books: List<LibraryItem>?
|
||||
)
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
data class LibraryItemSearchResultLibraryItemType(
|
||||
val libraryItem: LibraryItem
|
||||
)
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
data class LibraryItemSearchResultType(
|
||||
var book:List<LibraryItemSearchResultLibraryItemType>?,
|
||||
var podcast:List<LibraryItemSearchResultLibraryItemType>?,
|
||||
var series:List<LibraryItemSearchResultSeriesItemType>?,
|
||||
var authors:List<LibraryAuthorItem>?
|
||||
)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import android.content.Context
|
|||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.support.v4.media.MediaDescriptionCompat
|
||||
import androidx.media.utils.MediaConstants
|
||||
import com.audiobookshelf.app.BuildConfig
|
||||
import com.audiobookshelf.app.R
|
||||
import com.audiobookshelf.app.device.DeviceManager
|
||||
|
|
@ -15,7 +16,6 @@ class LibraryAuthorItem(
|
|||
id:String,
|
||||
var libraryId:String,
|
||||
var name:String,
|
||||
var lastFirst:String,
|
||||
var description:String?,
|
||||
var imagePath:String?,
|
||||
var addedAt:Long,
|
||||
|
|
@ -40,8 +40,11 @@ class LibraryAuthorItem(
|
|||
}
|
||||
|
||||
@JsonIgnore
|
||||
override fun getMediaDescription(progress:MediaProgressWrapper?, ctx: Context): MediaDescriptionCompat {
|
||||
fun getMediaDescription(progress:MediaProgressWrapper?, ctx: Context, groupTitle: String?): MediaDescriptionCompat {
|
||||
val extras = Bundle()
|
||||
if (groupTitle !== null) {
|
||||
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, groupTitle)
|
||||
}
|
||||
|
||||
val mediaId = "__LIBRARY__${libraryId}__AUTHOR__${id}"
|
||||
return MediaDescriptionCompat.Builder()
|
||||
|
|
@ -52,4 +55,9 @@ class LibraryAuthorItem(
|
|||
.setExtras(extras)
|
||||
.build()
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
override fun getMediaDescription(progress:MediaProgressWrapper?, ctx: Context): MediaDescriptionCompat {
|
||||
return getMediaDescription(progress, ctx, null)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ class LibraryItem(
|
|||
}
|
||||
|
||||
@JsonIgnore
|
||||
fun getMediaDescription(progress:MediaProgressWrapper?, ctx: Context, authorId: String?, showSeriesNumber: Boolean?): MediaDescriptionCompat {
|
||||
fun getMediaDescription(progress:MediaProgressWrapper?, ctx: Context, authorId: String?, showSeriesNumber: Boolean?, groupTitle: String?): MediaDescriptionCompat {
|
||||
val extras = Bundle()
|
||||
|
||||
if (collapsedSeries == null) {
|
||||
|
|
@ -124,6 +124,9 @@ class LibraryItem(
|
|||
)
|
||||
}
|
||||
}
|
||||
if (groupTitle !== null) {
|
||||
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, groupTitle)
|
||||
}
|
||||
|
||||
val mediaId = if (localLibraryItemId != null) {
|
||||
localLibraryItemId
|
||||
|
|
@ -153,9 +156,14 @@ class LibraryItem(
|
|||
.build()
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
fun getMediaDescription(progress:MediaProgressWrapper?, ctx: Context, authorId: String?, showSeriesNumber: Boolean?): MediaDescriptionCompat {
|
||||
return getMediaDescription(progress, ctx, authorId, showSeriesNumber, null)
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
fun getMediaDescription(progress:MediaProgressWrapper?, ctx: Context, authorId: String?): MediaDescriptionCompat {
|
||||
return getMediaDescription(progress, ctx, authorId, null)
|
||||
return getMediaDescription(progress, ctx, authorId, null, null)
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
|
|
@ -163,6 +171,6 @@ class LibraryItem(
|
|||
/*
|
||||
This is needed so Android auto library hierarchy for author series can be implemented
|
||||
*/
|
||||
return getMediaDescription(progress, ctx, null, null)
|
||||
return getMediaDescription(progress, ctx, null, null, null)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.audiobookshelf.app.data
|
|||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.support.v4.media.MediaDescriptionCompat
|
||||
import androidx.media.utils.MediaConstants
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
||||
|
||||
|
|
@ -29,7 +30,7 @@ class LibrarySeriesItem(
|
|||
}
|
||||
|
||||
@JsonIgnore
|
||||
override fun getMediaDescription(progress:MediaProgressWrapper?, ctx: Context): MediaDescriptionCompat {
|
||||
fun getMediaDescription(progress:MediaProgressWrapper?, ctx: Context, groupTitle: String?): MediaDescriptionCompat {
|
||||
val extras = Bundle()
|
||||
|
||||
if (localLibraryItemId != null) {
|
||||
|
|
@ -38,6 +39,9 @@ class LibrarySeriesItem(
|
|||
MediaDescriptionCompat.STATUS_DOWNLOADED
|
||||
)
|
||||
}
|
||||
if (groupTitle !== null) {
|
||||
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, groupTitle)
|
||||
}
|
||||
|
||||
val mediaId = "__LIBRARY__${libraryId}__SERIES__${id}"
|
||||
return MediaDescriptionCompat.Builder()
|
||||
|
|
@ -48,4 +52,9 @@ class LibrarySeriesItem(
|
|||
.setExtras(extras)
|
||||
.build()
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
override fun getMediaDescription(progress:MediaProgressWrapper?, ctx: Context): MediaDescriptionCompat {
|
||||
return getMediaDescription(progress, ctx, null)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -619,6 +619,14 @@ class MediaManager(private var apiHandler: ApiHandler, var ctx: Context) {
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun searchLocalCache(libraryId: String, queryString: String) : LibraryItemSearchResultType? {
|
||||
return suspendCoroutine {
|
||||
apiHandler.getSearchResults(libraryId, queryString) { results ->
|
||||
it.resume(results)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getFirstItem() : LibraryItemWrapper? {
|
||||
if (serverLibraryItems.isNotEmpty()) {
|
||||
return serverLibraryItems[0]
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ import com.google.android.exoplayer2.source.ProgressiveMediaSource
|
|||
import com.google.android.exoplayer2.source.hls.HlsMediaSource
|
||||
import com.google.android.exoplayer2.ui.PlayerNotificationManager
|
||||
import com.google.android.exoplayer2.upstream.*
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import java.util.*
|
||||
import kotlin.concurrent.schedule
|
||||
|
||||
|
|
@ -1350,13 +1351,55 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
|
|||
|
||||
override fun onSearch(query: String, extras: Bundle?, result: Result<MutableList<MediaBrowserCompat.MediaItem>>) {
|
||||
result.detach()
|
||||
mediaManager.loadAndroidAutoItems {
|
||||
browseTree = BrowseTree(this, mediaManager.serverItemsInProgress, mediaManager.serverLibraries)
|
||||
val children = browseTree[LIBRARIES_ROOT]?.map { item ->
|
||||
MediaBrowserCompat.MediaItem(item.description, MediaBrowserCompat.MediaItem.FLAG_BROWSABLE)
|
||||
Log.d(tag, "Search bundle: $extras")
|
||||
var foundBooks: MutableList<MediaBrowserCompat.MediaItem> = mutableListOf()
|
||||
var foundPodcasts: MutableList<MediaBrowserCompat.MediaItem> = mutableListOf()
|
||||
var foundSeries: MutableList<MediaBrowserCompat.MediaItem> = mutableListOf()
|
||||
var foundAuthors: MutableList<MediaBrowserCompat.MediaItem> = mutableListOf()
|
||||
|
||||
mediaManager.serverLibraries.forEach { serverLibrary ->
|
||||
runBlocking{
|
||||
val searchResult = mediaManager.searchLocalCache(serverLibrary.id, query)
|
||||
Log.d(tag, "onSearch: SearchResult: $searchResult")
|
||||
if (searchResult === null) return@runBlocking
|
||||
if (searchResult.book !== null && searchResult.book!!.isNotEmpty()) {
|
||||
Log.d(tag, "onSearch: found ${searchResult.book!!.size} books")
|
||||
val children = searchResult.book!!.map { bookResult ->
|
||||
val libraryItem = bookResult.libraryItem
|
||||
val progress = mediaManager.serverUserMediaProgress.find { it.libraryItemId == libraryItem.id }
|
||||
val localLibraryItem = DeviceManager.dbManager.getLocalLibraryItemByLId(libraryItem.id)
|
||||
libraryItem.localLibraryItemId = localLibraryItem?.id
|
||||
val description = libraryItem.getMediaDescription(progress, ctx, null, null, "Books (${serverLibrary.name})")
|
||||
MediaBrowserCompat.MediaItem(description, MediaBrowserCompat.MediaItem.FLAG_PLAYABLE)
|
||||
}
|
||||
foundBooks.addAll(children)
|
||||
}
|
||||
if (searchResult.series !== null && searchResult.series!!.isNotEmpty()) {
|
||||
Log.d(tag, "onSearch: found ${searchResult.series!!.size} series")
|
||||
val children = searchResult.series!!.map { seriesResult ->
|
||||
val seriesItem = seriesResult.series
|
||||
seriesItem.books = seriesResult.books as MutableList<LibraryItem>
|
||||
val description = seriesItem.getMediaDescription(null, ctx, "Series (${serverLibrary.name})")
|
||||
MediaBrowserCompat.MediaItem(description, MediaBrowserCompat.MediaItem.FLAG_BROWSABLE)
|
||||
}
|
||||
foundSeries.addAll(children)
|
||||
}
|
||||
if (searchResult.authors !== null && searchResult.authors!!.isNotEmpty()) {
|
||||
Log.d(tag, "onSearch: found ${searchResult.authors!!.size} authors")
|
||||
val children = searchResult.authors!!.map { authorItem ->
|
||||
val description = authorItem.getMediaDescription(null, ctx, "Authors (${serverLibrary.name})")
|
||||
MediaBrowserCompat.MediaItem(description, MediaBrowserCompat.MediaItem.FLAG_BROWSABLE)
|
||||
}
|
||||
foundAuthors.addAll(children)
|
||||
}
|
||||
Log.d(tag, "onSearch: Library ${serverLibrary.id} processed")
|
||||
}
|
||||
result.sendResult(children as MutableList<MediaBrowserCompat.MediaItem>?)
|
||||
Log.d(tag, "onSearch: Library ${serverLibrary.id} scanned")
|
||||
}
|
||||
foundBooks.addAll(foundSeries)
|
||||
foundBooks.addAll(foundAuthors)
|
||||
result.sendResult(foundBooks as MutableList<MediaBrowserCompat.MediaItem>?)
|
||||
Log.d(tag, "onSearch: Done")
|
||||
}
|
||||
|
||||
//
|
||||
|
|
|
|||
|
|
@ -286,6 +286,19 @@ class ApiHandler(var ctx:Context) {
|
|||
}
|
||||
}
|
||||
|
||||
fun getSearchResults(libraryId:String, queryString:String, cb: (LibraryItemSearchResultType?) -> Unit) {
|
||||
Log.d(tag, "Doing search for library $libraryId")
|
||||
getRequest("/api/libraries/$libraryId/search?q=$queryString", null, null) {
|
||||
if (it.has("error")) {
|
||||
Log.e(tag, it.getString("error") ?: "getSearchResults Failed")
|
||||
cb(null)
|
||||
} else {
|
||||
val librarySearchResults = jacksonMapper.readValue<LibraryItemSearchResultType>(it.toString())
|
||||
cb(librarySearchResults)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getAllItemsInProgress(cb: (List<ItemInProgress>) -> Unit) {
|
||||
getRequest("/api/me/items-in-progress", null, null) {
|
||||
val items = mutableListOf<ItemInProgress>()
|
||||
|
|
|
|||
Loading…
Reference in a new issue