From a5b9e87eb99ba1ed18ac8bf2b5c216fe501dd499 Mon Sep 17 00:00:00 2001 From: minster586 <43217359+minster586@users.noreply.github.com> Date: Sun, 12 Apr 2026 21:41:35 -0400 Subject: [PATCH] added files --- index.html | 36 ++++++++++++++ script.js | 128 +++++++++++++++++++++++++++++++++++++++++++++++ style.css | 143 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 307 insertions(+) create mode 100644 index.html create mode 100644 script.js create mode 100644 style.css diff --git a/index.html b/index.html new file mode 100644 index 0000000..8e7abb8 --- /dev/null +++ b/index.html @@ -0,0 +1,36 @@ + + + + + + Radio DJ Now Playing + + + +
+ +
+ Album backdrop +
+ + +
+ + +
+ +
+ Album art +
+ + +
+
Loading...
+
Loading...
+
+
+
+ + + + diff --git a/script.js b/script.js new file mode 100644 index 0000000..cb437ae --- /dev/null +++ b/script.js @@ -0,0 +1,128 @@ +/////////////// +// PARAMETERS // +/////////////// + +const queryString = window.location.search; +const urlParams = new URLSearchParams(queryString); +const refreshRate = parseInt(urlParams.get("refreshrate")) || 6; // Default 6 seconds + +// Image filename - update this if the backend changes the image path +const IMAGE_FILENAME = "Album-Art.png"; + +////////////////// +// GLOBAL STATE // +////////////////// + +let currentArtist = ""; +let currentTitle = ""; +let refreshInterval = null; + +////////////////// +// REFRESH LOGIC // +////////////////// + +/** + * Fetches the artist and title from text files + */ +async function fetchSongData() { + try { + const artistResponse = await fetch("artist.txt"); + const titleResponse = await fetch("title.txt"); + + if (!artistResponse.ok || !titleResponse.ok) { + console.error("Failed to fetch song data"); + return null; + } + + const artist = (await artistResponse.text()).trim(); + const title = (await titleResponse.text()).trim(); + + return { artist, title }; + } catch (error) { + console.error("Error fetching song data:", error); + return null; + } +} + +/** + * Updates the DOM elements if the song data has changed + */ +function updateSongDisplay(artist, title) { + const titleElement = document.querySelector(".song-title"); + const artistElement = document.querySelector(".song-artist"); + + let trackChanged = false; + + // Only update if data has actually changed + if (title !== currentTitle) { + currentTitle = title; + titleElement.classList.add("updating"); + titleElement.textContent = title || "Unknown Title"; + trackChanged = true; + setTimeout(() => { + titleElement.classList.remove("updating"); + }, 400); + } + + if (artist !== currentArtist) { + currentArtist = artist; + artistElement.classList.add("updating"); + artistElement.textContent = artist || "Unknown Artist"; + trackChanged = true; + setTimeout(() => { + artistElement.classList.remove("updating"); + }, 400); + } + + // Return whether track changed (to trigger image update) + return trackChanged; +} + +/** + * Updates both the album art and background image with cache-busting timestamp + * Only called when track actually changes + */ +function updateAlbumArt() { + const albumArt = document.querySelector(".album-art"); + const backgroundImage = document.querySelector(".background-image"); + const timestamp = new Date().getTime(); + const imageUrl = `${IMAGE_FILENAME}?v=${timestamp}`; + + albumArt.src = imageUrl; + backgroundImage.src = imageUrl; +} + +/** + * Main refresh function - fetches data and updates if changed + */ +async function refresh() { + const data = await fetchSongData(); + if (data) { + const trackChanged = updateSongDisplay(data.artist, data.title); + // Only update images when track actually changes + if (trackChanged) { + updateAlbumArt(); + } + } +} + +/////////////// +// INITIALIZE // +/////////////// + +function init() { + // Load initial data immediately + refresh(); + + // Set up refresh interval based on URL parameter or default + refreshInterval = setInterval(refresh, refreshRate * 1000); + + console.log(`Radio DJ widget initialized - refreshing every ${refreshRate}s`); +} + +// Start when DOM is ready +if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", init); +} else { + init(); +} diff --git a/style.css b/style.css new file mode 100644 index 0000000..ecfa75f --- /dev/null +++ b/style.css @@ -0,0 +1,143 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + background: #000; + display: flex; + justify-content: center; + align-items: center; + min-height: 100vh; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif; +} + +/* Main widget container */ +.widget-container { + position: relative; + display: flex; + width: 100%; + max-width: 650px; + aspect-ratio: auto; + height: 200px; + border-radius: 20px; + overflow: hidden; + box-shadow: 0 12px 40px rgba(0, 0, 0, 0.95); + margin: 20px; +} + +/* Blurred background layer (z-index: 0) */ +.background-blur { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 0; + overflow: hidden; +} + +.background-image { + width: 100%; + height: 100%; + object-fit: cover; + filter: blur(40px) brightness(0.6); +} + +/* Dark overlay (z-index: 1) */ +.overlay { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.5); + z-index: 1; + pointer-events: none; +} + +/* Content layer (z-index: 2) */ +.widget-content { + position: relative; + display: flex; + gap: 24px; + padding: 20px; + z-index: 2; + width: 100%; + height: 100%; + align-items: center; +} + +/* Album art box (left) */ +.album-art-box { + flex-shrink: 0; + width: 140px; + height: 140px; + border-radius: 10px; + overflow: hidden; + background: rgba(60, 60, 60, 0.5); + display: flex; + justify-content: center; + align-items: center; + box-shadow: 0 6px 16px rgba(0, 0, 0, 0.6); +} + +.album-art { + width: 100%; + height: 100%; + object-fit: cover; +} + +/* Song info box (right) */ +.song-info { + flex: 1; + display: flex; + flex-direction: column; + justify-content: center; + gap: 4px; + min-width: 0; + padding: 0 12px; +} + +/* Song title */ +.song-title { + font-size: 30px; + font-weight: 700; + color: #ffffff; + text-shadow: 2px 2px 5px rgba(0, 0, 0, 0.9); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + letter-spacing: -0.5px; + line-height: 1.1; +} + +/* Song artist */ +.song-artist { + font-size: 22px; + font-weight: 500; + color: #ffffff; + text-shadow: 2px 2px 5px rgba(0, 0, 0, 0.9); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + letter-spacing: -0.3px; + line-height: 1.1; + opacity: 1; +} + +/* Smooth fade animation for updates */ +@keyframes fadeIn { + from { + opacity: 0.5; + } + to { + opacity: 1; + } +} + +.song-title.updating, +.song-artist.updating { + animation: fadeIn 0.4s ease-in-out; +}