Phase 7 — strawcore wrapper now bridges to Sulkta-OSS/strawcore-core

Replaces the rustypipe-backed extraction with calls into the new
NPE-port crate. The UniFFI surface Kotlin sees is unchanged:

  suspend fun search(query: String): List<SearchItem>
  suspend fun streamInfo(input: String): StreamInfo
  suspend fun channelInfo(input: String): ChannelInfo
  fun initLogging()  // also wires the strawcore-core Downloader
  fun helloFromRust(name: String): String

rust/strawcore/
  * Cargo.toml      — dropped rustypipe + rquickjs-sys direct dep;
                      added strawcore-core path dep (../../../strawcore)
  * src/error.rs    — From<strawcore_core::ExtractionError>, mapping
                      ContentUnavailable variants to typed
                      StrawcoreError cases (AgeRestricted, GeoRestricted,
                      Private, RequiresLogin) instead of bucketing all
                      to Extractor
  * src/runtime.rs  — Once-guarded ReqwestDownloader init via
                      NewPipe::init_full
  * src/search.rs   — search() spawn_blocks core search_extractor::search
                      against SearchFilter::Videos
  * src/stream.rs   — stream_info() resolves URL → video_id via
                      strawcore_core::linkhandler::stream, then
                      spawn_blocks core stream_extractor::stream_info,
                      then maps StreamInfo → wrapper DTOs (combined/
                      video_only/audio_only/dash/hls)
  * src/channel.rs  — channel_info() parses input via
                      strawcore_core::linkhandler::channel (handle /
                      custom-url / legacy-user resolution lives in
                      core), then spawn_blocks core channel::channel_info

Build verified: wrapper compiles linking strawcore-core, uniffi-bindgen
generates Kotlin bindings with the same suspend fun + data class
surface Kotlin already consumes. Android NDK cross-compile + APK + on-
device smoke pending (needs build-host container).

This commits onto rollback/vc18-back-to-NPE — the existing Kotlin code
still calls NewPipeExtractor directly. Switching the Kotlin side to
consume the rust wrapper is a separate cutover.
This commit is contained in:
Sulkta 2026-05-24 17:29:23 -07:00
parent c1d7fffb1f
commit fb89b22685
7 changed files with 250 additions and 309 deletions

View file

@ -17,22 +17,19 @@ crate-type = ["cdylib", "staticlib"]
# `tokio` feature wires `#[uniffi::export(async_runtime = "tokio")]` so async
# fns surface as suspend fun on the Kotlin side.
uniffi = { version = "0.28", features = ["cli", "tokio"] }
# Tokio multi-thread runtime — rustypipe is async-first.
# Tokio multi-thread runtime — needed to host spawn_blocking around the
# blocking strawcore_core HTTP calls so Kotlin `suspend fun` semantics
# survive.
tokio = { version = "1", features = ["rt-multi-thread", "macros", "sync"] }
# rustypipe — the actual YouTube Innertube client. Phase U-2 wires search.
# Force rustls + webpki-roots so we don't pull openssl-sys (cross-compiling
# system OpenSSL to four Android ABIs is a tarpit; rustls is pure-Rust).
rustypipe = { version = "0.11", default-features = false, features = ["rustls-tls-webpki-roots"] }
# rquickjs-sys (transitive dep of rustypipe for YT signature decryption JS)
# doesn't ship prebuilt Android bindings. Direct-depend with `bindgen` feature
# so it generates them at build time. The build host has libclang preinstalled.
rquickjs-sys = { version = "0.9", default-features = false, features = ["bindgen"] }
# The actual YT extractor lives in Sulkta-OSS/strawcore. Renamed locally
# to `strawcore_core` to avoid collision with this wrapper crate's name
# (which has to stay `strawcore` so the .so name + Kotlin loadLibrary
# call keep working).
strawcore-core = { path = "../../../strawcore" }
# Error glue.
thiserror = "1"
# Single-threaded init for the runtime + extractor singletons.
once_cell = "1"
# URL parsing for the video-id extractor in stream.rs.
url = { workspace = true }
# Android log integration — `log::info!()` ends up in `adb logcat -s strawcore`.
log = "0.4"
android_logger = { version = "0.14", default-features = false }