Stream patches

Branch stream-patches · 5 commits not yet in main · staged 2026-09-17

Open the patched buildspin the globe — count the markers Read the raw diff12 files changed, 11755 insertions(+), 1233 deletions(-)

The globe

82 → 199

Discovery is gated on the globe: build-iptv-sources.js maps ISO‑2 to a country name by reading the C array, so a channel from a nation the globe does not place is dropped before it is ever probed. These 117 nations had nowhere to live.

AndorraAntigua and BarbudaBahamasBahrainBarbadosBelizeBeninBhutanBosnia and HerzegovinaBotswanaBruneiBurkina FasoBurundiCameroonCape VerdeCentral African RepublicChadComorosCongoCosta RicaCote d'IvoireCroatiaCyprusCzechiaDenmarkDjiboutiDominicaDominican RepublicEcuadorEl SalvadorEquatorial GuineaEritreaEstoniaEswatiniFijiFinlandGabonGambiaGeorgiaGreenlandGrenadaGuatemalaGuineaGuinea-BissauGuyanaHondurasHong KongHungaryIcelandJamaicaKiribatiKosovoKuwaitKyrgyzstanLaosLatviaLesothoLiberiaLiechtensteinLithuaniaLuxembourgMadagascarMalawiMaldivesMaltaMarshall IslandsMauritaniaMauritiusMicronesiaMoldovaMonacoMongoliaMontenegroMozambiqueNamibiaNauruNicaraguaNigerNorth MacedoniaOmanPalauPalestinePanamaPapua New GuineaParaguayPuerto RicoQatarRwandaSaint Kitts and NevisSaint LuciaSaint Vincent and the GrenadinesSamoaSan MarinoSao Tome and PrincipeSenegalSeychellesSierra LeoneSingaporeSlovakiaSloveniaSolomon IslandsSouth SudanSri LankaSurinameSwitzerlandTajikistanTanzaniaTimor-LesteTogoTongaTrinidad and TobagoTurkmenistanTuvaluUruguayUzbekistanVanuatuZambia

What the numbers said

