diff --git a/js/listening.js b/js/listening.js index 14e58e0..480b99e 100644 --- a/js/listening.js +++ b/js/listening.js @@ -10,7 +10,18 @@ document.addEventListener("alpine:init", () => { async function apiFetch(path) { const r = await fetch(path); if (!r.ok) throw new Error(`HTTP ${r.status}`); - return r.json(); + const data = await r.json(); + try { localStorage.setItem("lp:" + path, JSON.stringify({ t: Date.now(), d: data })); } catch {} + return data; + } + + // Read cached API response; returns null on miss or parse error + function readCache(path) { + try { + const s = localStorage.getItem("lp:" + path); + if (!s) return null; + return JSON.parse(s).d ?? null; + } catch { return null; } } function mergeListens(fw = [], lfm = []) { @@ -48,7 +59,20 @@ document.addEventListener("alpine:init", () => { lfmNowPlaying: null, lfmScrobbles: [], + _loadCaches() { + const fwNp = readCache("/funkwhale/api/now-playing"); + const fwList = readCache("/funkwhale/api/listenings?limit=2"); + const lfmNp = readCache("/lastfmapi/api/now-playing"); + const lfmSc = readCache("/lastfmapi/api/scrobbles?limit=2"); + if (fwNp) this.fwNowPlaying = { ...fwNp, status: null }; // no stale "Now Playing" + if (fwList) this.fwListenings = fwList.listenings || []; + if (lfmNp) this.lfmNowPlaying = { ...lfmNp, status: null }; + if (lfmSc) this.lfmScrobbles = lfmSc.scrobbles || []; + return !!(fwNp || fwList || lfmNp || lfmSc); + }, + async init() { + if (this._loadCaches()) this.loading = false; try { const [fwNp, fwList, lfmNp, lfmList] = await Promise.allSettled([ apiFetch("/funkwhale/api/now-playing"), @@ -141,7 +165,37 @@ document.addEventListener("alpine:init", () => { lfm: { nowPlaying: null, scrobbles: [], loved: [], stats: null }, activeSource: "all", + _loadCaches() { + const fwNp = readCache("/funkwhale/api/now-playing"); + const fwList = readCache("/funkwhale/api/listenings"); + const fwFav = readCache("/funkwhale/api/favorites"); + const fwSt = readCache("/funkwhale/api/stats"); + const lfmNp = readCache("/lastfmapi/api/now-playing"); + const lfmSc = readCache("/lastfmapi/api/scrobbles?period=alltime&limit=20"); + const lfmLv = readCache("/lastfmapi/api/loved?limit=10"); + const lfmSt = readCache("/lastfmapi/api/stats?period=alltime"); + + const hasFw = fwNp || fwList || fwFav || fwSt; + const hasLfm = lfmNp || lfmSc || lfmLv || lfmSt; + + if (hasFw) { + if (fwNp) this.fw.nowPlaying = { ...fwNp, status: null }; // no stale "Now Playing" + if (fwList) this.fw.listenings = fwList.listenings || []; + if (fwFav) this.fw.favorites = fwFav.favorites || []; + if (fwSt) this.fw.stats = fwSt; + this.fwLoading = false; + } + if (hasLfm) { + if (lfmNp) this.lfm.nowPlaying = { ...lfmNp, status: null }; + if (lfmSc) this.lfm.scrobbles = lfmSc.scrobbles || []; + if (lfmLv) this.lfm.loved = lfmLv.loved || []; + if (lfmSt) this.lfm.stats = lfmSt; + this.lfmLoading = false; + } + }, + async init() { + this._loadCaches(); await Promise.all([this._fetchFunkwhale(), this._fetchLastfm()]); },