`
: ``;
const close = t.url ? "" : "
";
return `
${link}
${cover}
${escapeHtml(t.title)}
${artists || "—"}${context}
${dayLabel(t)}
${close}
`;
}).join("");
list.innerHTML = html || 'Пока нет прослушиваний';
st.firstLoad = false;
};
const setStatus = (msg, isError) => {
list.innerHTML = `${escapeHtml(msg)}`;
};
const tick = async () => {
if (busy) return;
busy = true;
try {
const res = await fetch(SOURCES[current], { headers: { Accept: "application/json" } });
const data = await res.json();
if (!res.ok || !data.ok) {
setStatus(data.error || `Ошибка ${res.status}`, true);
return;
}
render(data.tracks || []);
const t = new Date(data.updated_at * 1000);
updated.textContent = `обновлено ${t.toLocaleTimeString("ru-RU")}${data.stale ? " (кэш)" : ""}`;
} catch (e) {
setStatus("Нет связи с сервером", true);
} finally {
busy = false;
if (timer) clearTimeout(timer);
timer = setTimeout(tick, POLL_MS);
}
};
const poke = () => { if (timer) clearTimeout(timer); tick(); };
tabs.forEach((tab) => {
tab.addEventListener("click", () => {
const source = tab.dataset.source;
if (!source || source === current) return;
current = source;
tabs.forEach((t) => t.classList.toggle("active", t === tab));
updated.textContent = "подключение…";
poke();
});
});
if (refresh) refresh.addEventListener("click", poke);
document.addEventListener("visibilitychange", () => {
if (!document.hidden) poke();
});
tick();
})();