feat(index): add last played music via maloja
All checks were successful
build dist / build-dist (push) Successful in 29s
All checks were successful
build dist / build-dist (push) Successful in 29s
This commit is contained in:
parent
70dc2d08a6
commit
41d2c7cbac
6 changed files with 99 additions and 5 deletions
5
bun.lock
5
bun.lock
|
@ -8,6 +8,7 @@
|
||||||
"@biomejs/biome": "1.9.4",
|
"@biomejs/biome": "1.9.4",
|
||||||
"@fontsource-variable/geist": "^5.2.5",
|
"@fontsource-variable/geist": "^5.2.5",
|
||||||
"@tailwindcss/vite": "^4.1.4",
|
"@tailwindcss/vite": "^4.1.4",
|
||||||
|
"@types/node": "^22.14.1",
|
||||||
"@yeskunall/astro-umami": "^0.0.4",
|
"@yeskunall/astro-umami": "^0.0.4",
|
||||||
"astro": "^5.7.4",
|
"astro": "^5.7.4",
|
||||||
"motion": "^12.7.4",
|
"motion": "^12.7.4",
|
||||||
|
@ -281,6 +282,8 @@
|
||||||
|
|
||||||
"@types/nlcst": ["@types/nlcst@2.0.3", "", { "dependencies": { "@types/unist": "*" } }, "sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA=="],
|
"@types/nlcst": ["@types/nlcst@2.0.3", "", { "dependencies": { "@types/unist": "*" } }, "sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA=="],
|
||||||
|
|
||||||
|
"@types/node": ["@types/node@22.14.1", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw=="],
|
||||||
|
|
||||||
"@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
|
"@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
|
||||||
|
|
||||||
"@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="],
|
"@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="],
|
||||||
|
@ -837,6 +840,8 @@
|
||||||
|
|
||||||
"uncrypto": ["uncrypto@0.1.3", "", {}, "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q=="],
|
"uncrypto": ["uncrypto@0.1.3", "", {}, "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q=="],
|
||||||
|
|
||||||
|
"undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
|
||||||
|
|
||||||
"unicode-properties": ["unicode-properties@1.4.1", "", { "dependencies": { "base64-js": "^1.3.0", "unicode-trie": "^2.0.0" } }, "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg=="],
|
"unicode-properties": ["unicode-properties@1.4.1", "", { "dependencies": { "base64-js": "^1.3.0", "unicode-trie": "^2.0.0" } }, "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg=="],
|
||||||
|
|
||||||
"unicode-trie": ["unicode-trie@2.0.0", "", { "dependencies": { "pako": "^0.2.5", "tiny-inflate": "^1.0.0" } }, "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ=="],
|
"unicode-trie": ["unicode-trie@2.0.0", "", { "dependencies": { "pako": "^0.2.5", "tiny-inflate": "^1.0.0" } }, "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ=="],
|
||||||
|
|
|
@ -9,12 +9,13 @@
|
||||||
"astro": "astro"
|
"astro": "astro"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"astro": "^5.7.4",
|
|
||||||
"@astrojs/check": "^0.9.4",
|
"@astrojs/check": "^0.9.4",
|
||||||
"@biomejs/biome": "1.9.4",
|
"@biomejs/biome": "1.9.4",
|
||||||
"@fontsource-variable/geist": "^5.2.5",
|
"@fontsource-variable/geist": "^5.2.5",
|
||||||
"@tailwindcss/vite": "^4.1.4",
|
"@tailwindcss/vite": "^4.1.4",
|
||||||
|
"@types/node": "^22.14.1",
|
||||||
"@yeskunall/astro-umami": "^0.0.4",
|
"@yeskunall/astro-umami": "^0.0.4",
|
||||||
|
"astro": "^5.7.4",
|
||||||
"motion": "^12.7.4",
|
"motion": "^12.7.4",
|
||||||
"tailwindcss": "^4.1.4"
|
"tailwindcss": "^4.1.4"
|
||||||
},
|
},
|
||||||
|
|
8
src/components/Music.astro
Normal file
8
src/components/Music.astro
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<div
|
||||||
|
id="last-track-widget"
|
||||||
|
class="motion-music opacity-0 flex flex-col absolute top-0 left-0 m-8"
|
||||||
|
>
|
||||||
|
<p>Loading music data...</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="/src/js/music.ts"></script>
|
|
@ -1,7 +1,7 @@
|
||||||
import { animate, stagger } from "motion";
|
import { animate, stagger } from "motion";
|
||||||
|
|
||||||
const elements = document.querySelectorAll(
|
const elements = document.querySelectorAll(
|
||||||
".motion-links, .motion-logo, .motion-text, .motion-footer",
|
".motion-links, .motion-logo, .motion-text, .motion-music, .motion-footer",
|
||||||
);
|
);
|
||||||
|
|
||||||
animate(
|
animate(
|
||||||
|
@ -20,9 +20,10 @@ document.addEventListener("keydown", ({ key }: KeyboardEvent) => {
|
||||||
m: "a.matrix",
|
m: "a.matrix",
|
||||||
};
|
};
|
||||||
|
|
||||||
const link = document.querySelector(
|
const selector = shortcuts[key.toLowerCase()];
|
||||||
shortcuts[key.toLowerCase()] || "",
|
if (!selector) return;
|
||||||
) as HTMLAnchorElement | null;
|
|
||||||
|
const link = document.querySelector(selector) as HTMLAnchorElement | null;
|
||||||
if (!link?.href) return;
|
if (!link?.href) return;
|
||||||
|
|
||||||
const a = Object.assign(document.createElement("a"), {
|
const a = Object.assign(document.createElement("a"), {
|
||||||
|
|
77
src/js/music.ts
Normal file
77
src/js/music.ts
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
interface Track {
|
||||||
|
artists: string[];
|
||||||
|
title: string;
|
||||||
|
album: { albumtitle: string };
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Scrobble {
|
||||||
|
time: number;
|
||||||
|
track: Track;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchAndDisplayLastTrack() {
|
||||||
|
const container = document.getElementById("last-track-widget");
|
||||||
|
|
||||||
|
if (!container) {
|
||||||
|
console.error("Container element not found!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
let res: Response;
|
||||||
|
try {
|
||||||
|
res = await fetch("https://z0x.ca/music");
|
||||||
|
if (!res.ok) throw new Error("WAN fetch failed");
|
||||||
|
} catch (e) {
|
||||||
|
res = await fetch("https://z0x.home.arpa/music");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${res.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data: { status: string; list: Scrobble[] } = await res.json();
|
||||||
|
|
||||||
|
let lastTrack: Track | null = null;
|
||||||
|
if (data?.status === "ok" && data.list?.length) {
|
||||||
|
lastTrack = data.list.sort(
|
||||||
|
(a: Scrobble, b: Scrobble) => b.time - a.time,
|
||||||
|
)[0].track;
|
||||||
|
}
|
||||||
|
if (lastTrack) {
|
||||||
|
container.innerHTML = `
|
||||||
|
<span class="mb-2 flex gap-2">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 10v3"/><path d="M6 6v11"/><path d="M10 3v18"/><path d="M14 8v7"/><path d="M18 5v13"/><path d="M22 10v3"/></svg>
|
||||||
|
<span class="text-sm text-primary">
|
||||||
|
Last played..
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span class="text-md mb-2 font-bold leading-none">
|
||||||
|
${lastTrack.title}
|
||||||
|
</span>
|
||||||
|
<span class="text-xs text-muted-foreground">
|
||||||
|
<span class="font-semibold text-secondary-foreground">
|
||||||
|
by
|
||||||
|
</span>
|
||||||
|
${lastTrack.artists[0]}
|
||||||
|
</span>
|
||||||
|
<span class="text-xs text-muted-foreground">
|
||||||
|
<span class="font-semibold text-secondary-foreground">
|
||||||
|
on
|
||||||
|
</span>
|
||||||
|
${lastTrack.album.albumtitle}
|
||||||
|
</span>
|
||||||
|
`;
|
||||||
|
} else {
|
||||||
|
container.innerHTML = "<p>No tracks found.</p>";
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Fetch error:", e);
|
||||||
|
container.innerHTML = "<p>Error loading tracks.</p>";
|
||||||
|
} finally {
|
||||||
|
container.classList.remove("opacity-0");
|
||||||
|
container.classList.add("opacity-100");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchAndDisplayLastTrack();
|
|
@ -1,6 +1,7 @@
|
||||||
---
|
---
|
||||||
import Links from "@/components/Links.astro";
|
import Links from "@/components/Links.astro";
|
||||||
import Logo from "@/components/Logo.astro";
|
import Logo from "@/components/Logo.astro";
|
||||||
|
import Music from "@/components/Music.astro";
|
||||||
import Text from "@/components/Text.astro";
|
import Text from "@/components/Text.astro";
|
||||||
import Layout from "@/layouts/Layout.astro";
|
import Layout from "@/layouts/Layout.astro";
|
||||||
---
|
---
|
||||||
|
@ -9,4 +10,5 @@ import Layout from "@/layouts/Layout.astro";
|
||||||
<Text text="z0x"/>
|
<Text text="z0x"/>
|
||||||
<Logo />
|
<Logo />
|
||||||
<Links />
|
<Links />
|
||||||
|
<Music />
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|
Loading…
Add table
Reference in a new issue