(() => { "use strict"; const list = document.getElementById("ymHistory"); const updated = document.getElementById("ymUpdated"); const refresh = document.getElementById("ymRefresh"); if (!list || !updated) return; const POLL_MS = 30000; const seen = new Set(); let firstLoad = true; let timer = null; let busy = false; const escapeHtml = (s) => String(s).replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'", })[c]); const dayLabel = (day) => { if (!day) return ""; const today = new Date().toISOString().slice(0, 10); const yest = new Date(Date.now() - 86400000).toISOString().slice(0, 10); if (day === today) return "сегодня"; if (day === yest) return "вчера"; const [y, m, d] = day.split("-"); return `${d}.${m}.${y}`; }; const render = (tracks) => { const fresh = []; const html = tracks.map((t) => { const key = `${t.id}:${t.album_id || ""}:${t.day}`; if (!seen.has(key)) { seen.add(key); if (!firstLoad) fresh.push(key); } const isNew = fresh.includes(key); const cover = t.cover ? `` : ``; const artists = (t.artists || []).map(escapeHtml).join(", "); const context = t.context ? ` · ${escapeHtml(t.context)}` : ""; return `
  • ${cover} ${escapeHtml(t.title)} ${artists || "—"}${context} ${dayLabel(t.day)}
  • `; }).join(""); list.innerHTML = html || '
  • Пока нет прослушиваний за последние дни
  • '; firstLoad = false; }; const setStatus = (msg, isError) => { list.innerHTML = `
  • ${escapeHtml(msg)}
  • `; }; const tick = async () => { if (busy) return; busy = true; try { const res = await fetch("/api/ym/history", { 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(); }; if (refresh) refresh.addEventListener("click", poke); document.addEventListener("visibilitychange", () => { if (!document.hidden) poke(); }); tick(); })();