508 → 2,186live streams in the pool
80 → 154nations carrying at least one stream
33 of 199nations at 30+ streams — the target is 30–40 everywhere, so this is what is left to move
46nations still at zero, which no amount of iptv-org will fix
68%of probed channels were playable (https + #EXTM3U + CORS, following master → variant)

Commits

Note for other agents: what this branch touches, and what will conflict

e5919a0 · 2026-09-17

More than one agent is active in this repo -- main advanced mid-session and the
working tree holds uncommitted work from someone else (bluesky, reels, vwAttach,
liveWake, void-proxy/public). I built this branch in a separate worktree so none
of it was touched, but void2-final.html is now edited on both sides and whoever
merges needs to know exactly which two places in it are mine.

Also records the non-obvious coupling: build-iptv-sources.js maps ISO-2 to a
country name by READING the C array, so editing C adds or removes stream
coverage. That is not discoverable from either file alone.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

Streams: review page reads coverage from the committed pool

36d514d · 2026-09-17

The page stated the audit's figures as literals, so the moment the pool changed
it would go on confidently reporting 508 streams across 80 nations. Read the
numbers out of scripts/iptv-sources.generated.json and diff them against main's
copy instead, so the review surface cannot claim something the committed pool
does not contain.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

Streams: lift the per-nation cap — 508 -> 2,186 streams, 80 -> 154 nations

c9f8eab · 2026-09-17

The pool was never limited by what exists. It was limited by --per-country 8:
51 of 80 countries sat exactly on that cap. Probing 200 candidates per nation
instead of 40, and keeping 40 instead of 8:

  channels probed    5,344   (was ~2,000)
  PLAYABLE           3,626   (68%)
  after brand dedupe 2,186 streams across 154 nations

Verified no regression: no nation that had streams lost them. Ethiopia and
Malaysia read as new zeroes but were never in the old pool either -- it covered
80 of 82 nations and they were the two gaps. Kenya 8->7 and DR Congo 3->1 are
the only shrinks, both ordinary probe variance.

WEIGHT. inject-iptv.js bakes the pool into void2-final.html, shipped on every
load whether or not anyone opens a stream, so a 4.3x bigger pool needed
checking before it was committed rather than after:

  baked literal   268KB   (uncompressed -- NOT what ships)
  brotli           49KB
  app over the wire  204KB -> 242KB

+38KB for 4.3x the streams. Capping what is baked was the obvious move on the
uncompressed number and the wrong one on the real number, so the whole pool is
baked. This still needs revisiting when the pool passes ~3x again, and the
frozen-snapshot problem is untouched: these URLs rot in weeks.

  scripts/inject-iptv.js
    Stop warning "NOT wired" on a build where the pool IS wired. wallSlots was
    rewritten around WALL_MIX and now reads the pool as
    `shuffle(iptvFor(n).filter(usable))`; none of the three literal spellings
    this script pattern-matched survive that rewrite. Ask whether anything calls
    iptvFor() before falling back to patching a literal. A false alarm that
    prints on every run is one that eventually gets believed.

  scripts/streams/report.js
    Coverage by nation and what the pool costs to ship. A total alone hides the
    only thing that matters here -- 46 nations are still at zero, and just 33
    of 199 have reached 30 streams.

The 7/3 news-life mix in WALL_MIX is untouched.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

Streams: publish the patched build for review, not just a diff

bc39577 · 2026-09-17

A diff is a poor review surface for this branch. The C array is ONE line of
void2-final.html, so the globe going from 82 nations to 199 renders as
"1 insertion, 1 deletion" -- the diff cannot show whether markers land on the
right patch of ground, which is the only thing worth reviewing. So publish the
patched build itself, playable, next to the measurements behind each patch.

  scripts/streams/build-review-site.js
    Stages ./public: the review page, the patched void2-final.html, and the raw
    diff against main. The added-nation list is computed by diffing main's C
    array against the generated nation table rather than being typed out, so the
    page cannot drift from what actually shipped.

  .github/workflows/deploy-stream-patches.yml
    Deploys to its OWN Pages project (`stream-patches`), never void-app-d35c --
    the live app stays untouched until this branch is merged. Merging to main is
    what ships it, through deploy-pages.yml.

    Checks out with fetch-depth 0 because the site diffs against main, and a
    shallow clone has no main to diff against. Refuses to publish a build whose
    scripts do not parse or whose C array has a duplicate ISO: a broken C array
    black-screens the whole app, and C is exactly what this branch edits, so
    that gate is the point rather than a formality.

Needs the two secrets deploy-pages.yml already uses (CLOUDFLARE_API_TOKEN,
CLOUDFLARE_ACCOUNT_ID). The Pages project is created on first run.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

Streams: globe holds every nation (82 -> 199), unblocking the source pool

ce040b1 · 2026-09-17

The stream pool could not grow past 82 nations no matter how many catalogues
we added, because discovery is gated on the globe itself: build-iptv-sources.js
maps ISO-2 -> country name by READING the C array (appCountries()), so a channel
from a nation the globe does not place is dropped before it is ever probed.
111 UN members had nowhere to live.

Measured today against iptv-org, which is the catalogue already wired in:

  streams in catalogue        17,418
  https (playable at all)     14,078   (80.8%)
  countries with >=1 stream      177
  random 200-stream probe        79% pass  (https + #EXTM3U + CORS)

So the pool of 508 is a CAP artifact, not scarcity -- 51 of 80 countries sit
exactly on --per-country 8. Lifting the cap is the next commit; this one makes
the lift mean something by giving the other nations somewhere to land.

  scripts/streams/build-nations.js
    Appends 117 nations to C (193 UN members + Taiwan/Palestine/Kosovo/Hong
    Kong/Greenland/Puerto Rico). Existing 82 entries round-trip BYTE-EXACT --
    their lat/lon and subreddit mappings were hand-tuned and eyeballed against
    the globe, so they are kept as raw text, never regenerated. Centroids come
    from a dataset rather than by hand; South Sudan is supplied here because
    that dataset predates 2011. Refuses to place a marker at 0,0 and throws on
    a display-name collision, since every source pool is keyed by display name.
    Verifies the page still parses before it finishes -- a broken C array
    black-screens the whole app.

  scripts/build-iptv-sources.js
    mentionsOtherNation drops a channel that names a DIFFERENT nation. With 82
    nations that was safe; with 199 it silently deletes catalogues, because a
    nation whose name contains another nation's name can never pass: every
    channel in DR Congo says "Congo", every channel in Equatorial Guinea says
    "Guinea", every channel in South Sudan says "Sudan". Skip a candidate whose
    name is a whole word of the owning nation's name. Palestine is on the globe
    now, so its own channels stop being treated as a foreign mention.

`sr` is null for every new nation -- verified unused in the app today (nothing
reads .sr), so it is a placeholder, not a gap.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

Files

.github/workflows/deploy-stream-patches.yml |    81 +
 .gitignore                                  |     3 +
 NOTE-FOR-AGENTS.md                          |    80 +
 scripts/build-iptv-sources.js               |    18 +-
 scripts/inject-iptv.js                      |    12 +-
 scripts/iptv-sources.generated.json         | 10988 +++++++++++++++++++++++---
 scripts/streams/build-nations.js            |   187 +
 scripts/streams/build-review-site.js        |   153 +
 scripts/streams/nations.generated.json      |  1201 +++
 scripts/streams/report.js                   |    88 +
 stream dept plan/00-AUDIT.md                |   169 +
 void2-final.html                            |     8 +-
 12 files changed, 11755 insertions(+), 1233 deletions(-)

What to check

1Spin the globe. Markers should sit on land, in the right places — new nations take their centroid from a dataset, not by hand.
2Search a new nation (Moldova, Tuvalu, Kosovo). It should open a nation panel, not fail.
3The wall should look and feel unchanged — the 7 news / 3 life mix is untouched.
4Existing nations should be byte-identical: their hand-tuned lat/lon and subreddits are kept as raw text, never regenerated.