Repository navigation
Revert Spotify recently played streaming changes (#174) #178
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,14 +2,17 @@ import * as stylex from "@stylexjs/stylex"; | |
|
|
||
| import Link from "next/link"; | ||
| import { Post } from "@/app/(writing)/utils/types"; | ||
| import { SpotifyRecentlyPlayed } from "@/app/components/SpotifyRecentlyPlayed"; | ||
| import { Suspense } from "react"; | ||
| import { RecentlyPlayed } from "@/app/components/RecentlyPlayed"; | ||
| import { getNowPlaying } from "@/app/utils/spotify"; | ||
| import postsJson from "@/app/(writing)/posts.json"; | ||
| import { sharedStyles } from "@/app/styles"; | ||
|
|
||
| export default function Page() { | ||
| export const dynamic = "force-dynamic"; | ||
|
|
||
| export default async function Page() { | ||
| const { posts } = postsJson; | ||
| const lastThreePosts: Post[] = posts.slice(0, 3); | ||
| const nowPlaying = await getNowPlaying(); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When Redis or Spotify is slow or unreachable, this await runs before any homepage JSX is returned and outside local error handling. The underlying Redis operations and Useful? React with 👍 / 👎. |
||
|
|
||
| return ( | ||
| <section {...stylex.props(sharedStyles.pageSection)}> | ||
|
|
@@ -102,11 +105,7 @@ export default function Page() { | |
| </a> | ||
| </li> | ||
| </ul> | ||
| <div data-spotify-slot="" {...stylex.props(styles.spotifySlot)}> | ||
| <Suspense fallback={null}> | ||
| <SpotifyRecentlyPlayed /> | ||
| </Suspense> | ||
| </div> | ||
| {nowPlaying ? <RecentlyPlayed track={nowPlaying} /> : null} | ||
| <p {...stylex.props(styles.mt5)}> | ||
| You can read my{" "} | ||
| <Link aria-label="Link to Nathan's writing" href="/writing"> | ||
|
|
@@ -151,10 +150,6 @@ export default function Page() { | |
| } | ||
|
|
||
| const styles = stylex.create({ | ||
| spotifySlot: { | ||
| height: "5.75rem", | ||
| marginTop: "1.25rem", | ||
| }, | ||
| mt4: { | ||
| marginTop: "1rem", | ||
| }, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,7 +10,6 @@ export type NowPlayingTrack = { | |
|
|
||
| const SPOTIFY_TOKEN_URL = "https://accounts.spotify.com/api/token"; | ||
| const SPOTIFY_API_BASE = "https://api.spotify.com/v1"; | ||
| const SPOTIFY_REQUEST_TIMEOUT_MS = 3000; | ||
|
|
||
| export async function getAccessToken(): Promise<string | null> { | ||
| const clientId = process.env.SPOTIFY_CLIENT_ID; | ||
|
|
@@ -30,14 +29,13 @@ export async function getAccessToken(): Promise<string | null> { | |
|
|
||
| const res = await fetch(SPOTIFY_TOKEN_URL, { | ||
| cache: "no-store", | ||
| signal: AbortSignal.timeout(SPOTIFY_REQUEST_TIMEOUT_MS), | ||
| method: "POST", | ||
| headers: { "Content-Type": "application/x-www-form-urlencoded" }, | ||
| body: body.toString(), | ||
| }); | ||
|
|
||
| if (!res.ok) { | ||
| throw new Error(`Spotify request failed (${res.status})`); | ||
| return null; | ||
| } | ||
|
|
||
| const data = (await res.json()) as { access_token?: string }; | ||
|
|
@@ -64,18 +62,13 @@ function normalizeTrack(item: { | |
| export async function getCurrentlyPlaying(accessToken: string): Promise<NowPlayingTrack | null> { | ||
| const res = await fetch(`${SPOTIFY_API_BASE}/me/player/currently-playing`, { | ||
| cache: "no-store", | ||
| signal: AbortSignal.timeout(SPOTIFY_REQUEST_TIMEOUT_MS), | ||
| headers: { Authorization: `Bearer ${accessToken}` }, | ||
| }); | ||
|
|
||
| if (res.status === 204) { | ||
| if (res.status === 204 || !res.ok) { | ||
| return null; | ||
|
Comment on lines
+68
to
69
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When the currently-playing endpoint responds with an error such as 401, 429, or 5xx, this condition treats it exactly like the valid 204 “nothing playing” response. Useful? React with 👍 / 👎. |
||
| } | ||
|
|
||
| if (!res.ok) { | ||
| throw new Error(`Spotify request failed (${res.status})`); | ||
| } | ||
|
|
||
| const data = (await res.json()) as { | ||
| item?: { | ||
| name: string; | ||
|
|
@@ -95,12 +88,11 @@ export async function getCurrentlyPlaying(accessToken: string): Promise<NowPlayi | |
| export async function getRecentlyPlayed(accessToken: string, limit = 1): Promise<NowPlayingTrack | null> { | ||
| const res = await fetch(`${SPOTIFY_API_BASE}/me/player/recently-played?limit=${limit}`, { | ||
| cache: "no-store", | ||
| signal: AbortSignal.timeout(SPOTIFY_REQUEST_TIMEOUT_MS), | ||
| headers: { Authorization: `Bearer ${accessToken}` }, | ||
| }); | ||
|
|
||
| if (!res.ok) { | ||
| throw new Error(`Spotify request failed (${res.status})`); | ||
| return null; | ||
| } | ||
|
|
||
| const data = (await res.json()) as { | ||
|
|
@@ -123,20 +115,22 @@ export async function getRecentlyPlayed(accessToken: string, limit = 1): Promise | |
| } | ||
|
|
||
| export async function getNowPlaying(): Promise<NowPlayingTrack | null> { | ||
| if (process.env.NODE_ENV === "production") { | ||
| const cachedTrack = await redis.get<NowPlayingTrack | null>(getSpotifyRecentlyPlayedRedisKey()); | ||
| if (cachedTrack) { | ||
| return cachedTrack; | ||
| } | ||
| } | ||
|
|
||
| const accessToken = await getAccessToken(); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
On every production homepage request, this refreshes the Spotify access token before consulting the one-hour Redis cache. Consequently, even a cache hit incurs an external request, and a token-endpoint error response returns Useful? React with 👍 / 👎. |
||
| if (!accessToken) { | ||
| return null; | ||
| } | ||
|
|
||
| const currentTrack = await getCurrentlyPlaying(accessToken); | ||
| const track = currentTrack ?? (await getRecentlyPlayed(accessToken, 1)); | ||
| if (process.env.NODE_ENV === "production") { | ||
| const recentlyPlayedCache = await redis.get<NowPlayingTrack | null>(getSpotifyRecentlyPlayedRedisKey()); | ||
| if (recentlyPlayedCache) { | ||
| return recentlyPlayedCache; | ||
| } | ||
| } | ||
|
|
||
| let track: NowPlayingTrack | null = await getCurrentlyPlaying(accessToken); | ||
| if (!track) { | ||
| track = await getRecentlyPlayed(accessToken, 1); | ||
| } | ||
|
|
||
| if (process.env.NODE_ENV === "production") { | ||
| await redis.set(getSpotifyRecentlyPlayedRedisKey(), track, SPOTIFY_RECENTLY_PLAYED_TTL_S); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On narrow viewports or for Spotify metadata containing a long unbroken track or artist name, this
fit-contentflex link has no maximum width and its text child has neithermin-width: 0nor forced wrapping. The row can therefore grow wider than the homepage section and introduce horizontal scrolling; constrain the link to the available width and restore wrapping or clamping on the text child.Useful? React with 👍 / 👎.