mirror of
https://github.com/advplyr/audiobookshelf-app.git
synced 2026-08-31 23:57:15 +02:00
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:
@@ -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)
|
||||||
|
|||||||
+9
-12
@@ -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")) {
|
||||||
|
|||||||
Reference in New Issue
Block a user