Update loading library stats, filter non-audio items from search, prevent recent episodes from loading same podcast library item multiple times

This commit is contained in:
advplyr
2025-01-11 14:55:30 -06:00
parent 0da3045c73
commit 8e6e0cf673
7 changed files with 43 additions and 49 deletions
@@ -28,7 +28,8 @@ open class MediaType(var metadata:MediaTypeMetadata, var coverPath:String?) {
open fun removeAudioTrack(localFileId:String) { } open fun removeAudioTrack(localFileId:String) { }
@JsonIgnore @JsonIgnore
open fun getLocalCopy():MediaType { return MediaType(MediaTypeMetadata("", false),null) } open fun getLocalCopy():MediaType { return MediaType(MediaTypeMetadata("", false),null) }
@JsonIgnore
open fun checkHasTracks():Boolean { return false }
} }
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
@@ -96,6 +97,11 @@ class Podcast(
return Podcast(metadata as PodcastMetadata,coverPath,tags, mutableListOf(),autoDownloadEpisodes, 0) return Podcast(metadata as PodcastMetadata,coverPath,tags, mutableListOf(),autoDownloadEpisodes, 0)
} }
@JsonIgnore
override fun checkHasTracks():Boolean {
return (episodes?.size ?: numEpisodes ?: 0) > 0
}
@JsonIgnore @JsonIgnore
fun addEpisode(audioTrack:AudioTrack, episode:PodcastEpisode):PodcastEpisode { fun addEpisode(audioTrack:AudioTrack, episode:PodcastEpisode):PodcastEpisode {
val localEpisodeId = "local_ep_" + episode.id val localEpisodeId = "local_ep_" + episode.id
@@ -185,6 +191,11 @@ class Book(
override fun getLocalCopy(): Book { override fun getLocalCopy(): Book {
return Book(metadata as BookMetadata,coverPath,tags, mutableListOf(),chapters,mutableListOf(), ebookFile, null,null, 0) return Book(metadata as BookMetadata,coverPath,tags, mutableListOf(),chapters,mutableListOf(), ebookFile, null,null, 0)
} }
@JsonIgnore
override fun checkHasTracks():Boolean {
return (tracks?.size ?: numTracks ?: 0) > 0
}
} }
// This auto-detects whether it is a BookMetadata or PodcastMetadata // This auto-detects whether it is a BookMetadata or PodcastMetadata
@@ -375,8 +386,9 @@ data class Library(
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
data class LibraryStats( data class LibraryStats(
var totalItems: Int, var totalItems: Int,
var totalAuthors: Int, var totalSize: Long,
var numAudioTracks: Int var totalDuration: Double,
var numAudioFiles: Int
) )
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
@@ -58,11 +58,7 @@ class LibraryItem(
@JsonIgnore @JsonIgnore
fun checkHasTracks():Boolean { fun checkHasTracks():Boolean {
return if (mediaType == "podcast") { return media.checkHasTracks()
((media as Podcast).numEpisodes ?: 0) > 0
} else {
((media as Book).numTracks ?: 0) > 0
}
} }
@get:JsonIgnore @get:JsonIgnore
@@ -25,7 +25,7 @@ class LibrarySeriesItem(
val audiobookCount: Int val audiobookCount: Int
get() { get() {
if (books == null) return 0 if (books == null) return 0
val booksWithAudio = books?.filter { b -> (b.media as Book).numTracks != 0 } val booksWithAudio = books?.filter { b -> b.media.checkHasTracks() }
return booksWithAudio?.size ?: 0 return booksWithAudio?.size ?: 0
} }
@@ -182,7 +182,10 @@ class MediaManager(private var apiHandler: ApiHandler, var ctx: Context) {
libraryPersonalizationsDone-- libraryPersonalizationsDone--
} }
} }
while (libraryPersonalizationsDone > 0) { } while (libraryPersonalizationsDone > 0) { }
Log.d(tag, "Finished loading all library personalization data")
allLibraryPersonalizationsDone = true allLibraryPersonalizationsDone = true
cb() cb()
} }
@@ -239,8 +242,12 @@ class MediaManager(private var apiHandler: ApiHandler, var ctx: Context) {
cachedLibraryRecentShelves[libraryId]!!.add(shelf) cachedLibraryRecentShelves[libraryId]!!.add(shelf)
} }
val podcastLibraryItemIds = mutableListOf<String>()
(shelf as LibraryShelfEpisodeEntity).entities?.forEach { libraryItem -> (shelf as LibraryShelfEpisodeEntity).entities?.forEach { libraryItem ->
loadPodcastItem(libraryItem.libraryId, libraryItem.id) {} if (!podcastLibraryItemIds.contains(libraryItem.id)) {
podcastLibraryItemIds.add(libraryItem.id)
loadPodcastItem(libraryItem.libraryId, libraryItem.id) {}
}
} }
} }
} else if (shelf.type == "podcast") { } else if (shelf.type == "podcast") {
@@ -596,13 +603,13 @@ class MediaManager(private var apiHandler: ApiHandler, var ctx: Context) {
cachedLibraryPodcasts[libraryId] = mutableMapOf() cachedLibraryPodcasts[libraryId] = mutableMapOf()
} }
if (cachedLibraryPodcasts[libraryId]!!.containsKey(libraryItemId)) { if (cachedLibraryPodcasts[libraryId]!!.containsKey(libraryItemId)) {
Log.d(tag, "Podcast found from cache | Library $libraryItemId ") Log.d(tag, "loadPodcastItem: Podcast found from cache | Library $libraryItemId ")
cb(cachedLibraryPodcasts[libraryId]?.get(libraryItemId)) cb(cachedLibraryPodcasts[libraryId]?.get(libraryItemId))
} else { } else {
Log.d(tag, "loadPodcastItem: $libraryItemId") Log.d(tag, "loadPodcastItem: Calling getLibraryItem $libraryItemId")
apiHandler.getLibraryItem(libraryItemId) { libraryItem -> apiHandler.getLibraryItem(libraryItemId) { libraryItem ->
if (libraryItem !== null) { if (libraryItem !== null) {
Log.d(tag, "loadPodcastItem: Got library item $libraryItem") Log.d(tag, "loadPodcastItem: Got library item ${libraryItem.id} ${libraryItem.media.metadata.title}")
val podcast = libraryItem.media as Podcast val podcast = libraryItem.media as Podcast
podcast.episodes?.forEach { podcastEpisode -> podcast.episodes?.forEach { podcastEpisode ->
podcastEpisodeLibraryItemMap[podcastEpisode.id] = LibraryItemWithEpisode(libraryItem, podcastEpisode) podcastEpisodeLibraryItemMap[podcastEpisode.id] = LibraryItemWithEpisode(libraryItem, podcastEpisode)
@@ -682,21 +689,14 @@ class MediaManager(private var apiHandler: ApiHandler, var ctx: Context) {
} }
/** /**
* Loads libraries for selected server. * Loads libraries for selected server with stats
* After loading libraries fetches stats for each library.
*/ */
private fun loadLibraries(cb: (List<Library>) -> Unit) { private fun loadLibraries(cb: (List<Library>) -> Unit) {
if (serverLibraries.isNotEmpty()) { if (serverLibraries.isNotEmpty()) {
cb(serverLibraries) cb(serverLibraries)
} else { } else {
apiHandler.getLibraries { loadedLibraries -> apiHandler.getLibraries { loadedLibraries ->
serverLibraries = loadedLibraries.map { library -> serverLibraries = loadedLibraries
apiHandler.getLibraryStats(library.id) { libraryStats ->
Log.d(tag, "Library stats for library ${library.id} | $libraryStats")
library.stats = libraryStats
}
library
}
cb(serverLibraries) cb(serverLibraries)
} }
} }
@@ -875,8 +875,9 @@ class MediaManager(private var apiHandler: ApiHandler, var ctx: Context) {
// Books // Books
if (searchResult.book !== null && searchResult.book!!.isNotEmpty()) { if (searchResult.book !== null && searchResult.book!!.isNotEmpty()) {
Log.d(tag, "searchLocalCache: found ${searchResult.book!!.size} books") Log.d(tag, "searchLocalCache: found ${searchResult.book!!.size} books")
val children = searchResult.book!!.map { bookResult -> val children = searchResult.book!!.filter { it.libraryItem.checkHasTracks() }.map { bookResult ->
val libraryItem = bookResult.libraryItem val libraryItem = bookResult.libraryItem
if (serverLibraryItems.find { li -> li.id == libraryItem.id } == null) { if (serverLibraryItems.find { li -> li.id == libraryItem.id } == null) {
serverLibraryItems.add(libraryItem) serverLibraryItems.add(libraryItem)
} }
@@ -54,7 +54,7 @@ class BrowseTree(
libraries.forEach { library -> libraries.forEach { library ->
// Skip libraries without audio content // Skip libraries without audio content
if (library.stats?.numAudioTracks == 0) return@forEach if (library.stats?.numAudioFiles == 0) return@forEach
Log.d("BrowseTree", "Library $library | ${library.icon}") Log.d("BrowseTree", "Library $library | ${library.icon}")
// Generate library list items for Libraries menu // Generate library list items for Libraries menu
val libraryMediaMetadata = library.getMediaMetadata(context) val libraryMediaMetadata = library.getMediaMetadata(context)
@@ -1224,7 +1224,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
return return
} }
Log.d(tag, "Mediaparts: ${mediaIdParts.size} | $mediaIdParts") Log.d(tag, "Mediaparts: ${mediaIdParts.size} | $mediaIdParts")
if(mediaIdParts.size == 3) { if (mediaIdParts.size == 3) {
mediaManager.getLibraryRecentShelfs(mediaIdParts[2]) { availableShelfs -> mediaManager.getLibraryRecentShelfs(mediaIdParts[2]) { availableShelfs ->
Log.d(tag, "Found ${availableShelfs.size} shelfs") Log.d(tag, "Found ${availableShelfs.size} shelfs")
val children : MutableList<MediaBrowserCompat.MediaItem> = mutableListOf() val children : MutableList<MediaBrowserCompat.MediaItem> = mutableListOf()
@@ -1302,7 +1302,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
MediaBrowserCompat.MediaItem(description, MediaBrowserCompat.MediaItem.FLAG_PLAYABLE) MediaBrowserCompat.MediaItem(description, MediaBrowserCompat.MediaItem.FLAG_PLAYABLE)
} }
result.sendResult(children as MutableList<MediaBrowserCompat.MediaItem>?) result.sendResult(children as MutableList<MediaBrowserCompat.MediaItem>?)
}else if (shelf.type == "episode") { } else if (shelf.type == "episode") {
val episodesWithRecentEpisode = (shelf as LibraryShelfEpisodeEntity).entities?.filter { libraryItem -> libraryItem.recentEpisode !== null } val episodesWithRecentEpisode = (shelf as LibraryShelfEpisodeEntity).entities?.filter { libraryItem -> libraryItem.recentEpisode !== null }
val children = episodesWithRecentEpisode?.map { libraryItem -> val children = episodesWithRecentEpisode?.map { libraryItem ->
val podcast = libraryItem.media as Podcast val podcast = libraryItem.media as Podcast
@@ -1319,7 +1319,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
MediaBrowserCompat.MediaItem(description, MediaBrowserCompat.MediaItem.FLAG_PLAYABLE) MediaBrowserCompat.MediaItem(description, MediaBrowserCompat.MediaItem.FLAG_PLAYABLE)
} }
result.sendResult(children as MutableList<MediaBrowserCompat.MediaItem>?) result.sendResult(children as MutableList<MediaBrowserCompat.MediaItem>?)
}else if (shelf.type == "podcast") { } else if (shelf.type == "podcast") {
val children = (shelf as LibraryShelfPodcastEntity).entities?.map { libraryItem -> val children = (shelf as LibraryShelfPodcastEntity).entities?.map { libraryItem ->
val mediaDescription = libraryItem.getMediaDescription(null, ctx) val mediaDescription = libraryItem.getMediaDescription(null, ctx)
MediaBrowserCompat.MediaItem( MediaBrowserCompat.MediaItem(
@@ -1328,22 +1328,19 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
) )
} }
result.sendResult(children as MutableList<MediaBrowserCompat.MediaItem>?) result.sendResult(children as MutableList<MediaBrowserCompat.MediaItem>?)
} } else if (shelf.type == "series") {
else if (shelf.type == "series") {
val children = (shelf as LibraryShelfSeriesEntity).entities?.map { librarySeriesItem -> val children = (shelf as LibraryShelfSeriesEntity).entities?.map { librarySeriesItem ->
val description = librarySeriesItem.getMediaDescription(null, ctx) val description = librarySeriesItem.getMediaDescription(null, ctx)
MediaBrowserCompat.MediaItem(description, MediaBrowserCompat.MediaItem.FLAG_BROWSABLE) MediaBrowserCompat.MediaItem(description, MediaBrowserCompat.MediaItem.FLAG_BROWSABLE)
} }
result.sendResult(children as MutableList<MediaBrowserCompat.MediaItem>?) result.sendResult(children as MutableList<MediaBrowserCompat.MediaItem>?)
} } else if (shelf.type == "authors") {
else if (shelf.type == "authors") {
val children = (shelf as LibraryShelfAuthorEntity).entities?.map { authorItem -> val children = (shelf as LibraryShelfAuthorEntity).entities?.map { authorItem ->
val description = authorItem.getMediaDescription(null, ctx) val description = authorItem.getMediaDescription(null, ctx)
MediaBrowserCompat.MediaItem(description, MediaBrowserCompat.MediaItem.FLAG_BROWSABLE) MediaBrowserCompat.MediaItem(description, MediaBrowserCompat.MediaItem.FLAG_BROWSABLE)
} }
result.sendResult(children as MutableList<MediaBrowserCompat.MediaItem>?) result.sendResult(children as MutableList<MediaBrowserCompat.MediaItem>?)
} } else {
else {
result.sendResult(mutableListOf()) result.sendResult(mutableListOf())
} }
@@ -1396,7 +1393,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
result.sendResult(children as MutableList<MediaBrowserCompat.MediaItem>?) result.sendResult(children as MutableList<MediaBrowserCompat.MediaItem>?)
} }
} }
}else if (mediaIdParts[3] == "SERIES_LIST") { } else if (mediaIdParts[3] == "SERIES_LIST") {
Log.d(tag, "Loading series from library ${mediaIdParts[2]}") Log.d(tag, "Loading series from library ${mediaIdParts[2]}")
mediaManager.loadLibrarySeriesWithAudio(mediaIdParts[2]) { seriesItems -> mediaManager.loadLibrarySeriesWithAudio(mediaIdParts[2]) { seriesItems ->
Log.d(tag, "Received ${seriesItems.size} series") Log.d(tag, "Received ${seriesItems.size} series")
@@ -1563,7 +1560,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
} }
result.sendResult(children as MutableList<MediaBrowserCompat.MediaItem>?) result.sendResult(children as MutableList<MediaBrowserCompat.MediaItem>?)
} }
}else { } else {
result.sendResult(null) result.sendResult(null)
} }
} else { } else {
@@ -1586,7 +1583,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() {
mediaManager.serverLibraries.forEach { serverLibrary -> mediaManager.serverLibraries.forEach { serverLibrary ->
runBlocking { runBlocking {
// Skip searching library if it doesn't have any audio files // Skip searching library if it doesn't have any audio files
if (serverLibrary.stats?.numAudioTracks == 0) return@runBlocking if (serverLibrary.stats?.numAudioFiles == 0) return@runBlocking
val searchResult = mediaManager.doSearch(serverLibrary.id, query) val searchResult = mediaManager.doSearch(serverLibrary.id, query)
for (resultData in searchResult.entries.iterator()) { for (resultData in searchResult.entries.iterator()) {
when (resultData.key) { when (resultData.key) {
@@ -155,7 +155,7 @@ class ApiHandler(var ctx:Context) {
fun getLibraries(cb: (List<Library>) -> Unit) { fun getLibraries(cb: (List<Library>) -> Unit) {
val mapper = jacksonMapper val mapper = jacksonMapper
getRequest("/api/libraries", null,null) { getRequest("/api/libraries?include=stats", null,null) {
val libraries = mutableListOf<Library>() val libraries = mutableListOf<Library>()
var array = JSONArray() var array = JSONArray()
@@ -172,18 +172,6 @@ class ApiHandler(var ctx:Context) {
} }
} }
fun getLibraryStats(libraryItemId:String, cb: (LibraryStats?) -> Unit) {
getRequest("/api/libraries/$libraryItemId/stats", null, null) {
if (it.has("error")) {
Log.e(tag, it.getString("error") ?: "getLibraryStats Failed")
cb(null)
} else {
val libraryStats = jacksonMapper.readValue<LibraryStats>(it.toString())
cb(libraryStats)
}
}
}
fun getLibraryPersonalized(libraryItemId:String, cb: (List<LibraryShelfType>?) -> Unit) { fun getLibraryPersonalized(libraryItemId:String, cb: (List<LibraryShelfType>?) -> Unit) {
getRequest("/api/libraries/$libraryItemId/personalized", null, null) { getRequest("/api/libraries/$libraryItemId/personalized", null, null) {
if (it.has("error")) { if (it.has("error")) {