straw/rust
Cobb 791975ca4a
All checks were successful
build-apk / build-and-publish (push) Successful in 7m22s
gitleaks / scan (push) Successful in 44s
vc=86: audit-fix sprint (HIGH H2 + 5 MED/LOW from the 2026-06-21 audit)
- Audio-only toggle no longer drops the max-resolution cap: both the
  fullscreen button (PlayerScreen) and the detail Audio pill (VideoDetailBody)
  rebuilt TrackSelectionParameters from a fresh Builder, wiping the data-saver
  ceiling. Now buildUpon() the existing params so the cap survives. (H2)
- Subscriptions Refresh button no longer sticks at "..." forever on a warm
  restart within the cache TTL: refreshIfStale clears the initial loading
  seed when it decides nothing needs refreshing. (M2)
- Search + Channel result lists get a stable item key (video url) so paging /
  shorts-filtering stops re-binding rows to new data (re-triggered thumbnail
  loads, scroll shift). (M3, M4)
- IosSafeHttpDataSource: the unknown-length (LENGTH_UNSET) chunk path rolls
  forward to the next Range chunk at inner-EOF instead of re-reading the
  exhausted source forever (was truncating playback to the first chunk). (M5)
- strawcore channel_feed_rss propagates the real failure (network/HTTP/parse)
  instead of collapsing every error to an empty list, so a broken fetch is
  distinguishable from "no new videos" (subscription_feed keeps its per-channel
  tolerance for fan-out). (M6)
- Feed recency: a clock-skewed future upload emits "0 seconds ago" (parses to
  top) instead of "just now" (which Kotlin's recency parser couldn't read, so
  the item sank to the bottom). (L4)

Deferred to a follow-up: M1 (bg-refresh cache-key mismatch — needs a worker
redesign) + M7 (build config-cache wiring). Verified: cargo check/clippy +
full Android compileDebugKotlin green.
2026-06-21 13:37:51 -07:00
..
strawcore vc=86: audit-fix sprint (HIGH H2 + 5 MED/LOW from the 2026-06-21 audit) 2026-06-21 13:37:51 -07:00
Cargo.lock vc=85: image caching + SB/RYD clients -> Rust + crash/autoplay fixes 2026-06-21 12:59:04 -07:00
Cargo.toml Public-flip audit: scrub audit-ticket prefixes + LAN refs + tighten README 2026-05-27 13:29:53 -07:00
README.md Public-flip audit: scrub audit-ticket prefixes + LAN refs + tighten README 2026-05-27 13:29:53 -07:00

rust/ — strawcore: Rust YouTube core for Straw

Phase U- of the Straw build. Goal: replace the Java NewPipeExtractor dependency with a Rust core (rustypipe + tokio + reqwest), exposed to the Kotlin/Compose UI via UniFFI. Compose UI stays in Kotlin — only the YouTube/Innertube fetching layer moves to Rust.

Phases

Phase What
U-1 Toolchain + UniFFI smoke test (hello_from_rust) round-tripping through JNA. No real APIs yet.
U-2 rustypipe search. SearchViewModel calls the Rust core.
U-3 rustypipe streamInfo + streams. VideoDetailViewModel + PlayerViewModel use it.
U-4 rustypipe channel + tabs. ChannelViewModel + SubscriptionFeedViewModel.
U-5 Rip NewPipeExtractor Java dep. Measure APK + cold-fetch latency before/after.
U-6 (stretch) SponsorBlock + RYD HTTP through reqwest + tokio in the same lib.

Build chain

Build container (Sulkta uses one; any toolchain matching this layout works)
├── rustup stable (target add: aarch64-linux-android, armv7-linux-androideabi,
│                  x86_64-linux-android, i686-linux-android)
├── cargo-ndk      (cross-compile helper)
├── android-sdk    (ANDROID_HOME, sdkmanager, build-tools, platforms)
└── android-ndk    (ANDROID_NDK_HOME, r27c LTS)

Gradle (strawApp/build.gradle.kts)
├── cargoBuild         Exec task → cargo ndk -t <abi>... -o jniLibs/ build --release
├── uniffiBindgen      Exec task → cargo run --bin uniffi-bindgen ... --library libstrawcore.so
└── source-set wiring  generated Kotlin lands in strawApp/src/main/java/uniffi/strawcore/

Runtime (StrawApp.onCreate)
├── System.loadLibrary("strawcore")
└── uniffi.strawcore.initLogging()

Why UniFFI (and not raw JNI / JNA bindings)

  • Hand-written JNI: tedious, error-prone, every type change is two files (Kotlin + Rust) that must stay in sync.
  • Raw JNA: better, but you still hand-write the Kotlin side and worry about string ownership.
  • UniFFI: write Rust, annotate with #[uniffi::export], get a Kotlin shim generated. Strings, structs, enums, Result types, async functions all cross the boundary transparently. The runtime is JNA under the hood.

When in doubt

  • cargo check -p strawcore --target aarch64-linux-android — fast iteration.
  • cargo run --bin uniffi-bindgen -- generate ... — regenerate Kotlin bindings.
  • adb logcat -s strawcore — Rust log::info!() lands here.
  • aapt dump badging strawApp/build/outputs/apk/debug/strawApp-debug.apk — inspect what ABIs/native-libs the APK carries.