My personal site hosted @ https://indexx.dev

Revert "feat: restructure last.fm recent track display"

This reverts commit 31128336c73f114f602b655b5ea90d55f20aee1d.

Index 87cdce2c 31128336

+1 -2
.env.example
···
-
PUBLIC_ACTOR_DID=""
-
LASTFM_API_KEY=""
+
PUBLIC_ACTOR_DID=""
+1 -7
astro.config.mjs
···
import react from "@astrojs/react";
-
import node from "@astrojs/node";
-
// https://astro.build/config
export default defineConfig({
integrations: [react()],
-
-
adapter: node({
-
mode: "standalone",
-
}),
-
});
+
});
-169
package-lock.json
···
"name": "static",
"version": "0.0.1",
"dependencies": {
-
"@astrojs/node": "^9.2.2",
"@astrojs/react": "^4.3.0",
"@atproto/api": "^0.15.6",
"@types/react": "^19.1.6",
···
"unist-util-visit": "^5.0.0",
"unist-util-visit-parents": "^6.0.1",
"vfile": "^6.0.3"
-
}
-
},
-
"node_modules/@astrojs/node": {
-
"version": "9.2.2",
-
"resolved": "https://registry.npmjs.org/@astrojs/node/-/node-9.2.2.tgz",
-
"integrity": "sha512-PtLPuuojmcl9O3CEvXqL/D+wB4x5DlbrGOvP0MeTAh/VfKFprYAzgw1+45xsnTO+QvPWb26l1cT+ZQvvohmvMw==",
-
"dependencies": {
-
"@astrojs/internal-helpers": "0.6.1",
-
"send": "^1.2.0",
-
"server-destroy": "^1.0.1"
-
},
-
"peerDependencies": {
-
"astro": "^5.3.0"
}
},
"node_modules/@astrojs/prism": {
···
"resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz",
"integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="
},
-
"node_modules/depd": {
-
"version": "2.0.0",
-
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
-
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
-
"engines": {
-
"node": ">= 0.8"
-
}
-
},
"node_modules/dequal": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
···
"node": ">=4"
},
-
"node_modules/ee-first": {
-
"version": "1.1.1",
-
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
-
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
-
},
"node_modules/electron-to-chromium": {
"version": "1.5.165",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.165.tgz",
···
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz",
"integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw=="
},
-
"node_modules/encodeurl": {
-
"version": "2.0.0",
-
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
-
"integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
-
"engines": {
-
"node": ">= 0.8"
-
}
-
},
"node_modules/entities": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-6.0.0.tgz",
···
"node": ">=6"
},
-
"node_modules/escape-html": {
-
"version": "1.0.3",
-
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
-
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
-
},
"node_modules/escape-string-regexp": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
···
"@types/estree": "^1.0.0"
},
-
"node_modules/etag": {
-
"version": "1.8.1",
-
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
-
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
-
"engines": {
-
"node": ">= 0.6"
-
}
-
},
"node_modules/eventemitter3": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
···
"unicode-trie": "^2.0.0"
},
-
"node_modules/fresh": {
-
"version": "2.0.0",
-
"resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
-
"integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==",
-
"engines": {
-
"node": ">= 0.8"
-
}
-
},
"node_modules/fs": {
"version": "0.0.1-security",
"resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz",
···
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz",
"integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ=="
},
-
"node_modules/http-errors": {
-
"version": "2.0.0",
-
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
-
"integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
-
"dependencies": {
-
"depd": "2.0.0",
-
"inherits": "2.0.4",
-
"setprototypeof": "1.2.0",
-
"statuses": "2.0.1",
-
"toidentifier": "1.0.1"
-
},
-
"engines": {
-
"node": ">= 0.8"
-
}
-
},
-
"node_modules/http-errors/node_modules/statuses": {
-
"version": "2.0.1",
-
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
-
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
-
"engines": {
-
"node": ">= 0.8"
-
}
-
},
"node_modules/import-meta-resolve": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz",
···
"type": "github",
"url": "https://github.com/sponsors/wooorm"
-
},
-
"node_modules/inherits": {
-
"version": "2.0.4",
-
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
-
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"node_modules/iron-webcrypto": {
"version": "1.2.1",
···
},
-
"node_modules/mime-db": {
-
"version": "1.54.0",
-
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
-
"integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
-
"engines": {
-
"node": ">= 0.6"
-
}
-
},
-
"node_modules/mime-types": {
-
"version": "3.0.1",
-
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz",
-
"integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==",
-
"dependencies": {
-
"mime-db": "^1.54.0"
-
},
-
"engines": {
-
"node": ">= 0.6"
-
}
-
},
"node_modules/mrmime": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
···
"resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz",
"integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ=="
},
-
"node_modules/on-finished": {
-
"version": "2.4.1",
-
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
-
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
-
"dependencies": {
-
"ee-first": "1.1.1"
-
},
-
"engines": {
-
"node": ">= 0.8"
-
}
-
},
"node_modules/oniguruma-parser": {
"version": "0.12.1",
"resolved": "https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.12.1.tgz",
···
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/radix3/-/radix3-1.1.2.tgz",
"integrity": "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA=="
-
},
-
"node_modules/range-parser": {
-
"version": "1.2.1",
-
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
-
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
-
"engines": {
-
"node": ">= 0.6"
-
}
},
"node_modules/react": {
"version": "19.1.0",
···
"node": ">=10"
},
-
"node_modules/send": {
-
"version": "1.2.0",
-
"resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz",
-
"integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==",
-
"dependencies": {
-
"debug": "^4.3.5",
-
"encodeurl": "^2.0.0",
-
"escape-html": "^1.0.3",
-
"etag": "^1.8.1",
-
"fresh": "^2.0.0",
-
"http-errors": "^2.0.0",
-
"mime-types": "^3.0.1",
-
"ms": "^2.1.3",
-
"on-finished": "^2.4.1",
-
"range-parser": "^1.2.1",
-
"statuses": "^2.0.1"
-
},
-
"engines": {
-
"node": ">= 18"
-
}
-
},
-
"node_modules/server-destroy": {
-
"version": "1.0.1",
-
"resolved": "https://registry.npmjs.org/server-destroy/-/server-destroy-1.0.1.tgz",
-
"integrity": "sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ=="
-
},
-
"node_modules/setprototypeof": {
-
"version": "1.2.0",
-
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
-
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
-
},
"node_modules/sharp": {
"version": "0.33.5",
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz",
···
"url": "https://github.com/sponsors/wooorm"
},
-
"node_modules/statuses": {
-
"version": "2.0.2",
-
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
-
"integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
-
"engines": {
-
"node": ">= 0.8"
-
}
-
},
"node_modules/string-width": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
···
"integrity": "sha512-XGhStWuOlBA5D8QnyN2xtgB2cUOdJ3ztisne1DYVWMcVH29qh8eQIpRmP3HnuJLdgyzG0HpdGzRMu1lm/Oictw==",
"bin": {
"tlds": "bin.js"
-
}
-
},
-
"node_modules/toidentifier": {
-
"version": "1.0.1",
-
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
-
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
-
"engines": {
-
"node": ">=0.6"
},
"node_modules/tr46": {
-1
package.json
···
"astro": "astro"
},
"dependencies": {
-
"@astrojs/node": "^9.2.2",
"@astrojs/react": "^4.3.0",
"@atproto/api": "^0.15.6",
"@types/react": "^19.1.6",
+2
robots.txt
···
+
User-agent: *
+
Disallow:
+18 -8
src/components/NowPlaying.jsx
···
useEffect(() => {
const fetchNowPlaying = async () => {
try {
-
const res = await fetch("/api/now-playing");
+
const res = await fetch(
+
"https://lastfm-last-played.biancarosa.com.br/Index_Card/latest-song",
+
);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const json = await res.json();
+
const track = json.track;
+
const isNowPlaying = track["@attr"]?.nowplaying === "true";
+
const status = {
-
song: json.track,
-
artist: json.artist,
-
albumArt: json.cover,
-
createdAt: json.createdAt,
-
link: json.url,
-
nowPlaying: !json.createdAt,
+
song: track.name,
+
artist: track.artist["#text"],
+
album: track.album["#text"],
+
createdAt: track.date
+
? new Date(track.date.uts * 1000).toISOString()
+
: null,
+
link: track.url,
+
nowPlaying: isNowPlaying,
+
albumArt: track.image.find((img) =>
+
img.size === "medium"
+
)["#text"],
};
setData(status);
} catch (err) {
···
}, []);
if (error) return <span>Error: {error}</span>;
-
if (!data) return null;
+
if (!data) return <span>Loading now playing...</span>;
let timeAgo = "";
let oldStatusClasses = "";
-4
src/components/SocialLinks.astro
···
{
name: 'GitHub',
href: 'https://github.com/indexxing/'
-
},
-
{
-
name: 'last.fm',
-
href: 'https://last.fm/user/Index_Card'
}
];
---
+2 -20
src/components/Status.jsx
···
fetchStatus();
}, [actorDid]);
-
if (error) {
-
return (
-
<span
-
className="badge bg-dark"
-
style={{ color: "#595959 !important" }}
-
>
-
Error: {error}
-
</span>
-
);
-
}
-
if (!data) {
-
return (
-
<span
-
className="badge bg-dark"
-
style={{ color: "#595959 !important" }}
-
>
-
Loading status...
-
</span>
-
);
-
}
+
if (error) return <span>Error: {error}</span>;
+
if (!data) return <span>Loading status...</span>;
const date = new Date(data.createdAt);
const now = new Date();
-76
src/pages/api/now-playing.ts
···
-
import type { APIRoute } from "astro";
-
const lastfmAPIKey = import.meta.env.LASTFM_API_KEY;
-
-
if (!lastfmAPIKey) {
-
console.error(
-
"LASTFM_API_KEY is not defined. Please check your .env file.",
-
);
-
}
-
-
interface CacheItem {
-
data: any;
-
timestamp: number;
-
}
-
-
let cache: CacheItem | null = null;
-
const CACHE_DURATION = 5 * 60 * 1000; // 5 minutes in milliseconds
-
-
export const prerender = false;
-
-
export const GET: APIRoute = async () => {
-
const now = Date.now();
-
-
if (cache && (now - cache.timestamp) < CACHE_DURATION) {
-
return Response.json(cache.data, {
-
status: 200,
-
headers: {
-
"X-Index-API-Cached": "true", // Debug
-
"Cache-Control": "max-age=300", // 5 minutes
-
},
-
});
-
}
-
-
try {
-
const apiRes = await fetch(
-
`http://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&user=Index_Card&api_key=${lastfmAPIKey}&format=json`,
-
);
-
-
if (!apiRes.ok) {
-
return Response.json({}, {
-
status: 502,
-
});
-
}
-
-
const apiData: LastFmApiResponse = await apiRes.json();
-
const recentTrack = apiData.recenttracks.track[0];
-
-
const data = {
-
track: recentTrack.name,
-
artist: recentTrack.artist["#text"],
-
cover: recentTrack.image.find((img) =>
-
img.size == "medium"
-
)!["#text"],
-
url: recentTrack.url,
-
createdAt: recentTrack.date
-
? new Date(parseInt(recentTrack.date.uts) * 1000).toISOString()
-
: null,
-
};
-
-
cache = {
-
data,
-
timestamp: Date.now(),
-
};
-
-
return Response.json(data, {
-
status: 200,
-
headers: {
-
"X-Index-API-Cached": "true", // Debug
-
"Cache-Control": "max-age=300", // 5 minutes
-
},
-
});
-
} catch (error) {
-
return Response.json({}, {
-
status: 500,
-
});
-
}
-
};
-52
types/lastfm.d.ts
···
-
interface LastFmImage {
-
size: "small" | "medium" | "large" | "extralarge";
-
"#text": string;
-
}
-
-
interface LastFmArtist {
-
mbid: string;
-
"#text": string;
-
}
-
-
interface LastFmAlbum {
-
mbid: string;
-
"#text": string;
-
}
-
-
interface LastFmDate {
-
uts: string;
-
"#text": string;
-
}
-
-
interface LastFmTrackAttr {
-
nowplaying?: boolean;
-
}
-
-
interface LastFmTrack {
-
artist: LastFmArtist;
-
streamable: boolean;
-
image: LastFmImage[];
-
mbid: string;
-
album: LastFmAlbum;
-
name: string;
-
url: string;
-
date: LastFmDate;
-
"@attr"?: LastFmTrackAttr;
-
}
-
-
interface LastFmRecentTracksAttr {
-
user: string;
-
totalPages: string;
-
page: string;
-
perPage: string;
-
total: string;
-
}
-
-
interface LastFmRecentTracks {
-
track: LastFmTrack[];
-
"@attr": LastFmRecentTracksAttr;
-
}
-
-
interface LastFmApiResponse {
-
recenttracks: LastFmRecentTracks;
-
}