straw/docs/sulkta/DAY2.md
Kayos 496ed30bda Sulkta day-2: search → detail → player → SponsorBlock + RYD
Phase A: NewPipeExtractor + OkHttp Downloader wired in. Search bar +
LazyColumn results. Tap = navigate to detail.

Phase B: VideoDetail screen — StreamInfo metadata + Return YouTube
Dislike chips + description.

Phase C: Media3 ExoPlayer in Compose. Resolves StreamInfo to best
playable: DASH MPD → HLS → combined progressive → merged
videoOnly+audio.

Phase D: SponsorBlock SHA-256 prefix lookup. 250ms position-poll loop
inside PlayerScreen — exoPlayer.seekTo(segment.end) when entering
a sponsor segment. Toast on skip.

Phase E: Verified live on Android 14 emulator. linus tech tips search
returns real results with thumbnails; tapped result opens detail;
hit Play → video plays through ExoPlayer.

Architecture: everything in :strawApp for now (not pushed into :shared
yet — KMP refactor is day-3). Pure-state nav (sealed Screen + stack,
no nav library).

Known polish gaps (day-3): RYD chips render empty on some videos,
description has raw HTML (markdown render needed), no Koin DI yet,
no persistence.

GPL-3.0-or-later per upstream NewPipe.
2026-05-23 19:22:52 -07:00

3.6 KiB

Straw day-2 — search → detail → player → SponsorBlock + RYD

Loop pass through phases A → E in one session. Built on the day-1 :strawApp module without touching :shared or :app.

What landed

Phase Component Location
A Search bar + LazyColumn results feature/search/Search{ViewModel,Screen}.kt
A NewPipeExtractor + OkHttp Downloader extractor/NewPipeDownloader.kt, StrawApp.kt
B Video detail screen + RYD chips feature/detail/VideoDetail{ViewModel,Screen}.kt
B Return YouTube Dislike client net/RydClient.kt
C Media3 ExoPlayer (DASH / HLS / progressive / merge fallback) feature/player/PlayerScreen.kt
C Player view-model resolves StreamInfo feature/player/PlayerViewModel.kt
D SponsorBlock client (SHA-256 prefix lookup) net/SponsorBlockClient.kt
D Auto-skip via position-poll loop inside PlayerScreen.kt
E Repo committed, APK rebuilt this commit

Navigation

Home → Search → VideoDetail → Player.

Pure-state nav (sealed Screen + Navigator, no nav library). Back button unwinds the stack; falling off the root exits the app. Day-3 will switch to androidx-navigation3 to match upstream's KMP scaffold.

Architecture notes

  • All code lives in :strawApp for now — not pushed into :shared. The KMP-skill canonical shape would be domain/data/presentation per feature in :shared/commonMain. We'll refactor later; day-2 is about shipping the vertical slice fast.
  • No DI yet — ViewModels are viewModel()-constructed with no constructor args. Day-3 will introduce Koin for the OkHttp client, RydClient, SponsorBlockClient.
  • No persistence — search history, watch history, subs all live in memory and die on app close. Day-3 = Room/DataStore.
  • No iOS/Desktop:strawApp is Android-only because NewPipeExtractor is JVM-only. KMP-ification of the extractor is a multi-week project upstream is presumably already eyeing.

Player path

NewPipeExtractor returns a StreamInfo with a few possible playback shapes. We try them in this preference order:

  1. DASH MPD URLinfo.dashMpdUrlDashMediaSource. Best quality when YouTube serves it.
  2. HLS URLinfo.hlsUrlHlsMediaSource. Mostly for live streams.
  3. Combined video+audioinfo.videoStreams.maxByBitrateProgressiveMediaSource. Rare on modern YouTube; older clients only.
  4. Merged DASH-chunksinfo.videoOnlyStreams.maxByBitrate + info.audioStreams.maxByBitrateMergingMediaSource. The fallback.
  5. Video-only — last-resort, silent playback.

SponsorBlock auto-skip

Phase D wires SponsorBlockClient.fetch(videoId, ["sponsor"]) into PlayerViewModel and runs a 250ms position-poll loop in PlayerScreen that calls exoPlayer.seekTo(segment.endSec * 1000) when the playhead enters a segment. A Toast says skipped <category> each time.

Categories default to sponsor only — matches siku2 defaults. User-settable categories are day-3.

Known limitations / day-3 items

  • ExoPlayer reuse across video changes: each VideoDetail → Player is a fresh resolve. Quick navigation can leave stranded resolves.
  • No background audio / PiP.
  • No screen-rotation handling (config-changes are caught so the activity isn't recreated, but the player UI doesn't lock landscape on play).
  • No watch history / subscriptions.
  • No channel browse / playlist browse.
  • No proxy / Tor support.
  • No "open with Straw" intent filter for YouTube URLs (low-hanging Day-3).
  • DI via Koin (skill recommends it).
  • Move logic into :shared per KMP best-practices.