strawcore/tests/fixtures/live_tab
Cobb a969254b09 test: drop the tracking fields left in a feed fixture; guard every fixture
tests/fixtures/feed_lookup/past_stream.json still carried trackingParams
and clickTrackingParams (opaque YouTube tokens, 27 and 34 bytes once
base64-decoded; neither holds the capturing address as text or as four
bytes, checked with a planted control). They are removed; nothing reads
them. The three feed fixtures now each come out unchanged from the
README's scrub filter.

the_fixtures_carry_no_request_bound_data listed the twelve Live tab
fixtures by name and never looked at the three feed fixtures, and its
markers missed clickTrackingParams (the match was case-sensitive),
URL-encoded "ip%3D" and a bare IPv4 address. It now reads every JSON
file in both fixture directories from disk, checks that the walk found
five named files (so a moved directory cannot pass empty), and fails on
any "...TrackingParams" field, "ip%3D" or "ip%3d", and a dotted IPv4
address with every group at most 255. "20.772.51.34", a duration the
Indonesian fixture carries, is not taken for an address (772 > 255);
first_ipv4 has its own test. Run on the old past_stream.json, the guard
fails on trackingParams.

Correction to b1a0cf2: its message says tracking fields were removed
from the captures before saving. That was true of the Live tab fixtures
and not of past_stream.json, which kept both fields until this commit.
2026-10-23 11:27:35 -07:00
..
kurzgesagt_live_request.json test: capture Live tab and feed lookup responses from public channels 2026-10-23 09:40:16 -07:00
lofigirl_live.json test: drop two unread fields from the Live tab fixtures 2026-10-23 09:48:20 -07:00
lofigirl_live_de.json test: drop two unread fields from the Live tab fixtures 2026-10-23 09:48:20 -07:00
lofigirl_live_fi.json channel_live: read durations written with "." and only take a word for scheduled 2026-10-23 11:24:40 -07:00
lofigirl_live_fr.json test: drop two unread fields from the Live tab fixtures 2026-10-23 09:48:20 -07:00
lofigirl_live_id.json channel_live: read durations written with "." and only take a word for scheduled 2026-10-23 11:24:40 -07:00
nasa_home_tabs.json test: capture Live tab and feed lookup responses from public channels 2026-10-23 09:40:16 -07:00
nasa_live.json test: drop two unread fields from the Live tab fixtures 2026-10-23 09:48:20 -07:00
nasa_live_da.json channel_live: read durations written with "." and only take a word for scheduled 2026-10-23 11:24:40 -07:00
nasa_live_page2.json test: drop two unread fields from the Live tab fixtures 2026-10-23 09:48:20 -07:00
nasaspaceflight_live.json test: drop two unread fields from the Live tab fixtures 2026-10-23 09:48:20 -07:00
README.md test: drop the tracking fields left in a feed fixture; guard every fixture 2026-10-23 11:27:35 -07:00
skynews_live.json test: drop two unread fields from the Live tab fixtures 2026-10-23 09:48:20 -07:00
skynews_live_de.json test: drop two unread fields from the Live tab fixtures 2026-10-23 09:48:20 -07:00
skynews_live_page2.json test: Live tab cases the first mutation run found untested 2026-10-23 09:48:20 -07:00
spacex_live.json test: drop two unread fields from the Live tab fixtures 2026-10-23 09:48:20 -07:00

Live tab fixtures

Captured on 2026-10-23 from public channels with the request strawcore sends: POST https://www.youtube.com/youtubei/v1/browse, WEB client envelope (build_desktop_envelope), utcOffsetMinutes 0, the channel's browseId and the Live tab params EgdzdHJlYW1z8gYECgJ6AA==. Continuation pages were fetched with {"continuation": <token>} against the same endpoint.

