mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-09 16:11:20 +02:00
Fix: suggestions logic/re-implement logic into worker and address performance issues (#834)
## Description Addresses suggestion logic oversight in recently merged suggestions fix, and improve performance (especially app homepage startup time) - Re-implements the logic that sorts/categorizes tailored suggestions based on the seed items that are queried. This was implemented in the original PR at first, but I overlooked moving that logic - Removes `OneTimeWorkRequestBuilder` and replaces with trigger in `PeriodicWorkRequest` to fire with a 30 seconds delay if cache is empty. ### Related issues Fixes #833 ### Testing Will be testing in: Android TV emulator in Android Studio NVIDIA Shield TV Pro 2019 ## AI or LLM usage I will use AI to help with writing the tests/testing services.
This commit is contained in:
parent
abf5ec8004
commit
8e9a6799f5
4 changed files with 186 additions and 26 deletions
|
|
@ -7,7 +7,6 @@ import androidx.work.BackoffPolicy
|
|||
import androidx.work.Constraints
|
||||
import androidx.work.ExistingPeriodicWorkPolicy
|
||||
import androidx.work.NetworkType
|
||||
import androidx.work.OneTimeWorkRequestBuilder
|
||||
import androidx.work.PeriodicWorkRequestBuilder
|
||||
import androidx.work.WorkManager
|
||||
import androidx.work.workDataOf
|
||||
|
|
@ -23,6 +22,7 @@ import java.util.UUID
|
|||
import javax.inject.Inject
|
||||
import kotlin.time.Duration.Companion.hours
|
||||
import kotlin.time.Duration.Companion.minutes
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
import kotlin.time.toJavaDuration
|
||||
|
||||
@ActivityScoped
|
||||
|
|
@ -72,29 +72,26 @@ class SuggestionsSchedulerService
|
|||
SuggestionsWorker.PARAM_SERVER_ID to serverId.toString(),
|
||||
)
|
||||
|
||||
val periodicWorkRequestBuilder =
|
||||
PeriodicWorkRequestBuilder<SuggestionsWorker>(
|
||||
repeatInterval = 12.hours.toJavaDuration(),
|
||||
).setConstraints(constraints)
|
||||
.setBackoffCriteria(
|
||||
BackoffPolicy.EXPONENTIAL,
|
||||
15.minutes.toJavaDuration(),
|
||||
).setInputData(inputData)
|
||||
|
||||
if (cache.isEmpty()) {
|
||||
Timber.i("Suggestions cache empty, scheduling immediate fetch")
|
||||
workManager.enqueue(
|
||||
OneTimeWorkRequestBuilder<SuggestionsWorker>()
|
||||
.setConstraints(constraints)
|
||||
.setInputData(inputData)
|
||||
.build(),
|
||||
)
|
||||
Timber.i("Suggestions cache empty, scheduling periodic fetch with 30s delay")
|
||||
periodicWorkRequestBuilder.setInitialDelay(30.seconds.toJavaDuration())
|
||||
} else {
|
||||
Timber.i("Scheduling periodic SuggestionsWorker")
|
||||
}
|
||||
|
||||
Timber.i("Scheduling periodic SuggestionsWorker")
|
||||
workManager.enqueueUniquePeriodicWork(
|
||||
uniqueWorkName = SuggestionsWorker.WORK_NAME,
|
||||
existingPeriodicWorkPolicy = ExistingPeriodicWorkPolicy.UPDATE,
|
||||
request =
|
||||
PeriodicWorkRequestBuilder<SuggestionsWorker>(
|
||||
repeatInterval = 12.hours.toJavaDuration(),
|
||||
).setConstraints(constraints)
|
||||
.setBackoffCriteria(
|
||||
BackoffPolicy.EXPONENTIAL,
|
||||
15.minutes.toJavaDuration(),
|
||||
).setInputData(inputData)
|
||||
.build(),
|
||||
request = periodicWorkRequestBuilder.build(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -136,6 +136,10 @@ class SuggestionsWorker
|
|||
val isSeries = itemKind == BaseItemKind.SERIES
|
||||
val historyItemType = if (isSeries) BaseItemKind.EPISODE else itemKind
|
||||
|
||||
val contextualLimit = (itemsPerRow * 0.4).toInt().coerceAtLeast(1)
|
||||
val randomLimit = (itemsPerRow * 0.3).toInt().coerceAtLeast(1)
|
||||
val freshLimit = (itemsPerRow * 0.3).toInt().coerceAtLeast(1)
|
||||
|
||||
val historyDeferred =
|
||||
async(Dispatchers.IO) {
|
||||
fetchItems(
|
||||
|
|
@ -144,11 +148,38 @@ class SuggestionsWorker
|
|||
itemKind = historyItemType,
|
||||
sortBy = ItemSortBy.DATE_PLAYED,
|
||||
isPlayed = true,
|
||||
limit = 10,
|
||||
limit = 20,
|
||||
extraFields = listOf(ItemFields.GENRES),
|
||||
).distinctBy { it.relevantId }.take(3)
|
||||
}
|
||||
|
||||
val seedItems = historyDeferred.await()
|
||||
|
||||
val allGenreIds =
|
||||
seedItems
|
||||
.flatMap { it.genreItems?.mapNotNull { g -> g.id } ?: emptyList() }
|
||||
.distinct()
|
||||
|
||||
val excludeIds = seedItems.mapTo(HashSet()) { it.relevantId }
|
||||
|
||||
val contextualDeferred =
|
||||
async(Dispatchers.IO) {
|
||||
if (allGenreIds.isEmpty()) {
|
||||
emptyList()
|
||||
} else {
|
||||
fetchItems(
|
||||
parentId = parentId,
|
||||
userId = userId,
|
||||
itemKind = itemKind,
|
||||
sortBy = ItemSortBy.RANDOM,
|
||||
isPlayed = false,
|
||||
limit = contextualLimit,
|
||||
genreIds = allGenreIds,
|
||||
excludeItemIds = excludeIds.toList(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val randomDeferred =
|
||||
async(Dispatchers.IO) {
|
||||
fetchItems(
|
||||
|
|
@ -157,7 +188,7 @@ class SuggestionsWorker
|
|||
itemKind = itemKind,
|
||||
sortBy = ItemSortBy.RANDOM,
|
||||
isPlayed = false,
|
||||
limit = itemsPerRow,
|
||||
limit = randomLimit,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -170,17 +201,15 @@ class SuggestionsWorker
|
|||
sortBy = ItemSortBy.DATE_CREATED,
|
||||
sortOrder = SortOrder.DESCENDING,
|
||||
isPlayed = false,
|
||||
limit = (itemsPerRow * FRESH_CONTENT_RATIO).toInt().coerceAtLeast(1),
|
||||
limit = freshLimit,
|
||||
)
|
||||
}
|
||||
|
||||
val seedItems = historyDeferred.await()
|
||||
val contextual = contextualDeferred.await()
|
||||
val random = randomDeferred.await()
|
||||
val fresh = freshDeferred.await()
|
||||
|
||||
val excludeIds = seedItems.mapTo(HashSet()) { it.relevantId }
|
||||
|
||||
(fresh + random)
|
||||
(contextual + fresh + random)
|
||||
.asSequence()
|
||||
.distinctBy { it.id }
|
||||
.filterNot { excludeIds.contains(it.relevantId) }
|
||||
|
|
@ -198,6 +227,7 @@ class SuggestionsWorker
|
|||
limit: Int,
|
||||
sortOrder: SortOrder? = null,
|
||||
genreIds: List<UUID>? = null,
|
||||
excludeItemIds: List<UUID>? = null,
|
||||
extraFields: List<ItemFields> = emptyList(),
|
||||
): List<BaseItemDto> {
|
||||
val request =
|
||||
|
|
@ -209,6 +239,7 @@ class SuggestionsWorker
|
|||
genreIds = genreIds,
|
||||
recursive = true,
|
||||
isPlayed = isPlayed,
|
||||
excludeItemIds = excludeItemIds,
|
||||
sortBy = listOf(sortBy),
|
||||
sortOrder = sortOrder?.let { listOf(it) },
|
||||
limit = limit,
|
||||
|
|
@ -225,6 +256,5 @@ class SuggestionsWorker
|
|||
const val WORK_NAME = "com.github.damontecres.wholphin.services.SuggestionsWorker"
|
||||
const val PARAM_USER_ID = "userId"
|
||||
const val PARAM_SERVER_ID = "serverId"
|
||||
private const val FRESH_CONTENT_RATIO = 0.4
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue