diff --git a/53eb00aa300595b54d999e60d501f621ad4399918c568561f74b536b6f37c0ab.riv b/53eb00aa300595b54d999e60d501f621ad4399918c568561f74b536b6f37c0ab.riv
deleted file mode 100644
index 132d22b..0000000
Binary files a/53eb00aa300595b54d999e60d501f621ad4399918c568561f74b536b6f37c0ab.riv and /dev/null differ
diff --git a/88x31/index.html b/88x31/index.html
index bb48f4e..26855c3 100644
--- a/88x31/index.html
+++ b/88x31/index.html
@@ -73,48 +73,48 @@
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
+
+
+
-
-
+
+
-
-
-
-
+
+
+
+
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/assets/88x31/antifa.gif b/assets/88x31/antifa.gif
deleted file mode 100644
index 0809cab..0000000
Binary files a/assets/88x31/antifa.gif and /dev/null differ
diff --git a/assets/88x31/antifa.png b/assets/88x31/antifa.png
new file mode 100644
index 0000000..e054682
Binary files /dev/null and b/assets/88x31/antifa.png differ
diff --git a/assets/88x31/badapple.gif b/assets/88x31/badapple.gif
deleted file mode 100644
index af20fa1..0000000
Binary files a/assets/88x31/badapple.gif and /dev/null differ
diff --git a/assets/88x31/badapple.png b/assets/88x31/badapple.png
new file mode 100644
index 0000000..fa48c62
Binary files /dev/null and b/assets/88x31/badapple.png differ
diff --git a/assets/88x31/bestvieweddesktop.gif b/assets/88x31/bestvieweddesktop.gif
deleted file mode 100644
index 06620c0..0000000
Binary files a/assets/88x31/bestvieweddesktop.gif and /dev/null differ
diff --git a/assets/88x31/bestvieweddesktop.png b/assets/88x31/bestvieweddesktop.png
new file mode 100644
index 0000000..68b0c78
Binary files /dev/null and b/assets/88x31/bestvieweddesktop.png differ
diff --git a/assets/88x31/blink.gif b/assets/88x31/blink.gif
deleted file mode 100644
index 2acde1c..0000000
Binary files a/assets/88x31/blink.gif and /dev/null differ
diff --git a/assets/88x31/blink.png b/assets/88x31/blink.png
new file mode 100644
index 0000000..9656324
Binary files /dev/null and b/assets/88x31/blink.png differ
diff --git a/assets/88x31/caramelldansen.gif b/assets/88x31/caramelldansen.gif
deleted file mode 100644
index a79c0a5..0000000
Binary files a/assets/88x31/caramelldansen.gif and /dev/null differ
diff --git a/assets/88x31/caramelldansen.png b/assets/88x31/caramelldansen.png
new file mode 100644
index 0000000..1907831
Binary files /dev/null and b/assets/88x31/caramelldansen.png differ
diff --git a/assets/88x31/cutesocks.gif b/assets/88x31/cutesocks.gif
deleted file mode 100644
index 69af349..0000000
Binary files a/assets/88x31/cutesocks.gif and /dev/null differ
diff --git a/assets/88x31/cutesocks.png b/assets/88x31/cutesocks.png
new file mode 100644
index 0000000..b770a7f
Binary files /dev/null and b/assets/88x31/cutesocks.png differ
diff --git a/assets/88x31/dark-mode.gif b/assets/88x31/dark-mode.gif
deleted file mode 100644
index 3171661..0000000
Binary files a/assets/88x31/dark-mode.gif and /dev/null differ
diff --git a/assets/88x31/dark-mode.png b/assets/88x31/dark-mode.png
new file mode 100644
index 0000000..85cce60
Binary files /dev/null and b/assets/88x31/dark-mode.png differ
diff --git a/assets/88x31/discord.gif b/assets/88x31/discord.gif
deleted file mode 100644
index 8b8b457..0000000
Binary files a/assets/88x31/discord.gif and /dev/null differ
diff --git a/assets/88x31/discord.png b/assets/88x31/discord.png
new file mode 100644
index 0000000..b4c90af
Binary files /dev/null and b/assets/88x31/discord.png differ
diff --git a/assets/88x31/doughmination.gif b/assets/88x31/doughmination.gif
deleted file mode 100644
index 20af87e..0000000
Binary files a/assets/88x31/doughmination.gif and /dev/null differ
diff --git a/assets/88x31/doughmination.png b/assets/88x31/doughmination.png
new file mode 100644
index 0000000..e5836fc
Binary files /dev/null and b/assets/88x31/doughmination.png differ
diff --git a/assets/88x31/estrogen.gif b/assets/88x31/estrogen.gif
deleted file mode 100644
index 4c965dd..0000000
Binary files a/assets/88x31/estrogen.gif and /dev/null differ
diff --git a/assets/88x31/estrogen.png b/assets/88x31/estrogen.png
new file mode 100644
index 0000000..d587bdc
Binary files /dev/null and b/assets/88x31/estrogen.png differ
diff --git a/assets/88x31/firefox.gif b/assets/88x31/firefox.gif
deleted file mode 100644
index 94621d5..0000000
Binary files a/assets/88x31/firefox.gif and /dev/null differ
diff --git a/assets/88x31/firefox.png b/assets/88x31/firefox.png
new file mode 100644
index 0000000..42a0b56
Binary files /dev/null and b/assets/88x31/firefox.png differ
diff --git a/assets/88x31/htmldream.gif b/assets/88x31/htmldream.gif
deleted file mode 100644
index 28fd2c2..0000000
Binary files a/assets/88x31/htmldream.gif and /dev/null differ
diff --git a/assets/88x31/htmldream.png b/assets/88x31/htmldream.png
new file mode 100644
index 0000000..e73739d
Binary files /dev/null and b/assets/88x31/htmldream.png differ
diff --git a/assets/88x31/killmenow.gif b/assets/88x31/killmenow.gif
deleted file mode 100644
index 0dbe965..0000000
Binary files a/assets/88x31/killmenow.gif and /dev/null differ
diff --git a/assets/88x31/killmenow.png b/assets/88x31/killmenow.png
new file mode 100644
index 0000000..48391de
Binary files /dev/null and b/assets/88x31/killmenow.png differ
diff --git a/assets/88x31/linux.gif b/assets/88x31/linux.gif
deleted file mode 100644
index 89e3562..0000000
Binary files a/assets/88x31/linux.gif and /dev/null differ
diff --git a/assets/88x31/linux.png b/assets/88x31/linux.png
new file mode 100644
index 0000000..709a67c
Binary files /dev/null and b/assets/88x31/linux.png differ
diff --git a/assets/88x31/macbutton.gif b/assets/88x31/macbutton.gif
deleted file mode 100644
index 4292104..0000000
Binary files a/assets/88x31/macbutton.gif and /dev/null differ
diff --git a/assets/88x31/macbutton.png b/assets/88x31/macbutton.png
new file mode 100644
index 0000000..45fc12c
Binary files /dev/null and b/assets/88x31/macbutton.png differ
diff --git a/assets/88x31/meltice.gif b/assets/88x31/meltice.gif
deleted file mode 100644
index 7e6df98..0000000
Binary files a/assets/88x31/meltice.gif and /dev/null differ
diff --git a/assets/88x31/meltice.png b/assets/88x31/meltice.png
new file mode 100644
index 0000000..501c974
Binary files /dev/null and b/assets/88x31/meltice.png differ
diff --git a/assets/88x31/microslop.gif b/assets/88x31/microslop.gif
deleted file mode 100644
index 61936c7..0000000
Binary files a/assets/88x31/microslop.gif and /dev/null differ
diff --git a/assets/88x31/microslop.png b/assets/88x31/microslop.png
new file mode 100644
index 0000000..6f6f81c
Binary files /dev/null and b/assets/88x31/microslop.png differ
diff --git a/assets/88x31/miku.gif b/assets/88x31/miku.gif
deleted file mode 100644
index e61072d..0000000
Binary files a/assets/88x31/miku.gif and /dev/null differ
diff --git a/assets/88x31/miku.png b/assets/88x31/miku.png
new file mode 100644
index 0000000..3ffa06c
Binary files /dev/null and b/assets/88x31/miku.png differ
diff --git a/assets/88x31/minecraft.gif b/assets/88x31/minecraft.gif
deleted file mode 100644
index a149b45..0000000
Binary files a/assets/88x31/minecraft.gif and /dev/null differ
diff --git a/assets/88x31/minecraft.png b/assets/88x31/minecraft.png
new file mode 100644
index 0000000..c27aad2
Binary files /dev/null and b/assets/88x31/minecraft.png differ
diff --git a/assets/88x31/nft.gif b/assets/88x31/nft.gif
deleted file mode 100644
index 12f48ab..0000000
Binary files a/assets/88x31/nft.gif and /dev/null differ
diff --git a/assets/88x31/nft.png b/assets/88x31/nft.png
new file mode 100644
index 0000000..1408e2a
Binary files /dev/null and b/assets/88x31/nft.png differ
diff --git a/assets/88x31/no-chrome.gif b/assets/88x31/no-chrome.gif
deleted file mode 100644
index eabb7d8..0000000
Binary files a/assets/88x31/no-chrome.gif and /dev/null differ
diff --git a/assets/88x31/no-chrome.png b/assets/88x31/no-chrome.png
new file mode 100644
index 0000000..061f948
Binary files /dev/null and b/assets/88x31/no-chrome.png differ
diff --git a/assets/88x31/no.gif b/assets/88x31/no.gif
deleted file mode 100644
index 188b46c..0000000
Binary files a/assets/88x31/no.gif and /dev/null differ
diff --git a/assets/88x31/no.png b/assets/88x31/no.png
new file mode 100644
index 0000000..2653eb3
Binary files /dev/null and b/assets/88x31/no.png differ
diff --git a/assets/88x31/noweb32.gif b/assets/88x31/noweb32.gif
deleted file mode 100644
index 9485709..0000000
Binary files a/assets/88x31/noweb32.gif and /dev/null differ
diff --git a/assets/88x31/noweb32.png b/assets/88x31/noweb32.png
new file mode 100644
index 0000000..1bc510f
Binary files /dev/null and b/assets/88x31/noweb32.png differ
diff --git a/assets/88x31/nowebp.gif b/assets/88x31/nowebp.gif
deleted file mode 100644
index e63f1f2..0000000
Binary files a/assets/88x31/nowebp.gif and /dev/null differ
diff --git a/assets/88x31/nowebp.png b/assets/88x31/nowebp.png
new file mode 100644
index 0000000..2e55df1
Binary files /dev/null and b/assets/88x31/nowebp.png differ
diff --git a/assets/88x31/palestine.gif b/assets/88x31/palestine.gif
deleted file mode 100644
index e58600b..0000000
Binary files a/assets/88x31/palestine.gif and /dev/null differ
diff --git a/assets/88x31/palestine.png b/assets/88x31/palestine.png
new file mode 100644
index 0000000..6a0fcc5
Binary files /dev/null and b/assets/88x31/palestine.png differ
diff --git a/assets/88x31/pokemon.gif b/assets/88x31/pokemon.gif
deleted file mode 100644
index 996001c..0000000
Binary files a/assets/88x31/pokemon.gif and /dev/null differ
diff --git a/assets/88x31/pokemon.png b/assets/88x31/pokemon.png
new file mode 100644
index 0000000..a4a229b
Binary files /dev/null and b/assets/88x31/pokemon.png differ
diff --git a/assets/88x31/queerpride.gif b/assets/88x31/queerpride.gif
deleted file mode 100644
index 6749c9d..0000000
Binary files a/assets/88x31/queerpride.gif and /dev/null differ
diff --git a/assets/88x31/queerpride.png b/assets/88x31/queerpride.png
new file mode 100644
index 0000000..bf8b0e0
Binary files /dev/null and b/assets/88x31/queerpride.png differ
diff --git a/assets/88x31/skirt.gif b/assets/88x31/skirt.gif
deleted file mode 100644
index e2833dc..0000000
Binary files a/assets/88x31/skirt.gif and /dev/null differ
diff --git a/assets/88x31/skirt.png b/assets/88x31/skirt.png
new file mode 100644
index 0000000..fc7af69
Binary files /dev/null and b/assets/88x31/skirt.png differ
diff --git a/assets/88x31/transnow.gif b/assets/88x31/transnow.gif
deleted file mode 100644
index 7f705aa..0000000
Binary files a/assets/88x31/transnow.gif and /dev/null differ
diff --git a/assets/88x31/transnow.png b/assets/88x31/transnow.png
new file mode 100644
index 0000000..f310c33
Binary files /dev/null and b/assets/88x31/transnow.png differ
diff --git a/assets/88x31/tummy.gif b/assets/88x31/tummy.gif
deleted file mode 100644
index db75e06..0000000
Binary files a/assets/88x31/tummy.gif and /dev/null differ
diff --git a/assets/88x31/tummy.png b/assets/88x31/tummy.png
new file mode 100644
index 0000000..e221f18
Binary files /dev/null and b/assets/88x31/tummy.png differ
diff --git a/assets/88x31/ukraine.gif b/assets/88x31/ukraine.gif
deleted file mode 100644
index 501b734..0000000
Binary files a/assets/88x31/ukraine.gif and /dev/null differ
diff --git a/assets/88x31/ukraine.png b/assets/88x31/ukraine.png
new file mode 100644
index 0000000..c3e6c34
Binary files /dev/null and b/assets/88x31/ukraine.png differ
diff --git a/assets/88x31/underwearisforloser.gif b/assets/88x31/underwearisforloser.gif
deleted file mode 100644
index 7b3584a..0000000
Binary files a/assets/88x31/underwearisforloser.gif and /dev/null differ
diff --git a/assets/88x31/underwearisforloser.png b/assets/88x31/underwearisforloser.png
new file mode 100644
index 0000000..559b3eb
Binary files /dev/null and b/assets/88x31/underwearisforloser.png differ
diff --git a/assets/88x31/valid-css.gif b/assets/88x31/valid-css.gif
deleted file mode 100644
index 020c75a..0000000
Binary files a/assets/88x31/valid-css.gif and /dev/null differ
diff --git a/assets/88x31/valid-css.png b/assets/88x31/valid-css.png
new file mode 100644
index 0000000..af6cd55
Binary files /dev/null and b/assets/88x31/valid-css.png differ
diff --git a/assets/88x31/valid-html5.gif b/assets/88x31/valid-html5.gif
deleted file mode 100644
index 5cf8da1..0000000
Binary files a/assets/88x31/valid-html5.gif and /dev/null differ
diff --git a/assets/88x31/valid-html5.png b/assets/88x31/valid-html5.png
new file mode 100644
index 0000000..c081906
Binary files /dev/null and b/assets/88x31/valid-html5.png differ
diff --git a/assets/88x31/vscbutton.gif b/assets/88x31/vscbutton.gif
deleted file mode 100644
index 8a4cad2..0000000
Binary files a/assets/88x31/vscbutton.gif and /dev/null differ
diff --git a/assets/88x31/vscbutton.png b/assets/88x31/vscbutton.png
new file mode 100644
index 0000000..e474b42
Binary files /dev/null and b/assets/88x31/vscbutton.png differ
diff --git a/assets/88x31/winrar4.gif b/assets/88x31/winrar4.gif
deleted file mode 100644
index bb5761d..0000000
Binary files a/assets/88x31/winrar4.gif and /dev/null differ
diff --git a/assets/88x31/winrar4.png b/assets/88x31/winrar4.png
new file mode 100644
index 0000000..4e65425
Binary files /dev/null and b/assets/88x31/winrar4.png differ
diff --git a/js/music.js b/js/music.js
index 2246dde..12a52e5 100644
--- a/js/music.js
+++ b/js/music.js
@@ -517,31 +517,95 @@
}
// Last.fm stopped serving artist images — every artist returns the same
- // placeholder star — so we pull them from Deezer. Deezer's REST API blocks
- // CORS, but its JSONP mode (output=jsonp) doesn't. Resolves to a picture URL
- // (or "" when there's no match / it fails).
- function deezerArtistImg(name) {
- return new Promise(function (resolve) {
- if (!name) { resolve(""); return; }
- var cb = "__dz_" + Math.random().toString(36).slice(2);
- var s = document.createElement("script");
- var done = false;
- function finish(url) {
- if (done) return;
- done = true;
- try { delete window[cb]; s.remove(); } catch (e) { /* no-op */ }
- resolve(url || "");
+ // placeholder star. Spotify has the photos, but its catalog API needs an
+ // OAuth token (and a client secret we can't ship in a static page). So we
+ // take the keyless route: MusicBrainz maps an artist name → their linked
+ // Spotify page, then Spotify's own oEmbed hands back that page's picture.
+ // No API key, no token, no third-party cookies. Resolves to a picture URL
+ // (or "" when there's no confident match). Results are cached in
+ // localStorage, so repeat visits are instant and we stay gentle on the APIs.
+ const MB_ROOT = "https://musicbrainz.org/ws/2";
+ const ART_CACHE_PREFIX = "cstupidcat:artimg:";
+ const ART_TTL_HIT = 30 * 864e5; // remember a found photo for 30 days
+ const ART_TTL_MISS = 3 * 864e5; // retry a miss after 3 days
+
+ // MusicBrainz asks anonymous clients for ~1 request/second, so funnel every
+ // MB call through a one-at-a-time queue spaced ~1.1s apart.
+ let mbChain = Promise.resolve();
+ function mbFetch(url) {
+ const run = mbChain.then(() =>
+ fetch(url, { headers: { Accept: "application/json" } })
+ .then((r) => (r.ok ? r.json() : null))
+ .catch(() => null)
+ );
+ const gap = () => new Promise((r) => setTimeout(r, 1100));
+ mbChain = run.then(gap, gap); // pace the next call whether this one won or lost
+ return run;
+ }
+
+ function artCacheGet(name) {
+ try {
+ const raw = localStorage.getItem(ART_CACHE_PREFIX + name.toLowerCase());
+ if (!raw) return undefined;
+ const hit = JSON.parse(raw);
+ const ttl = hit.url ? ART_TTL_HIT : ART_TTL_MISS;
+ if (Date.now() - hit.ts > ttl) return undefined; // stale → re-resolve
+ return hit.url || "";
+ } catch (e) { return undefined; }
+ }
+ function artCacheSet(name, url) {
+ try {
+ localStorage.setItem(ART_CACHE_PREFIX + name.toLowerCase(),
+ JSON.stringify({ url: url || "", ts: Date.now() }));
+ } catch (e) { /* storage full / disabled — just skip caching */ }
+ }
+
+ // name → MusicBrainz artist MBID, but only when we're confident it's the
+ // right act: an exact (case-insensitive) name match or a strong search score.
+ async function mbArtistId(name) {
+ const q = encodeURIComponent('artist:"' + name.replace(/"/g, " ") + '"');
+ const data = await mbFetch(MB_ROOT + "/artist?query=" + q + "&limit=1&fmt=json");
+ const a = data && data.artists && data.artists[0];
+ if (!a) return "";
+ const same = (a.name || "").toLowerCase() === name.toLowerCase();
+ return (same || a.score >= 90) ? a.id : "";
+ }
+ // MBID → the artist's linked Spotify page (via MusicBrainz URL relationships)
+ async function mbSpotifyUrl(mbid) {
+ const data = await mbFetch(MB_ROOT + "/artist/" + mbid + "?inc=url-rels&fmt=json");
+ const rels = (data && data.relations) || [];
+ for (const r of rels) {
+ const u = r && r.url && r.url.resource;
+ if (u && u.indexOf("open.spotify.com/artist") !== -1) return u;
+ }
+ return "";
+ }
+ // Spotify page → its photo, via Spotify's public (keyless, CORS-open) oEmbed
+ async function spotifyOembedImg(spotifyUrl) {
+ try {
+ const res = await fetch("https://open.spotify.com/oembed?url=" +
+ encodeURIComponent(spotifyUrl));
+ if (!res.ok) return "";
+ const data = await res.json();
+ return data.thumbnail_url || "";
+ } catch (e) { return ""; }
+ }
+
+ // Public resolver used by loadTop(): artist name → picture URL ("" on miss).
+ async function artistImg(name) {
+ if (!name) return "";
+ const cached = artCacheGet(name);
+ if (cached !== undefined) return cached; // fresh hit or fresh miss
+ let url = "";
+ try {
+ const mbid = await mbArtistId(name);
+ if (mbid) {
+ const sp = await mbSpotifyUrl(mbid);
+ if (sp) url = await spotifyOembedImg(sp);
}
- window[cb] = function (data) {
- var a = data && data.data && data.data[0];
- finish(a ? (a.picture_medium || a.picture || "") : "");
- };
- s.onerror = function () { finish(""); };
- s.src = "https://api.deezer.com/search/artist?q=" + encodeURIComponent(name) +
- "&limit=1&output=jsonp&callback=" + cb;
- document.head.appendChild(s);
- setTimeout(function () { finish(""); }, 6000);
- });
+ } catch (e) { url = ""; }
+ artCacheSet(name, url);
+ return url;
}
async function loadTop() {
@@ -561,10 +625,10 @@
'' + esc(a.playcount) + " plays" +
"" +
"").join("") + "";
- // chips are already visible — fill in the artist images as Deezer answers
+ // chips are already visible — fill in the artist images as they resolve
const chips = topBox.querySelectorAll(".top-chip");
arr.forEach((a, i) => {
- deezerArtistImg(a.name).then((url) => {
+ artistImg(a.name).then((url) => {
if (!url) return;
const slot = chips[i] && chips[i].querySelector(".top-art");
if (!slot) return;