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.
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
:strawAppfor 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 —
:strawAppis 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:
- DASH MPD URL —
info.dashMpdUrl→DashMediaSource. Best quality when YouTube serves it. - HLS URL —
info.hlsUrl→HlsMediaSource. Mostly for live streams. - Combined video+audio —
info.videoStreams.maxByBitrate→ProgressiveMediaSource. Rare on modern YouTube; older clients only. - Merged DASH-chunks —
info.videoOnlyStreams.maxByBitrate+info.audioStreams.maxByBitrate→MergingMediaSource. The fallback. - 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
:sharedper KMP best-practices.