Skip to content

Commit

Permalink
feat: spotify
Browse files Browse the repository at this point in the history
  • Loading branch information
rexdotsh committed Dec 14, 2024
1 parent 6d6f23f commit 2027d51
Show file tree
Hide file tree
Showing 6 changed files with 168 additions and 2 deletions.
2 changes: 2 additions & 0 deletions src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {FileRoutes} from "@solidjs/start/router";
import {Suspense} from "solid-js";
import "./app.css";
import Footer from "./components/Footer";
import SpotifyNowPlaying from "./components/Spotify";

export default function App() {
return (
Expand All @@ -11,6 +12,7 @@ export default function App() {
root={(props) => (
<main class="fixed inset-0 overflow-hidden">
<Suspense>{props.children}</Suspense>
<SpotifyNowPlaying />
<Footer />
</main>
)}>
Expand Down
3 changes: 2 additions & 1 deletion src/components/Art/Navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ const Navigation: Component = () => {
const links = [
{href: "https://blog.rex.wf", text: "BLOG"},
// {href: "/resume", text: "RESUME"},
{href: "https://x.com/rexmkv", text: "TWITTER"},
{href: "https://github.com/rexdotsh", text: "GITHUB"},
{href: "https://flora.tf", text: "FLORA"},
];

return (
<nav class="flex flex-col md:flex-row items-center gap-6 md:gap-12 mt-16 md:mt-12">
<nav class="flex flex-col md:flex-row items-center gap-6 md:gap-12 mt-8 md:mt-12">
{links.map(({href, text}) => (
<a href={href} class="text-lg md:text-2xl md:my-12 text-[#d6e9ef] font-bold hover:text-rose-400/80 transition-colors">
{text}
Expand Down
72 changes: 72 additions & 0 deletions src/components/Spotify/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import {Component, createEffect, createSignal, onCleanup} from "solid-js";
import type {SpotifyTrack} from "../../lib/spotify";

const SpotifyNowPlaying: Component = () => {
const [track, setTrack] = createSignal<SpotifyTrack | null>(null);
const [error, setError] = createSignal<string | null>(null);
const [isVisible, setIsVisible] = createSignal(false);

const fetchNowPlaying = async () => {
try {
const response = await fetch("/api/spotify");
if (!response.ok) throw new Error("Failed to fetch");
const data = await response.json();
setTrack(data);
setError(null);
} catch (err) {
setError("Failed to load track data");
console.error(err);
}
};

createEffect(() => {
fetchNowPlaying();
const interval = setInterval(fetchNowPlaying, 30000);
onCleanup(() => clearInterval(interval));
});

const getAlbumArt = () => {
const images = track()?.image || [];
const mediumImage = images.find((img) => img.size === "medium");
return mediumImage?.["#text"] || "";
};

const handleImageLoad = () => {
setTimeout(() => setIsVisible(true), 1);
};

return (
<div class="fixed bottom-20 w-full px-6">
<div class="max-w-sm mx-auto">
{error() ? null : track() ? (
<a
href={track()?.url}
target="_blank"
rel="noopener noreferrer"
class={`group flex items-center gap-4 bg-neutral-900/50 backdrop-blur-sm p-4
rounded-lg border border-neutral-800 hover:border-neutral-700
transition-all duration-300
${isVisible() ? "animate-fade-in" : "opacity-0"}`}>
<div class="relative min-w-16 h-16">
<img src={getAlbumArt()} alt={`${track()?.album} album art`} class="w-16 h-16 rounded-md object-cover" onLoad={handleImageLoad} />
{track()?.isPlaying && isVisible() && (
<div class="absolute -bottom-2 -right-2 flex items-center gap-[3px] bg-neutral-900/90 p-1.5 rounded-md">
<span class="w-[3px] h-3 bg-rose-400/80 animate-spotify-bar" />
<span class="w-[3px] h-3 bg-rose-400/80 animate-spotify-bar delay-[0.25s]" />
<span class="w-[3px] h-3 bg-rose-400/80 animate-spotify-bar delay-[0.5s]" />
</div>
)}
</div>
<div class="flex flex-col min-w-0">
<span class="font-medium text-neutral-200 truncate group-hover:text-rose-400/80 transition-colors">{track()?.name}</span>
<span class="text-neutral-400 text-sm truncate">{track()?.artist}</span>
<span class="text-neutral-500 text-sm truncate">{track()?.album}</span>
</div>
</a>
) : null}
</div>
</div>
);
};

export default SpotifyNowPlaying;
30 changes: 30 additions & 0 deletions src/lib/spotify.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
export interface SpotifyImage {
"#text": string;
size: "small" | "medium" | "large" | "extralarge";
}

export interface SpotifyTrack {
name: string;
artist: string;
album: string;
isPlaying: boolean;
url: string;
image: SpotifyImage[];
}

export interface SpotifyResponse {
track: {
"@attr"?: {
nowplaying: string;
};
name: string;
url: string;
artist: {
"#text": string;
};
album: {
"#text": string;
};
image: SpotifyImage[];
};
}
40 changes: 40 additions & 0 deletions src/routes/api/spotify.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import {APIEvent} from "@solidjs/start/server";
import type {SpotifyResponse} from "../../lib/spotify";

export async function GET({request}: APIEvent) {
try {
const response = await fetch("https://lastfm-last-played.biancarosa.com.br/rexdotsh/latest-song");
const data: SpotifyResponse = await response.json();

return new Response(
JSON.stringify({
isPlaying: data.track["@attr"]?.nowplaying === "true",
name: data.track.name,
artist: data.track.artist["#text"],
album: data.track.album["#text"],
url: data.track.url,
image: data.track.image,
}),
{
status: 200,
headers: {
"content-type": "application/json",
"cache-control": "public, s-maxage=60, stale-while-revalidate=30",
},
}
);
} catch (error) {
console.error("Spotify API Error:", error);
return new Response(
JSON.stringify({
error: error instanceof Error ? error.message : "Unknown error occurred",
}),
{
status: 500,
headers: {
"content-type": "application/json",
},
}
);
}
}
23 changes: 22 additions & 1 deletion tailwind.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,28 @@
export default {
content: ["./src/**/*.{js,jsx,ts,tsx}"],
theme: {
extend: {},
extend: {
keyframes: {
"spotify-bar": {
"0%, 100%": {height: "0.75rem"},
"50%": {height: "1.25rem"},
},
"fade-in": {
"0%": {
opacity: "0",
transform: "translateY(10px)",
},
"100%": {
opacity: "1",
transform: "translateY(0)",
},
},
},
animation: {
"spotify-bar": "spotify-bar 1s ease-in-out infinite",
"fade-in": "fade-in 0.6s ease-out forwards",
},
},
},
plugins: [],
};

0 comments on commit 2027d51

Please sign in to comment.