stream: show a random Navidrome-style background image
All checks were successful
continuous-integration/drone/push Build is passing

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
nemunaire 2026-07-03 18:34:53 +08:00
commit 85cd5d1b74

View file

@ -14,6 +14,19 @@
background: radial-gradient(circle at 30% 20%, #2a2140, #0d0b14 70%);
color: #f2f0f7;
}
/* Fond façon écran de connexion Navidrome : une image aléatoire tirée de
leur galerie, posée derrière la carte avec un voile sombre pour garder
le texte lisible. Le dégradé du body reste visible tant que l'image
n'est pas chargée (ou en cas d'échec réseau). */
.bg {
position: fixed; inset: 0; z-index: -1; background-size: cover;
background-position: center; opacity: 0; transition: opacity .8s ease;
}
.bg.loaded { opacity: 1; }
.bg::after {
content: ""; position: absolute; inset: 0;
background: radial-gradient(circle at 30% 20%, rgba(20,16,34,.72), rgba(13,11,20,.9) 75%);
}
.card {
width: min(90vw, 420px); padding: 2.5rem 2rem;
background: rgba(255,255,255,.04); border: 1px solid rgba(255,255,255,.08);
@ -82,6 +95,7 @@
</style>
</head>
<body>
<div class="bg" id="bg"></div>
<main class="card">
<div class="logo" id="stationName"></div>
<div class="np-label"><span class="dot"></span><span id="npLabel">Préchargement</span></div>
@ -107,6 +121,33 @@
document.getElementById("stationName").textContent = "◈ " + STATION_NAME;
document.getElementById("pageTitle").textContent = STATION_NAME;
// Fond aléatoire repris de l'écran de connexion Navidrome : on récupère la
// liste de leur galerie (index.yml, CORS ouvert), on tire un nom au hasard
// et on sert la version .webp depuis leur CDN — même logique que Navidrome,
// qui retire l'extension du nom listé. On précharge l'image avant de
// l'afficher pour éviter tout flash, et on ignore silencieusement les
// échecs (le dégradé du body reste alors le fond).
(async () => {
const BASE = "https://www.navidrome.org/images/";
try {
const r = await fetch(BASE + "index.yml", { cache: "no-store" });
const names = (await r.text())
.split("\n")
.map((l) => l.replace(/^\s*-\s*/, "").trim())
.filter(Boolean);
if (!names.length) return;
const name = names[Math.floor(Math.random() * names.length)];
const url = BASE + name.replace(/\.[^.]+$/, "") + ".webp";
const img = new Image();
img.onload = () => {
const bg = document.getElementById("bg");
bg.style.backgroundImage = `url("${url}")`;
bg.classList.add("loaded");
};
img.src = url;
} catch (e) { /* pas de fond : on garde le dégradé */ }
})();
const titleEl = document.getElementById("title");
const artistEl = document.getElementById("artist");
const npLabel = document.getElementById("npLabel");