diff --git a/buildSrc/src/main/kotlin/ProjectConfig.kt b/buildSrc/src/main/kotlin/ProjectConfig.kt index e4f73c6c8..b65da1030 100644 --- a/buildSrc/src/main/kotlin/ProjectConfig.kt +++ b/buildSrc/src/main/kotlin/ProjectConfig.kt @@ -55,6 +55,6 @@ const val NEWPIPE_APPLICATION_ID_NEW = "net.newpipe.app" // vc=19 / 0.1.0-AE — rust pipeline cutover. Extraction via // strawcore-core (Sulkta-Coop/strawcore) via the UniFFI wrapper; no // NewPipeExtractor in the runtime path. -const val STRAW_VERSION_CODE = 57 -const val STRAW_VERSION_NAME = "0.1.0-BQ" +const val STRAW_VERSION_CODE = 58 +const val STRAW_VERSION_NAME = "0.1.0-BR" const val STRAW_APPLICATION_ID = "com.sulkta.straw" diff --git a/strawApp/src/main/kotlin/com/sulkta/straw/feature/detail/VideoDetailViewModel.kt b/strawApp/src/main/kotlin/com/sulkta/straw/feature/detail/VideoDetailViewModel.kt index 45336a6bc..f20864efa 100644 --- a/strawApp/src/main/kotlin/com/sulkta/straw/feature/detail/VideoDetailViewModel.kt +++ b/strawApp/src/main/kotlin/com/sulkta/straw/feature/detail/VideoDetailViewModel.kt @@ -29,6 +29,8 @@ import com.sulkta.straw.util.runCatchingCancellable import kotlinx.coroutines.CancellationException import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job +import kotlinx.coroutines.async +import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow @@ -160,13 +162,24 @@ class VideoDetailViewModel : ViewModel() { } } - val ryd = withContext(Dispatchers.IO) { - runCatchingCancellable { RydClient.fetch(videoId) }.getOrNull() - } + // RYD + SponsorBlock in parallel — both are independent + // network round-trips that block the detail UI. Running + // them sequentially via two withContext blocks left the + // slower one fully serialized behind the faster one + // (~200-500ms wasted per video open). async{}.await() + // on Dispatchers.IO closes that gap. val sbCats = Settings.get().sbCategories.value.map { it.key } - val segments = if (sbCats.isEmpty()) emptyList() else withContext(Dispatchers.IO) { - runCatchingCancellable { SponsorBlockClient.fetch(videoId, sbCats) } - .getOrDefault(emptyList()) + val (ryd, segments) = coroutineScope { + val rydDeferred = async(Dispatchers.IO) { + runCatchingCancellable { RydClient.fetch(videoId) }.getOrNull() + } + val sbDeferred = async(Dispatchers.IO) { + if (sbCats.isEmpty()) emptyList() + else runCatchingCancellable { + SponsorBlockClient.fetch(videoId, sbCats) + }.getOrDefault(emptyList()) + } + rydDeferred.await() to sbDeferred.await() } val related = info.related.map { r ->