diff --git a/Rick Roll (Different link + no ads) [xvFZjo5PgG0].webm b/Rick Roll (Different link + no ads) [xvFZjo5PgG0].webm new file mode 100644 index 0000000..799cec0 Binary files /dev/null and b/Rick Roll (Different link + no ads) [xvFZjo5PgG0].webm differ diff --git a/prank.sh b/prank.sh new file mode 100755 index 0000000..2f53d35 --- /dev/null +++ b/prank.sh @@ -0,0 +1 @@ +#mpv '/home/jonas/scripts/Rick Roll (Different link + no ads) [xvFZjo5PgG0].webm' diff --git a/sjnet-launcher b/sjnet-launcher index 7907f7f..9e1455f 100755 --- a/sjnet-launcher +++ b/sjnet-launcher @@ -77,7 +77,7 @@ def menu(): box("Available programs:") - + print("q: quit") counter = 1 program_map = {} @@ -109,8 +109,10 @@ def menu(): counter += 1 try: - choose = int(input("enter number of the programm you want: > ")) - chosen = program_map[choose] + choose = input("enter number of the programm you want: > ") + if choose.lower() == "q": + sys.exit() + chosen = program_map[int(choose)] except ValueError: print("Es muss eine Nummer sein.") @@ -124,6 +126,7 @@ def menu(): try: subprocess.run([sys.executable, chosen]) except KeyboardInterrupt: + print("Interrupt signal recived...") sys.exit(0) diff --git a/statserver/app.py b/statserver/app.py new file mode 100644 index 0000000..9101fc5 --- /dev/null +++ b/statserver/app.py @@ -0,0 +1,133 @@ +# Simple statserver: CPU usage + CPU temp, chart on localhost:8080 +# +# Every sample is stored in SQLite (stats.db, next to this file). Rows older +# than 7 days are purged, so the database always holds the last 7 days. A +# background thread samples once per second, independent of web clients. +import os +import sqlite3 +import threading +import time + +from flask import Flask, request, jsonify, Response + +app = Flask(__name__) + +DB_PATH = os.path.join(os.path.dirname(__file__), "stats.db") +SEVEN_DAYS = 7 * 24 * 3600 + +# name -> (span in seconds, bucket size in seconds); day/week rows are +# averaged into buckets so the payload stays small. +RANGES = { + "minute": (60, 1), + "hour": (3600, 10), + "day": (86400, 60), + "week": (SEVEN_DAYS, 600), +} + +_prev_cpu = None +_last = {"usage": 0.0, "temp": None} + +_db = sqlite3.connect(DB_PATH, check_same_thread=False) +_db_lock = threading.Lock() +_db.execute( + "CREATE TABLE IF NOT EXISTS samples (" + "ts REAL PRIMARY KEY, usage REAL, temp REAL)" +) +_db.commit() + + +def _read_temp(): + """Read CPU temperature from sysfs, preferring the CPU package sensor. + + Returns degrees Celsius, or None if nothing readable is found. + """ + fallback = None + base = "/sys/class/thermal" + for zone in sorted(x for x in os.listdir(base) if x.startswith("thermal_zone")): + try: + with open(f"{base}/{zone}/type") as f: + ztype = f.read().strip().lower() + with open(f"{base}/{zone}/temp") as f: + temp = int(f.read().strip()) + except OSError: + continue + if ztype == "x86_pkg_temp": # package temp = best guess for CPU temp + return temp / 1000 + if "cpu" in ztype or "pkg" in ztype or "core" in ztype: + fallback = fallback if fallback is not None else temp / 1000 + return fallback + + +def _read_usage(): + """CPU usage since the previous call, from /proc/stat. Returns percent.""" + with open("/proc/stat") as f: + cpu_line = next(line for line in f if line.startswith("cpu ")) + fields = [int(x) for x in cpu_line.split()[1:] if x.isdigit()] + idle, total = fields[3], sum(fields) + global _prev_cpu + prev = _prev_cpu + _prev_cpu = (idle, total) + if prev is None: + return None + didle = idle - prev[0] + dtotal = total - prev[1] + return 100.0 * (1 - didle / dtotal) if dtotal > 0 else 0.0 + + +def sample(): + usage = _read_usage() + if usage is not None: + _last["usage"] = usage + _last["temp"] = _read_temp() + + +def _sampler(): + """Sample once per second and store it; purge >7-day-old rows every minute.""" + n = 0 + while True: + sample() + with _db_lock: + _db.execute( + "INSERT OR REPLACE INTO samples VALUES (?, ?, ?)", + (time.time(), _last["usage"], _last["temp"]), + ) + # commit each insert so other connections (and restarts) see the + # data; a 1/sec commit is trivial load for SQLite + n += 1 + if n % 60 == 0: + _db.execute("DELETE FROM samples WHERE ts < ?", + (time.time() - SEVEN_DAYS,)) + _db.commit() + time.sleep(1) + + +@app.get("/api/stats") +def stats(): + return jsonify(usage=_last["usage"], temp=_last["temp"]) + + +@app.get("/api/history") +def history(): + rng = request.args.get("range", "minute") + span, bucket = RANGES.get(rng, RANGES["minute"]) + since = time.time() - span + with _db_lock: + rows = _db.execute( + "SELECT CAST(ts / ? AS INTEGER) * ? AS t, AVG(usage), AVG(temp) " + "FROM samples WHERE ts >= ? GROUP BY t ORDER BY t", + (bucket, bucket, since), + ).fetchall() + return jsonify(points=[ + {"t": round(r[0]), "u": r[1], "c": r[2]} for r in rows + ]) + + +@app.get("/") +def index(): + with open(os.path.join(os.path.dirname(__file__), "index.html"), "rb") as f: + return Response(f.read(), mimetype="text/html") + + +if __name__ == "__main__": + threading.Thread(target=_sampler, daemon=True).start() + app.run(host="0.0.0.0", port=8080, threaded=True, use_reloader=False) diff --git a/statserver/index.html b/statserver/index.html new file mode 100644 index 0000000..9983f36 --- /dev/null +++ b/statserver/index.html @@ -0,0 +1,118 @@ + + + + + CPU Stats + + + +

CPU Stats

+
+ Usage % + Temp °C +
+
+ + + + +
+ + + + + diff --git a/statserver/stats.db b/statserver/stats.db new file mode 100644 index 0000000..494b93a Binary files /dev/null and b/statserver/stats.db differ diff --git a/statserver/stats.db-journal b/statserver/stats.db-journal new file mode 100644 index 0000000..82fb5db Binary files /dev/null and b/statserver/stats.db-journal differ