jedmund-svelte/src/lib/utils/simpleLastfmStreamManager.ts
Justin Edmund 3d7eb6e985 refactor: consolidate now playing detection into single music stream store
- Merge albumStream and nowPlayingStream into unified musicStream store
- Simplify confidence scoring to binary detection (playing/not playing)
- Create single source of truth for music state across components
- Fix synchronization issues between header and album indicators
- Make Album spring animation more subtle (stiffness: 150, damping: 25)

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-10 21:32:14 -07:00

107 lines
No EOL
3.3 KiB
TypeScript

import type { Album } from '$lib/types/lastfm'
import type { LastClient } from '@musicorum/lastfm'
import { SimpleNowPlayingDetector } from './simpleNowPlayingDetector'
import { AlbumEnricher } from './albumEnricher'
import { trackToAlbum } from './lastfmTransformers'
import { logger } from '$lib/server/logger'
export interface StreamUpdate {
albums?: Album[]
}
export class SimpleLastfmStreamManager {
private client: LastClient
private username: string
private detector: SimpleNowPlayingDetector
private albumEnricher: AlbumEnricher
private lastAlbumState: string = ''
constructor(client: LastClient, username: string) {
this.client = client
this.username = username
this.detector = new SimpleNowPlayingDetector()
this.albumEnricher = new AlbumEnricher(client)
}
/**
* Check for updates and return any changes
*/
async checkForUpdates(): Promise<StreamUpdate> {
try {
// Fetch fresh data from Last.fm
logger.music('debug', '🔄 Fetching fresh tracks from Last.fm...')
const recentTracksResponse = await this.client.user.getRecentTracks(this.username, {
limit: 50,
extended: true
})
logger.music('debug', `📊 Got ${recentTracksResponse.tracks?.length || 0} tracks from Last.fm`)
// Cache for other uses but always use fresh for now playing
await this.albumEnricher.cacheRecentTracks(this.username, recentTracksResponse)
// Get recent albums (top 4)
const albums = await this.getRecentAlbums(4, recentTracksResponse)
// Debug the response structure
logger.music('debug', `📊 Response structure check:`, {
hasTracksProp: !!recentTracksResponse.tracks,
trackCount: recentTracksResponse.tracks?.length || 0,
firstTrack: recentTracksResponse.tracks?.[0]
})
// Process now playing status
const albumsWithNowPlaying = await this.detector.processAlbums(
albums,
recentTracksResponse.tracks,
(artistName, albumName) =>
this.albumEnricher.getAppleMusicDataForNowPlaying(artistName, albumName)
)
// Enrich albums with additional data
const enrichedAlbums = await Promise.all(
albumsWithNowPlaying.map((album) => this.albumEnricher.enrichAlbum(album))
)
// Check if anything changed
const currentState = JSON.stringify(
enrichedAlbums.map(a => ({
key: `${a.artist.name}:${a.name}`,
isNowPlaying: a.isNowPlaying,
track: a.nowPlayingTrack
}))
)
if (currentState !== this.lastAlbumState) {
this.lastAlbumState = currentState
logger.music('debug', '📤 Albums changed, sending update')
logger.music('debug', `Current state: ${currentState}`)
return { albums: enrichedAlbums }
} else {
logger.music('debug', '🔄 No changes detected, skipping update')
}
return {}
} catch (error) {
logger.error('Error checking for updates:', error as Error, undefined, 'music')
return {}
}
}
/**
* Get recent albums from Last.fm tracks
*/
private async getRecentAlbums(limit: number, recentTracksResponse: any): Promise<Album[]> {
const uniqueAlbums = new Map<string, Album>()
for (const track of recentTracksResponse.tracks) {
if (uniqueAlbums.size >= limit) break
const albumKey = track.album.mbid || track.album.name
if (!uniqueAlbums.has(albumKey)) {
uniqueAlbums.set(albumKey, trackToAlbum(track, uniqueAlbums.size + 1))
}
}
return Array.from(uniqueAlbums.values())
}
}