Files
helioviewer_grab/helioviewer_grab.py
T

105 lines
2.9 KiB
Python

#!/usr/bin/env python3
"""
Holt ein Sonnen-Vollscheibenbild von Helioviewer zu einem Zeitpunkt.
Beispiel:
./helioviewer_grab.py 2026-08-12T18:18:00Z hmi_continuum
./helioviewer_grab.py --list
"""
import json
import sys
import urllib.parse
import urllib.request
API = "https://api.helioviewer.org/v2"
def call(endpoint, **params):
"""GET auf die Helioviewer-API. Gibt (content_type, bytes) zurueck."""
url = f"{API}/{endpoint}/?" + urllib.parse.urlencode(params)
with urllib.request.urlopen(url, timeout=120) as r:
return r.headers.get_content_type(), r.read()
def sources():
"""Flache Liste aller Datenquellen: [(sourceId, layer_path, nickname), ...]
Der Pfad durch den verschachtelten JSON-Baum IST die Layer-Kette,
die takeScreenshot erwartet (z.B. SDO/HMI/HMI/continuum).
"""
_, raw = call("getDataSources")
out = []
def walk(node, path):
if not isinstance(node, dict):
return
if "sourceId" in node:
out.append((node["sourceId"], path, node.get("nickname", "")))
return
for key, val in node.items():
walk(val, path + [key])
walk(json.loads(raw), [])
return sorted(out)
def pick(needle):
"""Sucht eine Datenquelle anhand eines Substrings im Nickname/Pfad."""
hits = [
s
for s in sources()
if needle.lower().replace("_", " ")
in (" ".join(s[1]) + " " + s[2]).lower().replace("_", " ")
]
if not hits:
sys.exit(f"keine Datenquelle passt auf {needle!r} (siehe --list)")
return hits[0]
def main():
if "--list" in sys.argv:
for sid, path, nick in sources():
print(f"{sid:4d} {'/'.join(path):40s} {nick}")
return
date = sys.argv[1] if len(sys.argv) > 1 else "2026-08-12T18:18:00Z"
what = sys.argv[2] if len(sys.argv) > 2 else "continuum"
sid, path, nick = pick(what)
print(f"Quelle: [{sid}] {'/'.join(path)} ({nick})")
# Welches Bild liegt dem Wunschzeitpunkt am naechsten? Wichtig:
# Helioviewer liefert IMMER etwas, auch wenn es Stunden daneben liegt.
_, raw = call("getClosestImage", date=date, sourceId=sid)
meta = json.loads(raw)
print(f"angefragt: {date}\ngeliefert: {meta['date']}")
layers = "[" + ",".join(path) + ",1,100]"
ctype, png = call(
"takeScreenshot",
date=meta["date"],
imageScale=0.6, # Bogensekunden/Pixel -> ~3400 px Vollscheibe
layers=layers,
x1=-1100,
y1=-1100,
x2=1100,
y2=1100, # Bogensekunden ab Scheibenmitte
display="true",
watermark="false",
)
if not ctype.startswith("image"):
sys.exit(f"unerwartete Antwort ({ctype}): {png[:200]!r}")
name = (
f"{meta['date'].replace(':', '').replace('-', '')}_{nick.replace(' ', '')}.png"
)
with open(name, "wb") as f:
f.write(png)
print(f"-> {name} ({len(png) / 1e6:.1f} MB)")
if __name__ == "__main__":
main()