diff --git a/src/assets/illos/jedmund-headphones.svg b/src/assets/illos/jedmund-headphones.svg
new file mode 100644
index 0000000..83ab3dc
--- /dev/null
+++ b/src/assets/illos/jedmund-headphones.svg
@@ -0,0 +1,17 @@
+
diff --git a/src/lib/components/Avatar.svelte b/src/lib/components/Avatar.svelte
index 48ef29c..7bb0395 100644
--- a/src/lib/components/Avatar.svelte
+++ b/src/lib/components/Avatar.svelte
@@ -3,9 +3,11 @@
// We can do a thought bubble-y thing with the album art that takes you to the album section of the page
import { onMount, onDestroy } from 'svelte'
import { spring } from 'svelte/motion'
+ import { nowPlayingStream } from '$lib/stores/now-playing-stream'
- let isHovering = false
- let isBlinking = false
+ let isHovering = $state(false)
+ let isBlinking = $state(false)
+ let isPlayingMusic = $state(false)
const scale = spring(1, {
stiffness: 0.1,
@@ -55,10 +57,17 @@
}
}, 4000)
+ // Subscribe to now playing updates
+ const unsubscribe = nowPlayingStream.subscribe((state) => {
+ // Check if any album is currently playing
+ isPlayingMusic = Array.from(state.updates.values()).some((update) => update.isNowPlaying)
+ })
+
return () => {
if (blinkInterval) {
clearInterval(blinkInterval)
}
+ unsubscribe()
}
})
@@ -356,6 +365,75 @@
+
+
+ {#if isPlayingMusic}
+
+
+
+ {/if}
diff --git a/src/lib/components/Header.svelte b/src/lib/components/Header.svelte
index 258ce55..e7c64f9 100644
--- a/src/lib/components/Header.svelte
+++ b/src/lib/components/Header.svelte
@@ -113,13 +113,6 @@
:global(svg) {
height: 100%;
width: 100%;
- transition: transform 0.2s ease;
- }
-
- &:hover {
- :global(svg) {
- transform: scale(1.05);
- }
}
}
diff --git a/src/routes/api/lastfm/stream/+server.ts b/src/routes/api/lastfm/stream/+server.ts
index eeb625d..dd1dbf7 100644
--- a/src/routes/api/lastfm/stream/+server.ts
+++ b/src/routes/api/lastfm/stream/+server.ts
@@ -6,7 +6,7 @@ import redis from '../../redis-client'
const LASTFM_API_KEY = process.env.LASTFM_API_KEY
const USERNAME = 'jedmund'
-const UPDATE_INTERVAL = 30000 // 30 seconds
+const UPDATE_INTERVAL = 15000 // 15 seconds for more responsive updates
interface NowPlayingUpdate {
albumName: string
@@ -31,7 +31,7 @@ export const GET: RequestHandler = async ({ request }) => {
const stream = new ReadableStream({
async start(controller) {
const client = new LastClient(LASTFM_API_KEY || '')
- let lastNowPlayingState: Map = new Map()
+ let lastNowPlayingState: Map = new Map()
// Send initial connection message
controller.enqueue(encoder.encode('event: connected\ndata: {}\n\n'))
@@ -40,15 +40,41 @@ export const GET: RequestHandler = async ({ request }) => {
try {
const nowPlayingAlbums = await getNowPlayingAlbums(client)
const updates: NowPlayingUpdate[] = []
+ const currentAlbums = new Set()
// Check for changes
for (const album of nowPlayingAlbums) {
const key = `${album.artistName}:${album.albumName}`
- const wasPlaying = lastNowPlayingState.get(key) || false
+ currentAlbums.add(key)
- if (album.isNowPlaying !== wasPlaying) {
+ const lastState = lastNowPlayingState.get(key)
+ const wasPlaying = lastState?.isPlaying || false
+ const lastTrack = lastState?.track
+
+ // Update if playing status changed OR if the track changed
+ if (album.isNowPlaying !== wasPlaying ||
+ (album.isNowPlaying && album.nowPlayingTrack !== lastTrack)) {
updates.push(album)
- lastNowPlayingState.set(key, album.isNowPlaying)
+ console.log(`Update for ${album.albumName}: playing=${album.isNowPlaying}, track=${album.nowPlayingTrack}`)
+ }
+
+ lastNowPlayingState.set(key, {
+ isPlaying: album.isNowPlaying,
+ track: album.nowPlayingTrack
+ })
+ }
+
+ // Check for albums that were in the list but aren't anymore (stopped playing)
+ for (const [key, state] of lastNowPlayingState.entries()) {
+ if (!currentAlbums.has(key) && state.isPlaying) {
+ const [artistName, albumName] = key.split(':')
+ updates.push({
+ albumName,
+ artistName,
+ isNowPlaying: false
+ })
+ console.log(`Album no longer in recent: ${albumName}`)
+ lastNowPlayingState.delete(key)
}
}
@@ -173,20 +199,36 @@ function checkWithTracks(
const now = new Date()
const SCROBBLE_LAG = 3 * 60 * 1000 // 3 minutes
+ // Clean up old tracks first
+ recentTracks = recentTracks.filter(track => {
+ // Keep tracks from last hour only
+ const hourAgo = new Date(now.getTime() - 60 * 60 * 1000)
+ return track.scrobbleTime > hourAgo
+ })
+
+ // Find the most recent track from this album
+ let mostRecentTrack: TrackPlayInfo | null = null
for (const trackInfo of recentTracks) {
- if (trackInfo.albumName !== albumName) continue
-
+ if (trackInfo.albumName === albumName) {
+ if (!mostRecentTrack || trackInfo.scrobbleTime > mostRecentTrack.scrobbleTime) {
+ mostRecentTrack = trackInfo
+ }
+ }
+ }
+
+ if (mostRecentTrack) {
const trackData = tracks.find(t =>
- t.name.toLowerCase() === trackInfo.trackName.toLowerCase()
+ t.name.toLowerCase() === mostRecentTrack.trackName.toLowerCase()
)
if (trackData?.durationMs) {
- const trackEndTime = new Date(trackInfo.scrobbleTime.getTime() + trackData.durationMs + SCROBBLE_LAG)
+ const trackEndTime = new Date(mostRecentTrack.scrobbleTime.getTime() + trackData.durationMs + SCROBBLE_LAG)
if (now < trackEndTime) {
+ console.log(`Track "${mostRecentTrack.trackName}" is still playing (ends at ${trackEndTime.toLocaleTimeString()})`)
return {
isNowPlaying: true,
- nowPlayingTrack: trackInfo.trackName
+ nowPlayingTrack: mostRecentTrack.trackName
}
}
}