From 2ee2470089b7e14f566a28ac9ba4170ad66b4195 Mon Sep 17 00:00:00 2001 From: ltadeu6 Date: Sun, 7 Jun 2026 09:28:36 -0300 Subject: [PATCH] Tie animations to playback state - Paused/stopped playerctl treated as no music (QML change) - Character and notes only appear when Playing - VU meter always visible but in rest state (3px, faint) when idle Co-Authored-By: Claude Sonnet 4.6 --- plugin/contents/ui/dancer.html | 22 +++++++++++++--------- plugin/contents/ui/main.qml | 11 ++++++----- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/plugin/contents/ui/dancer.html b/plugin/contents/ui/dancer.html index 371ff5b..4182d83 100644 --- a/plugin/contents/ui/dancer.html +++ b/plugin/contents/ui/dancer.html @@ -102,14 +102,13 @@ html, body { ============================================================ */ .vu-meter { position: absolute; - bottom: 4%; + bottom: 5%; left: 50%; transform: translateX(-50%); display: flex; align-items: flex-end; gap: 4px; pointer-events: none; - opacity: 0; transition: opacity 0.8s; } .vu-bar { @@ -887,9 +886,14 @@ for (let i = 0; i < VU_COUNT; i++) { function animateVU(ts) { const t = ts * 0.001; vuBars.forEach(bar => { - const h = bar._maxH * Math.abs(Math.sin(t * bar._speed + bar._phase)); - bar.style.height = Math.max(3, h) + 'px'; - bar.style.opacity = 0.55 + 0.45 * (h / bar._maxH); + if (isPlaying) { + const h = bar._maxH * Math.abs(Math.sin(t * bar._speed + bar._phase)); + bar.style.height = Math.max(3, h) + 'px'; + bar.style.opacity = 0.55 + 0.45 * (h / bar._maxH); + } else { + bar.style.height = '3px'; + bar.style.opacity = '0.15'; + } }); requestAnimationFrame(animateVU); } @@ -919,15 +923,15 @@ function spawnNote() { setTimeout(() => el.remove(), dur * 1000 + 100); } +let isPlaying = false; + /* Spawn on every beat (500ms) with a 50 % chance of two notes */ setInterval(() => { + if (!isPlaying) return; spawnNote(); if (Math.random() < 0.5) spawnNote(); }, BEAT_MS); -/* First note immediately */ -spawnNote(); - /* ============================================================ MUSIC INFO (called by QML via runJavaScript) ============================================================ */ @@ -936,8 +940,8 @@ function updateMusic(artist, title) { const text = (artist && title) ? `${artist} — ${title}` : (title || artist || '— —'); const playing = !!(artist || title); + isPlaying = playing; document.querySelector('.robot-wrap').style.opacity = playing ? '1' : '0'; - document.querySelector('.vu-meter').style.opacity = playing ? '1' : '0'; if (el.textContent === text) return; el.textContent = text; if (playing) pickSkeleton(); diff --git a/plugin/contents/ui/main.qml b/plugin/contents/ui/main.qml index aa4b303..7c6ba96 100644 --- a/plugin/contents/ui/main.qml +++ b/plugin/contents/ui/main.qml @@ -28,14 +28,15 @@ WallpaperItem { Plasma5Support.DataSource { id: musicSource engine: "executable" - connectedSources: ["playerctl metadata --format '{{artist}}||{{title}}' 2>/dev/null || echo '||'"] + connectedSources: ["playerctl metadata --format '{{status}}||{{artist}}||{{title}}' 2>/dev/null || echo '||'"] interval: 2000 onNewData: function(sourceName, data) { - var raw = data["stdout"].trim() - var sep = raw.indexOf("||") - var artist = sep >= 0 ? raw.substring(0, sep) : "" - var title = sep >= 0 ? raw.substring(sep + 2) : raw + var raw = data["stdout"].trim() + var parts = raw.split("||") + var status = parts[0] || "" + var artist = status === "Playing" ? (parts[1] || "") : "" + var title = status === "Playing" ? (parts[2] || "") : "" webView.runJavaScript( "updateMusic(" + JSON.stringify(artist) + "," + JSON.stringify(title) + ")" )