straw/docs/sulkta/DECISIONS.md
Kayos ff4dc6f121 Sulkta day-1: straw — KMP/Compose YouTube client fork
Initial Sulkta fork of NewPipe with a new :strawApp module that ships a
clean Compose-based Android APK at applicationId com.sulkta.straw.

What's here:
- :strawApp — thin Android application shell, MaterialTheme + StrawHome
  Composable. Lives alongside legacy :app so we don't break upstream.
- buildSrc — STRAW_APPLICATION_ID/VERSION constants alongside the existing
  NEWPIPE_APPLICATION_ID_OLD/NEW.
- docs/sulkta — RECON.md (NewPipe codebase breakdown) + DECISIONS.md
  (stack + scope decisions).

NewPipe's :shared KMP scaffold is at 892 LOC and renders nothing; this
fork picks up there and races ahead. Day-1 ships a hello APK; day-2 wires
NewPipeExtractor + Media3 player + SponsorBlock + Return YouTube Dislike.

GPL-3.0-or-later per upstream.
2026-05-23 17:37:55 -07:00

4 KiB

NewPipe-fork decisions — 2026-05-23

Name: straw

  • Single short word, on-pattern with the rest of Sulkta's app family (cwho, tny, hawk, adhdo, tny).
  • Evokes "drawing content through a thin pipe" — fits the YouTube-extractor metaphor without leaning on the "pipe" word (NewPipe already owns that).
  • Easy to type, easy to read.

If Cobb hates it, alts: siphon, decant, funnel, kettle.

Approach: fork NewPipe master, rebrand, build on :shared

Not starting fresh. NewPipe has already done the gradle/CI/buildSrc/Koin/nav3 scaffold work — we'd be reinventing wheels to redo it. Forking lets us:

  • Keep upstream as a remote, cherry-pick their improvements as they land.
  • Build directly on :shared (the KMP module that has the scaffold but no features).
  • Keep the legacy :app module around as reference for working extractor wiring patterns (search, channel, video resolve, player setup). Eventually drop it once :shared is feature-complete.

Repo: Sulkta-Coop/straw on LAN Gitea

  • License: keep GPL-3.0-or-later (upstream's license, required for derivative).
  • Branches:
    • master — tracks upstream NewPipe master for clean rebase pulls.
    • sulkta — our work branch, default branch for clones.
  • Remote upstream pointing at github.com:TeamNewPipe/NewPipe.git for periodic merges.

App identity

Field Value
Application ID com.sulkta.straw (replaces net.newpipe.app)
App name "Straw" (debug suffix Straw debug)
Min SDK 24 (Android 7, NewPipe is 23 — minor bump for Media3)
Target SDK 35 (NewPipe matches)
Compile SDK 36 (NewPipe matches)
Java toolchain 21 (NewPipe matches)
Kotlin 2.3.21 (NewPipe matches)

Legacy :app (id org.schabi.newpipe) stays unchanged for now — we don't ship it but we read it.

Stack (matches NewPipe + 1 modernization)

  • UI: Jetpack Compose + Compose Multiplatform (commonMain Composables, androidMain ComposeActivity host).
  • Nav: androidx-navigation3 (type-safe sealed Screen interface).
  • DI: Koin 4.x with koin-plugin annotation processor (@KoinApplication, @Single, @KoinViewModel).
  • State: ViewModels + StateFlow.
  • HTTP for SponsorBlock + RYD: Ktor 3.x client (KMP-ready). NewPipe uses OkHttp; we use Ktor because we want SB/RYD clients to live in commonMain for future iOS/desktop.
  • Player: androidx.media3 1.4+ — NewPipe is still on legacy ExoPlayer 2.19.1. Media3 is the upstream-replacement, better lifecycle, better DASH.
  • Image loading: Coil 3 (NewPipe matches).
  • Serialization: kotlinx-serialization (NewPipe matches).
  • Extractor: NewPipeExtractor v0.26.2 via JitPack (NewPipe matches). JVM-only — lives in androidMain source set; commonMain declares an expected interface, androidMain wires the actual extractor.

What we don't try to do on day 1

  • iOS target. Extractor port is multi-week. iosApp stays a stub.
  • Desktop target. Same constraint; no point until extractor ported.
  • History/subscriptions persistence. Room is Android-only, fine for v0, but we want SQLDelight (KMP) eventually.
  • Settings. Use :shared/SettingsModule interface that already exists; Android impl backed by DataStore.
  • F-Droid metadata. Worry about it later.
  • Background audio + queue + autoplay. Phase 2 work.
  • DRM / age-gated content. Phase 2.

Day-1 deliverable scope

A working APK with:

  1. Search screen — type a query, list video results from YouTube. Tap opens video detail.
  2. Video detail screen — title, channel, description, RYD like/dislike row, "Play" button.
  3. Player screen — Media3 ExoPlayer playing the resolved video stream. SponsorBlock segments auto-skip during playback. Shows skip toast each time.
  4. Settings sketch — toggle for SponsorBlock categories (sponsor on by default).

That's the demo. No channel pages, subscriptions, history, downloads, PiP, background audio, kiosks, comments, related videos. Those are Phase 2+.