vc=84 — rename launcher to 'Straw' + move stream picker into Rust

Two changes:

1. Launcher name is now just 'Straw', not 'Straw debug' — past the
   debug-branding phase. Kept the .debug applicationId suffix (package
   stays com.sulkta.straw.debug) on purpose so fdroid updates install in
   place and the in-app auto-updater keeps working; dropping the suffix
   would change the package id and force a reinstall that wipes everyone's
   subs/history. That's a separate, deliberate release-track cutover.

2. Stream-selection logic moved out of Kotlin (resolveStreamPlayback) into
   the Rust strawcore wrapper as resolve_playback(StreamInfo, max_height)
   -> ResolvedStreams. The app keeps a thin shim that supplies the
   resolution cap (Settings.maxResolution) and attaches SponsorBlock
   segments. Byte-for-byte behavior parity with the old Kotlin picker:
   highest-bitrate stream at/under the cap, lowest-height fallback when
   nothing fits, first-element-wins on ties (matching Kotlin
   maxByOrNull/minByOrNull, not Rust's last-on-ties max_by_key), and
   isNotBlank() handling for the DASH/HLS URLs. First step of moving all
   backend logic to Rust; the picking lives at the FFI boundary because it
   depends on an app setting, keeping strawcore-core a pure extractor.

Wrapper cargo check + clippy clean (no new warnings); FFI surface adds
ResolvedStreams + resolvePlayback, bindings regen at build.
This commit is contained in:
Sulkta 2026-06-21 11:41:50 -07:00
parent 1531d41742
commit 4affa1e739
5 changed files with 127 additions and 20 deletions

View file

@ -67,8 +67,15 @@ configure<ApplicationExtension> {
// kotlinx-serialization companions.
debug {
isDebuggable = true
// Keep the `.debug` applicationId suffix (package stays
// com.sulkta.straw.debug) so in-place fdroid updates + the in-app
// auto-updater keep working and nobody loses their subs/history.
// But present as just "Straw" in the launcher — we're past the
// debug-phase branding. Dropping the suffix entirely is a
// separate, deliberate release-track cutover (it'd change the
// package id → force a reinstall + wipe app data).
applicationIdSuffix = ".debug"
resValue("string", "app_name", "Straw debug")
resValue("string", "app_name", "Straw")
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(

View file

@ -32,6 +32,11 @@ fun extractYtVideoId(url: String): String? =
* possible, otherwise the closest-to-cap fallback (lowest height) so
* we don't blow a user's data plan when only above-cap streams exist.
*
* The stream-selection logic now lives in Rust (strawcore
* `resolvePlayback`) this is a thin shim that supplies the user's
* resolution ceiling and attaches the SponsorBlock `segments` (a
* separate, app-side concern) on top of the picked URLs.
*
* `segments` is the SponsorBlock list to bake into the resulting
* ResolvedPlayback; pass emptyList() when no SB is desired (the queue
* path doesn't pre-fetch SB for queued items).
@ -40,23 +45,17 @@ fun resolveStreamPlayback(
info: uniffi.strawcore.StreamInfo,
segments: List<SbSegment> = emptyList(),
): ResolvedPlayback {
val maxRes = Settings.get().maxResolution.value.ceiling
fun pickVideo(streams: List<uniffi.strawcore.VideoStreamItem>): String? {
if (streams.isEmpty()) return null
val capped = streams.filter { it.height <= maxRes }
return if (capped.isNotEmpty()) {
capped.maxByOrNull { it.bitrate }?.url
} else {
streams.minByOrNull { it.height }?.url
}
}
val picked = uniffi.strawcore.resolvePlayback(
info,
Settings.get().maxResolution.value.ceiling,
)
return ResolvedPlayback(
title = info.title,
videoUrl = pickVideo(info.videoOnly),
audioUrl = info.audioOnly.maxByOrNull { it.bitrate }?.url,
combinedUrl = pickVideo(info.combined),
dashMpdUrl = info.dashMpdUrl?.takeIf { it.isNotBlank() },
hlsUrl = info.hlsUrl?.takeIf { it.isNotBlank() },
title = picked.title,
videoUrl = picked.videoUrl,
audioUrl = picked.audioUrl,
combinedUrl = picked.combinedUrl,
dashMpdUrl = picked.dashMpdUrl,
hlsUrl = picked.hlsUrl,
segments = segments,
)
}