# 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 URL** — `info.dashMpdUrl` → `DashMediaSource`. Best quality
   when YouTube serves it.
2. **HLS URL** — `info.hlsUrl` → `HlsMediaSource`. Mostly for live streams.
3. **Combined video+audio** — `info.videoStreams.maxByBitrate` →
   `ProgressiveMediaSource`. Rare on modern YouTube; older clients only.
4. **Merged DASH-chunks** — `info.videoOnlyStreams.maxByBitrate` +
   `info.audioStreams.maxByBitrate` → `MergingMediaSource`. 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.