File Language What it holds
nasa_live.json en-GB 2 scheduled, 2 live, 26 past, continuation
nasa_live_page2.json en-GB page 2 of the above: 30 past, continuation
skynews_live_page2.json en-GB page 2 of skynews_live.json: 30 past, two of them with a row naming collaborating channels above the views row, continuation
skynews_live.json en-GB 3 scheduled (one with no waiting count), 2 live, 25 past, continuation
skynews_live_de.json de-DE the same channel in German
lofigirl_live.json en-GB 23 live, 2 past with durations past 999 hours, no continuation
lofigirl_live_de.json de-DE the same channel in German
lofigirl_live_fr.json fr-FR the same channel in French
lofigirl_live_fi.json fi the same channel in Finnish: durations past 999 hours written 20\u00a0772.51.34
lofigirl_live_id.json id the same channel in Indonesian: durations past 999 hours written 20.772.51.34
spacex_live.json en-GB 30 past, nothing live or scheduled, continuation
nasa_live_da.json da nasa_live.json's channel in Danish, later the same day: 1 scheduled, 3 live, 26 past, durations written 28.10 and 1.42.26, continuation
nasaspaceflight_live.json en-GB 1 scheduled, 3 live, 26 past, some with a members-only or fundraiser badge row
nasa_home_tabs.json en-GB tab list of a Home tab response from a channel with a Live tab
kurzgesagt_live_request.json en-GB tab list of the answer to the Live tab params from a channel without a Live tab (its Home tab comes back selected)

The da, fi and id files were requested with that bare language code and gl GB.

Every response was trimmed with this jq filter before it was saved ($mode is page, continuation or tabs), then written with jq -a -c, so non-ASCII text is kept as JSON escapes. It drops request-bound and tracking data, item menus and hover actions, the sort and filter chips above the grid, and the query string of every i.ytimg.com URL, and keeps every other field of each item as captured. The chips and the badge animationActivationEntityKey are dropped because gitleaks 8.21.2 (the version CI runs) reports opaque tokens in them as generic API keys; neither is read by the parser:

def scrub:
  walk(
    if type == "object" then
      del(.trackingParams, .clickTrackingParams, .loggingDirectives, .loggingContext,
          .watchEndpointSupportedOnesieConfig, .menuButton,
          .thumbnailHoverOverlayToggleActionsViewModel, .themedPalette,
          .serviceTrackingParams, .visitorData, .accessibilityContext,
          .animationActivationEntityKey)
      | if has("overlays") and (.overlays | type) == "array"
        then .overlays |= map(select(. != {})) else . end
      | if has("richGridRenderer") then .richGridRenderer |= del(.header) else . end
    elif type == "string" and test("^https://i[0-9]?\\.ytimg\\.com/") then sub("\\?.*$"; "")
    else . end);
if $mode == "page" then {contents} | scrub
elif $mode == "continuation" then {onResponseReceivedActions} | scrub
elif $mode == "tabs" then {contents: {twoColumnBrowseResultsRenderer: {tabs: [.contents.twoColumnBrowseResultsRenderer.tabs[] | if has("tabRenderer") then .tabRenderer |= del(.content) else . end]}}} | scrub
else error("mode") end

The ../feed_lookup/ files are the narrowed metadata lookup the subscription feed already makes per item (stream_metadata: the ANDROID reel/reel_item_watch call with $fields=playerResponse.playabilityStatus,playerResponse.videoDetails), captured the same day for a stream that was live, a past stream and a scheduled stream. The query string of each i.ytimg.com URL was removed when they were captured; past_stream.json was first committed with two tracking fields (trackingParams, clickTrackingParams) that were removed later. Each file now comes out unchanged when the scrub filter above is run on it as the jq program (jq -a -c).

the_fixtures_carry_no_request_bound_data in tests/live_tab.rs reads every JSON file in both directories and fails on googlevideo, visitorData, trackingParams or any ...TrackingParams field, ip= (plain or URL-encoded), a bare IPv4 address, or a thumbnail URL with a query string.