Files
L'électron rare 60112b0790 feat(data-feeds): GDELT, Wikipedia, Tides, ATC
4 nouvelles sources open-data publiques, sans cle :

- gdelt : GDELT 2.0 events CSV.zip toutes les 15 min ; emet
  /event lat lon tone country eid + /batch n countries avg_tone.
  Dedup par GLOBALEVENTID, ring buffer 8192.
- wikimedia : EventStreams SSE firehose recentchange (toutes
  langues). Sample ~5% des edits + rate per second toutes
  les 5 s. Reconnect auto sur disconnect.
- tides : NOAA CO-OPS water level observe + predit pour une
  station (Boston par defaut, station code configurable) +
  phase lunaire calculee (Conway approx, sans dep). Poll 6 min.
- atc : LiveATC hub listeners scraping (KJFK/KLAX/KSFO/KORD/
  EGLL/LFPG par defaut). Polite 500ms entre hubs, total
  poll 5 min.
2026-05-13 19:17:36 +02:00

61 lines
2.0 KiB
Python

"""LiveATC feeds metadata — pas de pull audio, juste compteur de
streams actifs et indicateur d'activite par hub aeroport.
LiveATC.net expose un endpoint stats JSON simplifie pour quelques
aeroports majeurs (KJFK, KLAX, KSFO, EGLL, LFPG). On polle pour
chacun le nombre d'auditeurs courant (proxy pour 'activite ATC').
OSC out :
/data/atc/hub icao listeners
/data/atc/total total_listeners n_hubs
"""
from __future__ import annotations
import asyncio
import logging
import re
import httpx
LOG = logging.getLogger("feed.atc")
# LiveATC publie un page HTML par feed avec "Listeners: N" — on parse
# ca via regex au lieu d'un API officielle (pas disponible).
HUB_URL = "https://www.liveatc.net/search/?icao={icao}"
LISTENERS_RE = re.compile(r"Listeners:\s*<b>(\d+)</b>", re.I)
async def _fetch_hub(cli: httpx.AsyncClient, icao: str
) -> int:
r = await cli.get(HUB_URL.format(icao=icao),
headers={"User-Agent": "av-live-data-feeds/1.0"})
r.raise_for_status()
matches = LISTENERS_RE.findall(r.text)
if not matches:
return 0
return sum(int(m) for m in matches)
async def run(ctx) -> None:
cfg = ctx.cfg
hubs = cfg.get("hubs",
["KJFK", "KLAX", "KSFO", "KORD", "EGLL", "LFPG"])
period = float(cfg.get("poll_seconds", 300.0)) # 5 min
async with httpx.AsyncClient(timeout=20.0) as cli:
while True:
total = 0
active = 0
for icao in hubs:
try:
listeners = await _fetch_hub(cli, icao)
except Exception as e: # noqa: BLE001
LOG.warning("atc %s failed: %s", icao, e)
continue
ctx.send("hub", icao, float(listeners))
total += listeners
if listeners > 0:
active += 1
await asyncio.sleep(0.5) # polite scrape
ctx.send("total", float(total), float(active))
await asyncio.sleep(period)