Fixes from the second code audit (which adversarially refute-verified every
HIGH/CRIT — 0 survived; these are the confirmed MED/LOW):
- Duplicate-key crash (M-1 + M-4): the subs feed, Search, and Channel lists
key their LazyColumn by video url, but the sources weren't fully deduped —
a video appearing twice (collab/cross-post in two subscribed feeds, or the
same videoId across two search/channel shelves) made a duplicate Compose key
→ IllegalArgumentException → screen crash. Subs merge + Search/Channel FIRST
page now distinctBy(url); the continuation paths already did. (M-4 was a
regression of the vc=86 key additions, which only deduped the append path.)
- IosSafe end-of-chunk roll (M-5): the vc=86 change set chunkRemaining=0 on
inner EOF expecting the NEXT read() to roll, but Media3 stops calling read()
after a -1, so a LENGTH_UNSET inner still truncated to the first chunk. The
roll now happens WITHIN read() (loop) with a progress guard so a no-progress
chunk can't spin; folds in the zero-length-chunk EOF case too.
- SearchCacheStore.clear() is now atomic (updateAndGet) so a concurrent
record() can't resurrect a just-cleared entry on disk (M-3).
- Gate the YtRelated autoplay branch through isAllowedYtUrl to match the
universal-allowlist invariant (L-11); no-op-guard setLatestKnownVersion
(L-13); drop two unused TrackSelectionParameters imports (L-3).
Deferred (low-risk hygiene, none a crash): SharedPreferences write-ordering
serialization (M-2), the migration's dead Http.kt sweep (L-2), a stale-comment
pass. Verified: full Android compileDebugKotlin green.