feat: synced lyrics display from LRCLib
music-light fetches synced LRC lyrics from lrclib.net on track change, tracks playback position via a 0.5s background thread, and exposes the current lyric line in /current. dancer.html polls /current at 700ms and displays the line bottom-left with a cyan glow, subtitle style. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
99851a2ee7
commit
347b3729c8
1 changed files with 47 additions and 5 deletions
|
|
@ -236,6 +236,32 @@ html, body {
|
|||
100% { transform: translateY(-110px) rotate(18deg) scale(1.3); opacity: 0; }
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
LYRICS DISPLAY
|
||||
============================================================ */
|
||||
.lyric-display {
|
||||
position: absolute;
|
||||
left: 3%;
|
||||
bottom: 16%;
|
||||
max-width: 36%;
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
transition: opacity 0.5s ease;
|
||||
}
|
||||
.lyric-line {
|
||||
color: #ffffff;
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: clamp(13px, 1.5vw, 20px);
|
||||
line-height: 1.5;
|
||||
letter-spacing: 0.03em;
|
||||
text-shadow:
|
||||
0 0 18px rgba(0,200,255,0.85),
|
||||
0 0 6px rgba(0,200,255,0.5),
|
||||
0 2px 10px rgba(0,0,0,0.95),
|
||||
-1px -1px 0 rgba(0,0,0,0.7),
|
||||
1px 1px 0 rgba(0,0,0,0.7);
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
SCANLINES OVERLAY
|
||||
============================================================ */
|
||||
|
|
@ -861,6 +887,11 @@ html, body {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Lyrics display -->
|
||||
<div class="lyric-display" id="lyricDisplay">
|
||||
<div class="lyric-line" id="lyricLine"></div>
|
||||
</div>
|
||||
|
||||
<!-- Scanlines overlay -->
|
||||
<div class="scanlines"></div>
|
||||
|
||||
|
|
@ -1123,12 +1154,15 @@ function applyTheme(rgb) {
|
|||
});
|
||||
}
|
||||
|
||||
const albumArtEl = document.getElementById('albumArt');
|
||||
const fogWisps = document.querySelectorAll('.fog-wisp');
|
||||
const albumArtEl = document.getElementById('albumArt');
|
||||
const fogWisps = document.querySelectorAll('.fog-wisp');
|
||||
const lyricDisplay = document.getElementById('lyricDisplay');
|
||||
const lyricLineEl = document.getElementById('lyricLine');
|
||||
|
||||
let lastArtUrl = '';
|
||||
let lastArtUrl = '';
|
||||
let lastLyricLine = '';
|
||||
|
||||
function fetchTheme() {
|
||||
function fetchCurrent() {
|
||||
fetch('http://127.0.0.1:8765/current')
|
||||
.then(r => r.json())
|
||||
.then(d => {
|
||||
|
|
@ -1138,10 +1172,18 @@ function fetchTheme() {
|
|||
document.getElementById('albumArtImg').src =
|
||||
'http://127.0.0.1:8765/art?' + Date.now();
|
||||
}
|
||||
const line = d.lyric_line || '';
|
||||
if (line !== lastLyricLine) {
|
||||
lastLyricLine = line;
|
||||
lyricLineEl.textContent = line;
|
||||
}
|
||||
lyricDisplay.style.opacity = (isPlaying && line) ? '1' : '0';
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
setInterval(() => { if (isPlaying) fetchCurrent(); }, 700);
|
||||
|
||||
// Connect to CAVA SSE server (started by cava-start.sh via QML)
|
||||
(function connectCava() {
|
||||
const sse = new EventSource('http://127.0.0.1:5555');
|
||||
|
|
@ -1196,7 +1238,7 @@ function updateMusic(artist, title) {
|
|||
document.querySelector('.music-ticker').style.opacity = playing ? '1' : '0';
|
||||
albumArtEl.style.opacity = playing ? '1' : '0';
|
||||
if (!playing) vuBars.forEach(b => { b.style.height = '3px'; b.style.opacity = '0.15'; });
|
||||
if (playing) fetchTheme();
|
||||
if (playing) fetchCurrent();
|
||||
const key = artist + '|' + title;
|
||||
if (el.dataset.key === key) return;
|
||||
el.dataset.key = key;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue