From 2027d5191cbf036af73d7fe3e26e3985d6445164 Mon Sep 17 00:00:00 2001 From: rexdotsh <65942753+rexdotsh@users.noreply.github.com> Date: Sun, 15 Dec 2024 00:07:12 +0530 Subject: [PATCH] feat: spotify --- src/app.tsx | 2 + src/components/Art/Navigation.tsx | 3 +- src/components/Spotify/index.tsx | 72 +++++++++++++++++++++++++++++++ src/lib/spotify.ts | 30 +++++++++++++ src/routes/api/spotify.ts | 40 +++++++++++++++++ tailwind.config.js | 23 +++++++++- 6 files changed, 168 insertions(+), 2 deletions(-) create mode 100644 src/components/Spotify/index.tsx create mode 100644 src/lib/spotify.ts create mode 100644 src/routes/api/spotify.ts diff --git a/src/app.tsx b/src/app.tsx index a6b8526..6fff791 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -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 ( @@ -11,6 +12,7 @@ export default function App() { root={(props) => ( {props.children} + )}> diff --git a/src/components/Art/Navigation.tsx b/src/components/Art/Navigation.tsx index e243c1a..1e1b011 100644 --- a/src/components/Art/Navigation.tsx +++ b/src/components/Art/Navigation.tsx @@ -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 ( - + {links.map(({href, text}) => ( {text} diff --git a/src/components/Spotify/index.tsx b/src/components/Spotify/index.tsx new file mode 100644 index 0000000..60816fd --- /dev/null +++ b/src/components/Spotify/index.tsx @@ -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(null); + const [error, setError] = createSignal(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 ( + + + {error() ? null : track() ? ( + + + + {track()?.isPlaying && isVisible() && ( + + + + + + )} + + + {track()?.name} + {track()?.artist} + {track()?.album} + + + ) : null} + + + ); +}; + +export default SpotifyNowPlaying; diff --git a/src/lib/spotify.ts b/src/lib/spotify.ts new file mode 100644 index 0000000..40b4c36 --- /dev/null +++ b/src/lib/spotify.ts @@ -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[]; + }; +} diff --git a/src/routes/api/spotify.ts b/src/routes/api/spotify.ts new file mode 100644 index 0000000..59815f7 --- /dev/null +++ b/src/routes/api/spotify.ts @@ -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", + }, + } + ); + } +} diff --git a/tailwind.config.js b/tailwind.config.js index eac0ec2..5467192 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -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: [], };