straw/rust/strawcore/src/lib.rs
Sulkta a36e673627 vc=85: image caching + SB/RYD clients -> Rust + crash/autoplay fixes
- Thumbnails + channel icons stay cached: pin an explicit 256MB Coil disk
  cache + sized memory cache via SingletonImageLoader.Factory. Coil's
  default disk cap is 2% of the device's free space, so on a storage-tight
  phone the subs feed (most image-heavy screen) thrashed it and
  re-downloaded thumbnails on every visit.
- SponsorBlock + Return-YouTube-Dislike clients moved Kotlin -> Rust
  (strawcore net.rs: fetchSponsorSegments / fetchRydVotes). SponsorBlock
  keeps its privacy-preserving SHA-256 hash-prefix lookup. Kotlin is now a
  thin shim mapping the FFI records onto the SbSegment/RydVotes domain
  types; behavior identical. Migration #2 of "all backend -> Rust".
- Fix crash: extract_channel_id sliced the channel URL by a length derived
  from a lowercased copy of itself; to_lowercase() can change byte length
  on non-ASCII, so a non-ASCII URL tail could panic across the FFI and
  abort the app on a feed refresh. Now matches the prefix case-insensitively
  against the original with length + char-boundary guards.
- Fix autoplay hijack: advancing to the next video resolves over ~500ms; if
  you manually start a different video meanwhile, autoplay would replace
  your choice with the stale next-up. Added a staleness fence.

Verified: cargo check/test/clippy on the wrapper, full Android
compileDebugKotlin green, adversarial FFI pre-push audit passed.
2026-06-21 12:59:04 -07:00

58 lines
1.9 KiB
Rust

// strawcore (wrapper) — UniFFI surface for the Straw Android app.
//
// Thin layer over the new Sulkta-OSS/strawcore-core crate. All extractor
// logic (InnerTube, JS deobf, stream parsing, search, channel, playlist)
// lives in core. This file:
// * re-exports the DTOs Kotlin expects under their familiar names
// * exposes #[uniffi::export] async fns that bridge Kotlin suspend funs
// to the core's blocking calls via tokio::task::spawn_blocking
// * owns init_logging() — also initializes the core Downloader
use std::sync::Once;
mod channel;
mod error;
mod feed;
mod net;
mod runtime;
mod search;
mod stream;
// Re-exports so UniFFI sees the types at the crate root for macro discovery.
pub use channel::ChannelInfo;
pub use error::StrawcoreError;
pub use net::{RydVotes, SponsorSegment};
pub use search::{Page, SearchItem};
pub use stream::{AudioStreamItem, ResolvedStreams, StreamInfo, VideoStreamItem};
/// Initialize Android logging + the strawcore-core HTTP downloader.
/// Kotlin calls this from StrawApp.onCreate(). Idempotent.
#[uniffi::export]
pub fn init_logging() {
static ONCE: Once = Once::new();
ONCE.call_once(|| {
android_logger::init_once(
android_logger::Config::default()
.with_max_level(log::LevelFilter::Info)
.with_tag("strawcore"),
);
log::info!("strawcore initialized");
});
runtime::ensure_initialized();
}
/// Smoke-test entry point — round-trip a string through JNI.
/// Used during the initial UniFFI bring-up; kept for future smoke
/// debugging. Logs shape only — the `name` value never hits logcat
/// because a future caller might pass a real user-supplied string.
#[uniffi::export]
pub fn hello_from_rust(name: String) -> String {
log::info!("hello_from_rust called name_len={}", name.len());
format!(
"hello {} from rust 🦀 (strawcore v{})",
name,
env!("CARGO_PKG_VERSION")
)
}
uniffi::setup_scaffolding!();