Request playback with non-site player types and swap to backup Usher streams on stitched ad breaks; inject vendored vaft into the legacy WebView and expose a Settings toggle. Co-authored-by: Cursor <cursoragent@cursor.com>
41 lines
2.1 KiB
Markdown
41 lines
2.1 KiB
Markdown
# Frosty fork — Twitch ad blocking (DESIGN)
|
||
|
||
Personal Forgejo fork of [tommyxchow/frosty](https://github.com/tommyxchow/frosty) that adds **TwitchAdSolutions (vaft)**-style live ad blocking.
|
||
|
||
Canonical living plan: session `active-plan.md` for conversation `37b527bf-e540-4753-be1f-39906fe2ba28`. ADR: **ADR-026**.
|
||
|
||
## Problem
|
||
|
||
Upstream Frosty plays Twitch HLS (native) or `player.twitch.tv` (WebView). Twitch may stitch ad pods into the media playlist (`stitched` / `EXT-X-DATERANGE`). Upstream only **detects** native ad breaks (to avoid bogus latency recovery); it does **not** block ads. Turbo/sub `hide_ads` via linked web session is separate and not a client blocker.
|
||
|
||
## Approach (vaft-equivalent)
|
||
|
||
Browser vaft hooks `Worker`/`fetch` and rewrites m3u8. Frosty’s native player cannot do that from Dart (`loadUrl` + headers only).
|
||
|
||
| Layer | Behavior |
|
||
| --- | --- |
|
||
| Primary token | GQL `PlaybackAccessToken` with `playerType=popout` (vaft `ForceAccessTokenPlayerType`) when Settings `blockTwitchAds` is on |
|
||
| Ad break | Native `adBreakStream` → probe backup types `embed` → `popout` → `autoplay`; accept first Usher multivariant whose sampled media playlist lacks `stitched`; `loadUrl` that URL |
|
||
| Ad end | Restore primary popout Usher URL |
|
||
| WebView | Best-effort inject of vendored `assets/js/vaft.user.js` (not true document-start on Android) |
|
||
| UI | Settings toggle (default on; enabling forces native player); overlay “Blocking ads” |
|
||
|
||
## Key code
|
||
|
||
- `lib/apis/twitch_ad_solutions.dart` — constants + `TwitchAdFreePlayback`
|
||
- `lib/apis/twitch_gql_api.dart` — `playerType` / `platform`, `fetchPlaylistText`
|
||
- `lib/screens/channel/video/native_video_store.dart` — load + swap
|
||
- `lib/screens/channel/video/video_store.dart` — WebView inject
|
||
- `lib/screens/settings/stores/settings_store.dart` — `blockTwitchAds`
|
||
- `assets/js/vaft.user.js` — upstream TwitchAdSolutions script
|
||
|
||
## Non-goals (v1)
|
||
|
||
- Upstream PR
|
||
- Full Worker prototype cleaning
|
||
- Third-party proxies
|
||
- HLS segment stripping inside `better_native_video_player` (unless smoke tests demand it)
|
||
|
||
## Build
|
||
|
||
Flutter **3.47.0** (pinned in `pubspec.yaml`). Local SDK: `/home/yuna/.local/flutter-3.47.0`.
|