Commit graph

8 commits

Author SHA1 Message Date
42cb945654 Public-flip audit: scrub audit-ticket prefixes + LAN refs + tighten README
URLs → git.sulkta.com. Audit-ticket prefixes (SPEC §N, audit Track X, vc=N
audit-fix, FIX (audit ...), PORT DEVIATION) stripped from comments — technical
reasoning retained. Crafting-table LAN refs softened to 'Sulkta build host'.
README sheds marketing scaffolding + stale status tables.
2026-05-27 13:29:53 -07:00
5f2ba264b0 vc=68 fixup: enable reqwest 'stream' feature for bytes_stream 2026-05-26 20:56:24 -07:00
3a57696b46 vc=56 fixup: actually add reqwest+quick-xml+futures to Cargo.toml
Earlier Edit's old_string didn't match the file shape so the dep
additions never landed. Re-adding properly after android_logger.
2026-05-26 10:46:26 -07:00
10154c380b vc=42: loop round 4/5 — surgical cleanup, round 7 hit diminishing returns
Round-7 Opus audits explicitly flagged the audit loop as past
diminishing returns. Landing the few real items + one race-order
fix; leaving the comment-hygiene tail for the next time someone
reads the files.

MED
  R7-1  SearchViewModel cache-preview race: vc=41 wrote the cached
        preview to UI before cancelling the prior inFlight, so the
        prior coroutine (already past its ensureActive() gate)
        could reach its terminal _ui.update and clobber the cache
        preview. Moved inFlight?.cancel() above the preview write.
  R7-2  StrawActivity.YT_HOSTS duplicated util.YtUrl.ALLOWED_YT_HOSTS
        — drift risk where one gets a new host and the other
        doesn't. Collapsed StrawActivity.looksLikeYouTube to call
        util.isAllowedYtUrl, picking up the scheme + trailing-dot
        defenses for free.
  R7-3  Dropped dead once_cell dep from rust/strawcore/Cargo.toml.
        Round-4's runtime rewrite uses AtomicBool + Mutex; nothing
        consumes once_cell anymore.

Audit explicitly called out (and we agree) these as past the value
threshold for further rounds:
  - Verified-clean: try_lock no deadlock, ensureActive fence, bad-URL
    early-return cancel, duplicate-zip-entry rejection, LIMIT clauses,
    SponsorBlock 50ms exclusion drop, applied++ count.
  - False positive: H1 "Related videos always empty" — the section
    already gates on `d.related.isNotEmpty()`; UI doesn't paint when
    extractor stub returns empty.
  - Deferred: R8 enable, Nav rememberSaveable, LazyColumn keys,
    collectAsStateWithLifecycle, DASH/HLS max-resolution cap,
    Gradle cargo-build input/output declarations (CI cost, not
    runtime), legacy :app module trim, stale comment cleanup.
2026-05-25 15:36:00 -07:00
4e6a5dc929 Phase 7 fix — pin rquickjs-sys bindgen for Android cross-compile
rquickjs-sys 0.11 ships pre-generated bindings only for x86_64 hosts;
Android targets need bindgen at build time. Direct-dep with the feature
flag so cargo's feature resolver lifts it to the transitive use through
strawcore-core → rquickjs → rquickjs-sys.
2026-05-24 17:36:44 -07:00
467a5f10fa Phase 7 — strawcore wrapper now bridges to Sulkta-Coop/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 crafting-table 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.
2026-05-24 17:29:23 -07:00
7327de2843 v0.1.0-V (vc=9): U-3 — streamInfo via rustypipe drives VideoDetail+Player
stream_info(url) UniFFI suspend fn replaces NewPipeExtractor's
StreamInfo.getInfo() for both VideoDetailViewModel and PlayerViewModel.
One Rust round-trip drives the detail screen render AND the player's
resolve(). The VideoDetailUiState.info field cached on detail load is
reused by the Download dialog so we don't refetch.

Deferred to U-3.5:
- like_count (rustypipe's player() doesn't surface engagement data;
  a separate query is needed)
- related (player() doesn't include 'up next'; comes from a separate
  endpoint). Kotlin gets empty list for now — RelatedRow handles it.

Type quirks vs my initial guesses (caught by cargo check):
- details.duration is u32, not Option<u32>
- channel is split into channel_id + channel_name, not a struct
- like_count doesn't exist at this query depth
- VideoFormat::Webm (lowercase mb), VideoCodec::Avc1 (not H264)
- video_only is a separate vec (video_only_streams), not a bool flag
2026-05-24 08:52:43 -07:00
7ff5ac79e5 v0.1.0-U (vc=8): Phase U-1 + U-2 — Rust core + rustypipe search
NewPipeExtractor (Java) → strawcore (Rust) migration begins. Phase U:
- U-1: Rust toolchain + UniFFI smoke test
- U-2: rustypipe search via uniffi suspend fun, SearchViewModel swapped

What landed:
- rust/strawcore — UniFFI-exported Rust crate using proc-macros.
  Builds for arm64-v8a + armeabi-v7a + x86 + x86_64 via cargo-ndk.
  Tokio multi-thread runtime singleton drives rustypipe's async API.
- strawApp/build.gradle.kts — cargoBuildHost + cargoBuild + uniffiBindgen
  Gradle Exec tasks chained into the Android build. Generated Kotlin
  bindings land in src/main/java/uniffi/strawcore/ (gitignored).
- SearchViewModel.kt — calls uniffi.strawcore.search(query) directly.
  NewPipeExtractor still in deps for VideoDetail/Player/Channel paths;
  those move to Rust in U-3 / U-4.
- Build chain quirks beat:
  * cargo absolute path in Exec tasks (PATH wasn't propagating)
  * uniffi-bindgen needs UNSTRIPPED host .so — separate cargoBuildHost
    builds a debug-profile host lib to read metadata from
  * rustypipe rustls-tls-webpki-roots avoids the openssl-sys
    cross-compile tarpit
  * rquickjs-sys 'bindgen' feature opted in (no prebuilt Android
    bindings ship; crafting-table has libclang 14)
- crafting-table runtime install (until Dockerfile catches up):
  rustup + 4 Android targets + cargo-ndk + NDK r27c. Persists in
  /caches/cargo + /caches/android-sdk via the volume mount.

APK size: 22MB (U-1) → 37MB (U-2). libstrawcore.so 3-5MB per ABI carries
rustypipe + reqwest + tokio + rustls + rquickjs. NewPipeExtractor still
in for now (still drives detail + player + channel + feed), so the
Java half is doubled up. U-5 removes it.
2026-05-24 08:36:50 -07:00