From f47ddfa01cd72be35fa87183fb10459154ada3b9 Mon Sep 17 00:00:00 2001 From: KhooLy <73142442+KhooLy@users.noreply.github.com> Date: Tue, 14 Jul 2026 15:25:35 +0300 Subject: [PATCH] Fetch home bootstrap catalog rows concurrently readHomeBootstrap awaited each addon catalog request sequentially, causing 7-8s cold-start latency with multiple addons/genres. Applies the same async/Semaphore(8)/awaitAll fan-out already used by runDiscover for genre switches. --- .../rust/FluxaAndroidHeadlessEnvironment.kt | 51 ++++++++++--------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/com/fluxa/app/core/rust/FluxaAndroidHeadlessEnvironment.kt b/app/src/main/java/com/fluxa/app/core/rust/FluxaAndroidHeadlessEnvironment.kt index 4174e0c..c83183d 100644 --- a/app/src/main/java/com/fluxa/app/core/rust/FluxaAndroidHeadlessEnvironment.kt +++ b/app/src/main/java/com/fluxa/app/core/rust/FluxaAndroidHeadlessEnvironment.kt @@ -579,7 +579,7 @@ class FluxaAndroidHeadlessEnvironment @Inject constructor( return ok(effect, value) } - private suspend fun readHomeBootstrap(effect: NativeHeadlessEffect): HeadlessEffectCompletion { + private suspend fun readHomeBootstrap(effect: NativeHeadlessEffect): HeadlessEffectCompletion = coroutineScope { val profile = effect.payload.profile() profile?.id?.let(watchlistManager::setActiveProfile) val addons = addonRepository.getUserAddons(profile?.authKey.orEmpty(), profile?.safeLocalAddons.orEmpty()) @@ -590,29 +590,34 @@ class FluxaAndroidHeadlessEnvironment @Inject constructor( val selectedKeys = effectiveHomeMetadataFeedSelection(profile?.homeFeedToggles, availableKeys) feeds.filter { isMetadataFeedEnabled(selectedKeys, it.key) } } - val categories = metadataFeeds.mapNotNull { feed -> - val items = runCatching { - addonRepository.getAddonCatalog( - transportUrl = feed.transportUrl, - type = feed.type, - id = feed.id, - genre = feed.genre - ) - }.getOrDefault(emptyList()) - if (items.isEmpty()) null else { - HomeCategory( - name = feed.label, - semanticName = feed.label, - items = items, - id = feed.key, - type = feed.type, - catalogId = feed.id, - addonTransportUrl = feed.transportUrl, - addonGenre = feed.genre - ) + val semaphore = Semaphore(8) + val categories = metadataFeeds.map { feed -> + async { + val items = semaphore.withPermit { + runCatching { + addonRepository.getAddonCatalog( + transportUrl = feed.transportUrl, + type = feed.type, + id = feed.id, + genre = feed.genre + ) + }.getOrDefault(emptyList()) + } + if (items.isEmpty()) null else { + HomeCategory( + name = feed.label, + semanticName = feed.label, + items = items, + id = feed.key, + type = feed.type, + catalogId = feed.id, + addonTransportUrl = feed.transportUrl, + addonGenre = feed.genre + ) + } } - } - return ok( + }.awaitAll().filterNotNull() + ok( effect, mapOf( "categories" to categories,