From 39e7c07309a17e74b23adab0231dd5bf4eaa2667 Mon Sep 17 00:00:00 2001 From: ltadeu6 Date: Sun, 7 Jun 2026 09:37:58 -0300 Subject: [PATCH] Replace QML CAVA polling with SSE server for smooth bars DataSource fork+exec was limited to ~1fps. Now cava pipes into a Python SSE server (port 5555) and the HTML connects directly via EventSource, getting updates at up to 60fps without QML overhead. Co-Authored-By: Claude Sonnet 4.6 --- plugin/contents/ui/cava-server.py | 45 +++++++++++++++++++++++++++++++ plugin/contents/ui/cava-start.sh | 8 +++--- plugin/contents/ui/dancer.html | 13 ++++++--- plugin/contents/ui/main.qml | 15 +---------- 4 files changed, 59 insertions(+), 22 deletions(-) create mode 100644 plugin/contents/ui/cava-server.py diff --git a/plugin/contents/ui/cava-server.py b/plugin/contents/ui/cava-server.py new file mode 100644 index 0000000..6a88163 --- /dev/null +++ b/plugin/contents/ui/cava-server.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +""" +Reads CAVA ASCII output from stdin and streams it to the wallpaper +via Server-Sent Events on http://127.0.0.1:5555. +Run as: cava -p cava.conf | python3 cava-server.py +""" +import sys +import threading +import time +from http.server import HTTPServer, BaseHTTPRequestHandler + +latest = "" +lock = threading.Lock() + +class Handler(BaseHTTPRequestHandler): + def do_GET(self): + self.send_response(200) + self.send_header("Content-Type", "text/event-stream") + self.send_header("Cache-Control", "no-cache") + self.send_header("Access-Control-Allow-Origin", "*") + self.end_headers() + last = None + try: + while True: + with lock: + cur = latest + if cur and cur != last: + self.wfile.write(f"data: {cur}\n\n".encode()) + self.wfile.flush() + last = cur + time.sleep(0.016) # cap at ~60fps + except Exception: + pass + + def log_message(self, *args): + pass + +def serve(): + HTTPServer(("127.0.0.1", 5555), Handler).serve_forever() + +threading.Thread(target=serve, daemon=True).start() + +for line in sys.stdin: + with lock: + latest = line.strip() diff --git a/plugin/contents/ui/cava-start.sh b/plugin/contents/ui/cava-start.sh index 521eed0..e617aaf 100755 --- a/plugin/contents/ui/cava-start.sh +++ b/plugin/contents/ui/cava-start.sh @@ -1,9 +1,7 @@ #!/usr/bin/env bash -# Kill any previous instance tied to this config pkill -f "cava -p.*skeledance" 2>/dev/null +pkill -f "cava-server.py" 2>/dev/null sleep 0.2 -CONF="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/cava.conf" -cava -p "$CONF" | while IFS= read -r line; do - printf '%s\n' "$line" > /tmp/skeledance_bars -done +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cava -p "$DIR/cava.conf" | python3 "$DIR/cava-server.py" diff --git a/plugin/contents/ui/dancer.html b/plugin/contents/ui/dancer.html index 71ca84e..987f54a 100644 --- a/plugin/contents/ui/dancer.html +++ b/plugin/contents/ui/dancer.html @@ -886,18 +886,25 @@ for (let i = 0; i < VU_COUNT; i++) { vuBars.push(bar); } -// Called by QML with semicolon-separated CAVA values (0–100 per bar) +// Called with semicolon-separated CAVA values (0–100 per bar) function updateBars(raw) { if (!isPlaying) return; const vals = raw.split(';'); vuBars.forEach((bar, i) => { - const v = Math.min(100, parseInt(vals[i] || 0, 10)); - const h = Math.max(3, (v / 100) * MAX_BAR_H); + const v = Math.min(100, parseInt(vals[i] || 0, 10)); + const h = Math.max(3, (v / 100) * MAX_BAR_H); bar.style.height = h + 'px'; bar.style.opacity = 0.3 + 0.7 * (v / 100); }); } +// Connect to CAVA SSE server (started by cava-start.sh via QML) +(function connectCava() { + const sse = new EventSource('http://127.0.0.1:5555'); + sse.onmessage = e => { if (e.data) updateBars(e.data); }; + sse.onerror = () => { sse.close(); setTimeout(connectCava, 3000); }; +})(); + /* ============================================================ FLOATING MUSIC NOTES ============================================================ */ diff --git a/plugin/contents/ui/main.qml b/plugin/contents/ui/main.qml index b25d0f2..880ac51 100644 --- a/plugin/contents/ui/main.qml +++ b/plugin/contents/ui/main.qml @@ -16,6 +16,7 @@ WallpaperItem { settings { javascriptEnabled: true localContentCanAccessFileUrls: true + localContentCanAccessRemoteUrls: true localStorageEnabled: false } @@ -51,18 +52,4 @@ WallpaperItem { } } - // Polls CAVA bars at ~60fps - Plasma5Support.DataSource { - id: cavaSource - engine: "executable" - connectedSources: ["cat /tmp/skeledance_bars 2>/dev/null || echo ''"] - interval: 80 - - onNewData: function(sourceName, data) { - var raw = data["stdout"].trim() - if (raw !== "") { - webView.runJavaScript("updateBars(" + JSON.stringify(raw) + ")") - } - } - } }