- 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. |
||
|---|---|---|
| .forgejo/workflows | ||
| .github | ||
| .idea | ||
| assets | ||
| buildSrc | ||
| checkstyle | ||
| ci | ||
| doc | ||
| docs/sulkta | ||
| fastlane/metadata/android | ||
| gradle | ||
| iosApp | ||
| rust | ||
| strawApp | ||
| .editorconfig | ||
| .gitignore | ||
| .gitleaks.toml | ||
| build.gradle.kts | ||
| gradle.properties | ||
| gradlew | ||
| gradlew.bat | ||
| LICENSE | ||
| README.md | ||
| settings.gradle.kts | ||
Straw
A Sulkta fork of NewPipe. Android YouTube client, Compose UI, Media3 player, with SponsorBlock and Return YouTube Dislike baked in.
The extractor is strawcore, a Rust port of NewPipeExtractor exposed to Kotlin
via UniFFI. No InnerTube/JS deobf code path lives on the JVM anymore.
Install
F-Droid repo: https://fdroid.sulkta.com/fdroid/repo
Add the repo in your F-Droid client of choice, then install Straw.
The app also self-updates from the same repo when an APK lands there with a
higher versionCode.
What's in
- Search, video detail, channel pages, playlists
- Inline player + fullscreen + minibar + background audio + PiP
- Media3 ExoPlayer (DASH / HLS / progressive / merged DASH chunks)
- SponsorBlock auto-skip (categories user-toggleable)
- Return YouTube Dislike on video detail
- RSS-based subscription feed (fast — ~1s for 50 subs)
- Hide-shorts / hide-paid / hide-age-restricted feed filters
- Resume positions + watch history + search history
- Local playlists, downloads (video + audio)
- NewPipe-format settings import (subs + playlists + history)
What's out (on purpose)
- Trending / algorithmic feeds. Subscriptions only.
- iOS / desktop targets. Android-only for now.
- Google Play Services anything.
Layout
strawApp/ Sulkta-authored app — Compose UI, Media3 wiring, SB + RYD clients
rust/ strawcore — UniFFI wrapper around the Rust extractor
shared/ KMP scaffold inherited from upstream NewPipe (unused for now)
app/ Upstream NewPipe :app module — kept for reference
Build
./gradlew :strawApp:assembleDebug
Requires the Rust toolchain plus the four Android targets:
rustup target add aarch64-linux-android armv7-linux-androideabi \
x86_64-linux-android i686-linux-android
cargo install cargo-ndk uniffi-bindgen
…and ANDROID_NDK_HOME pointing at NDK r27c (or newer). The Gradle build runs
cargo ndk + uniffi-bindgen automatically.
License
GPL-3.0-or-later, inherited from upstream NewPipe.
Upstream
This repo tracks https://github.com/TeamNewPipe/NewPipe. Upstream changes
get pulled periodically via the upstream remote.
Disclaimer
Not affiliated with YouTube, Google, NewPipe e.V., the SponsorBlock project, or Return YouTube Dislike. Trademarks belong to their owners. Straw uses public web endpoints; nothing here authenticates to any account.