Ran linter

This commit is contained in:
Justin Edmund 2025-06-16 17:05:43 +01:00
parent 723f7acbc1
commit 4aaf33f19e
18 changed files with 353 additions and 298 deletions

View file

@ -1,130 +1,130 @@
{ {
"name": "jedmund-svelte", "name": "jedmund-svelte",
"version": "0.0.1", "version": "0.0.1",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "vite dev", "dev": "vite dev",
"build": "vite build", "build": "vite build",
"start": "node build", "start": "node build",
"preview": "vite preview", "preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"lint": "prettier --check . && eslint .", "lint": "prettier --check . && eslint .",
"format": "prettier --write .", "format": "prettier --write .",
"db:migrate": "prisma migrate dev", "db:migrate": "prisma migrate dev",
"db:seed": "prisma db seed", "db:seed": "prisma db seed",
"db:studio": "prisma studio", "db:studio": "prisma studio",
"db:init": "tsx scripts/init-db.ts", "db:init": "tsx scripts/init-db.ts",
"db:deploy": "prisma migrate deploy", "db:deploy": "prisma migrate deploy",
"setup:local": "./scripts/setup-local.sh", "setup:local": "./scripts/setup-local.sh",
"storybook": "storybook dev -p 6006", "storybook": "storybook dev -p 6006",
"build-storybook": "storybook build" "build-storybook": "storybook build"
}, },
"devDependencies": { "devDependencies": {
"@musicorum/lastfm": "github:jedmund/lastfm", "@musicorum/lastfm": "github:jedmund/lastfm",
"@poppanator/sveltekit-svg": "^5.0.0-svelte5.4", "@poppanator/sveltekit-svg": "^5.0.0-svelte5.4",
"@storybook/addon-a11y": "^9.0.9", "@storybook/addon-a11y": "^9.0.9",
"@storybook/addon-docs": "^9.0.9", "@storybook/addon-docs": "^9.0.9",
"@storybook/addon-svelte-csf": "^5.0.3", "@storybook/addon-svelte-csf": "^5.0.3",
"@storybook/sveltekit": "^9.0.9", "@storybook/sveltekit": "^9.0.9",
"@sveltejs/adapter-auto": "^3.0.0", "@sveltejs/adapter-auto": "^3.0.0",
"@sveltejs/kit": "^2.0.0", "@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^4.0.0-next.6", "@sveltejs/vite-plugin-svelte": "^4.0.0-next.6",
"@types/eslint": "^8.56.7", "@types/eslint": "^8.56.7",
"@types/node": "^22.0.2", "@types/node": "^22.0.2",
"autoprefixer": "^10.4.19", "autoprefixer": "^10.4.19",
"eslint": "^9.0.0", "eslint": "^9.0.0",
"eslint-config-prettier": "^9.1.0", "eslint-config-prettier": "^9.1.0",
"eslint-plugin-storybook": "^9.0.9", "eslint-plugin-storybook": "^9.0.9",
"eslint-plugin-svelte": "^2.36.0", "eslint-plugin-svelte": "^2.36.0",
"globals": "^15.0.0", "globals": "^15.0.0",
"postcss": "^8.4.39", "postcss": "^8.4.39",
"prettier": "^3.1.1", "prettier": "^3.1.1",
"prettier-plugin-svelte": "^3.1.2", "prettier-plugin-svelte": "^3.1.2",
"sass": "^1.77.8", "sass": "^1.77.8",
"storybook": "^9.0.9", "storybook": "^9.0.9",
"svelte": "^5.0.0-next.1", "svelte": "^5.0.0-next.1",
"svelte-check": "^3.6.0", "svelte-check": "^3.6.0",
"tslib": "^2.4.1", "tslib": "^2.4.1",
"tsx": "^4.19.4", "tsx": "^4.19.4",
"typescript": "^5.5.3", "typescript": "^5.5.3",
"typescript-eslint": "^8.0.0-alpha.20", "typescript-eslint": "^8.0.0-alpha.20",
"vite": "^5.0.3" "vite": "^5.0.3"
}, },
"type": "module", "type": "module",
"dependencies": { "dependencies": {
"@aarkue/tiptap-math-extension": "^1.3.6", "@aarkue/tiptap-math-extension": "^1.3.6",
"@prisma/client": "^6.8.2", "@prisma/client": "^6.8.2",
"@sveltejs/adapter-node": "^5.2.0", "@sveltejs/adapter-node": "^5.2.0",
"@tiptap/core": "^2.12.0", "@tiptap/core": "^2.12.0",
"@tiptap/extension-bubble-menu": "^2.12.0", "@tiptap/extension-bubble-menu": "^2.12.0",
"@tiptap/extension-character-count": "^2.12.0", "@tiptap/extension-character-count": "^2.12.0",
"@tiptap/extension-code-block-lowlight": "^2.12.0", "@tiptap/extension-code-block-lowlight": "^2.12.0",
"@tiptap/extension-color": "^2.12.0", "@tiptap/extension-color": "^2.12.0",
"@tiptap/extension-floating-menu": "^2.12.0", "@tiptap/extension-floating-menu": "^2.12.0",
"@tiptap/extension-highlight": "^2.12.0", "@tiptap/extension-highlight": "^2.12.0",
"@tiptap/extension-image": "^2.12.0", "@tiptap/extension-image": "^2.12.0",
"@tiptap/extension-link": "^2.12.0", "@tiptap/extension-link": "^2.12.0",
"@tiptap/extension-placeholder": "^2.12.0", "@tiptap/extension-placeholder": "^2.12.0",
"@tiptap/extension-subscript": "^2.12.0", "@tiptap/extension-subscript": "^2.12.0",
"@tiptap/extension-superscript": "^2.12.0", "@tiptap/extension-superscript": "^2.12.0",
"@tiptap/extension-table": "^2.12.0", "@tiptap/extension-table": "^2.12.0",
"@tiptap/extension-table-header": "^2.12.0", "@tiptap/extension-table-header": "^2.12.0",
"@tiptap/extension-table-row": "^2.12.0", "@tiptap/extension-table-row": "^2.12.0",
"@tiptap/extension-task-item": "^2.12.0", "@tiptap/extension-task-item": "^2.12.0",
"@tiptap/extension-task-list": "^2.12.0", "@tiptap/extension-task-list": "^2.12.0",
"@tiptap/extension-text": "^2.12.0", "@tiptap/extension-text": "^2.12.0",
"@tiptap/extension-text-align": "^2.12.0", "@tiptap/extension-text-align": "^2.12.0",
"@tiptap/extension-text-style": "^2.12.0", "@tiptap/extension-text-style": "^2.12.0",
"@tiptap/extension-typography": "^2.12.0", "@tiptap/extension-typography": "^2.12.0",
"@tiptap/extension-underline": "^2.12.0", "@tiptap/extension-underline": "^2.12.0",
"@tiptap/pm": "^2.12.0", "@tiptap/pm": "^2.12.0",
"@tiptap/starter-kit": "^2.12.0", "@tiptap/starter-kit": "^2.12.0",
"@tiptap/suggestion": "^2.12.0", "@tiptap/suggestion": "^2.12.0",
"@types/jsonwebtoken": "^9.0.9", "@types/jsonwebtoken": "^9.0.9",
"@types/multer": "^1.4.12", "@types/multer": "^1.4.12",
"@types/redis": "^4.0.10", "@types/redis": "^4.0.10",
"@types/steamapi": "^2.2.5", "@types/steamapi": "^2.2.5",
"cloudinary": "^2.6.1", "cloudinary": "^2.6.1",
"dotenv": "^16.5.0", "dotenv": "^16.5.0",
"exifr": "^7.1.3", "exifr": "^7.1.3",
"giantbombing-api": "^1.0.4", "giantbombing-api": "^1.0.4",
"gray-matter": "^4.0.3", "gray-matter": "^4.0.3",
"ioredis": "^5.4.1", "ioredis": "^5.4.1",
"jsonwebtoken": "^9.0.2", "jsonwebtoken": "^9.0.2",
"katex": "^0.16.22", "katex": "^0.16.22",
"lowlight": "^3.3.0", "lowlight": "^3.3.0",
"lucide-svelte": "^0.511.0", "lucide-svelte": "^0.511.0",
"marked": "^15.0.12", "marked": "^15.0.12",
"multer": "^2.0.0", "multer": "^2.0.0",
"node-itunes-search": "^1.2.3", "node-itunes-search": "^1.2.3",
"prisma": "^6.8.2", "prisma": "^6.8.2",
"psn-api": "github:jedmund/psn-api", "psn-api": "github:jedmund/psn-api",
"redis": "^4.7.0", "redis": "^4.7.0",
"sharp": "^0.34.2", "sharp": "^0.34.2",
"steamapi": "^3.0.11", "steamapi": "^3.0.11",
"svelte-bricks": "^0.3.2", "svelte-bricks": "^0.3.2",
"svelte-infinite": "^0.5.0", "svelte-infinite": "^0.5.0",
"svelte-medium-image-zoom": "^0.2.6", "svelte-medium-image-zoom": "^0.2.6",
"svelte-portal": "^2.2.1", "svelte-portal": "^2.2.1",
"svelte-tiptap": "^2.1.0", "svelte-tiptap": "^2.1.0",
"svgo": "^3.3.2", "svgo": "^3.3.2",
"tinyduration": "^3.3.1", "tinyduration": "^3.3.1",
"tippy.js": "^6.3.7", "tippy.js": "^6.3.7",
"tiptap-extension-auto-joiner": "^0.1.3", "tiptap-extension-auto-joiner": "^0.1.3",
"tiptap-extension-global-drag-handle": "^0.1.18", "tiptap-extension-global-drag-handle": "^0.1.18",
"tiptap-markdown": "^0.8.10", "tiptap-markdown": "^0.8.10",
"zod": "^3.25.30" "zod": "^3.25.30"
}, },
"prisma": { "prisma": {
"seed": "tsx prisma/seed.ts" "seed": "tsx prisma/seed.ts"
}, },
"engines": { "engines": {
"node": ">=20.0.0", "node": ">=20.0.0",
"npm": ">=10.0.0" "npm": ">=10.0.0"
}, },
"overrides": { "overrides": {
"@sveltejs/vite-plugin-svelte": "^4.0.0-next.6", "@sveltejs/vite-plugin-svelte": "^4.0.0-next.6",
"storybook": "$storybook" "storybook": "$storybook"
} }
} }

View file

@ -11,18 +11,25 @@ const mockAlbum = {
url: 'https://www.last.fm/music/Radiohead/In+Rainbows', url: 'https://www.last.fm/music/Radiohead/In+Rainbows',
rank: 1, rank: 1,
images: { images: {
small: 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp', small:
medium: 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp', 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp',
large: 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp', medium:
extralarge: 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp', 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp',
large:
'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp',
extralarge:
'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp',
mega: 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp', mega: 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp',
default: 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp' default:
'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp'
}, },
isNowPlaying: false, isNowPlaying: false,
appleMusicData: { appleMusicData: {
appleMusicId: '1109714933', appleMusicId: '1109714933',
highResArtwork: 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp', highResArtwork:
previewUrl: 'https://audio-ssl.itunes.apple.com/itunes-assets/AudioPreview125/v4/5c/e8/e3/5ce8e347-3bea-3bb0-0664-a6e1c9125d3a/mzaf_7638610958907470670.plus.aac.p.m4a', 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp',
previewUrl:
'https://audio-ssl.itunes.apple.com/itunes-assets/AudioPreview125/v4/5c/e8/e3/5ce8e347-3bea-3bb0-0664-a6e1c9125d3a/mzaf_7638610958907470670.plus.aac.p.m4a',
genres: ['Alternative', 'Music'], genres: ['Alternative', 'Music'],
releaseDate: '2007-10-10', releaseDate: '2007-10-10',
trackCount: 10, trackCount: 10,

View file

@ -11,18 +11,25 @@
url: 'https://www.last.fm/music/Radiohead/In+Rainbows', url: 'https://www.last.fm/music/Radiohead/In+Rainbows',
rank: 1, rank: 1,
images: { images: {
small: 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp', small:
medium: 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp', 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp',
large: 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp', medium:
extralarge: 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp', 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp',
large:
'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp',
extralarge:
'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp',
mega: 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp', mega: 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp',
default: 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp' default:
'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp'
}, },
isNowPlaying: false, isNowPlaying: false,
appleMusicData: { appleMusicData: {
appleMusicId: '1109714933', appleMusicId: '1109714933',
highResArtwork: 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp', highResArtwork:
previewUrl: 'https://audio-ssl.itunes.apple.com/itunes-assets/AudioPreview125/v4/5c/e8/e3/5ce8e347-3bea-3bb0-0664-a6e1c9125d3a/mzaf_7638610958907470670.plus.aac.p.m4a', 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp',
previewUrl:
'https://audio-ssl.itunes.apple.com/itunes-assets/AudioPreview125/v4/5c/e8/e3/5ce8e347-3bea-3bb0-0664-a6e1c9125d3a/mzaf_7638610958907470670.plus.aac.p.m4a',
genres: ['Alternative', 'Music'], genres: ['Alternative', 'Music'],
releaseDate: '2007-10-10', releaseDate: '2007-10-10',
trackCount: 10, trackCount: 10,

View file

@ -71,14 +71,17 @@
nowPlayingStoreState = state nowPlayingStoreState = state
// Check if any album is currently playing, unless forced // Check if any album is currently playing, unless forced
if (!forcePlayingMusic) { if (!forcePlayingMusic) {
const nowPlayingFromStream = Array.from(state.updates.values()).some((update) => update.isNowPlaying) const nowPlayingFromStream = Array.from(state.updates.values()).some(
(update) => update.isNowPlaying
)
console.log('Avatar - nowPlayingStream update:', { console.log('Avatar - nowPlayingStream update:', {
updatesCount: state.updates.size, updatesCount: state.updates.size,
hasNowPlaying: nowPlayingFromStream hasNowPlaying: nowPlayingFromStream
}) })
// Don't set to false if we haven't received album data yet // Don't set to false if we haven't received album data yet
if (nowPlayingFromStream || albumStoreState !== null) { if (nowPlayingFromStream || albumStoreState !== null) {
isPlayingMusic = nowPlayingFromStream || (albumStoreState?.some(album => album.isNowPlaying) ?? false) isPlayingMusic =
nowPlayingFromStream || (albumStoreState?.some((album) => album.isNowPlaying) ?? false)
} }
} }
}) })
@ -87,17 +90,19 @@
const unsubscribeAlbums = albumStream.subscribe((state) => { const unsubscribeAlbums = albumStream.subscribe((state) => {
albumStoreState = state.albums albumStoreState = state.albums
if (!forcePlayingMusic) { if (!forcePlayingMusic) {
const hasNowPlaying = state.albums.some(album => album.isNowPlaying) const hasNowPlaying = state.albums.some((album) => album.isNowPlaying)
// Get the current state of nowPlayingStream // Get the current state of nowPlayingStream
const nowPlayingState = nowPlayingStoreState || get(nowPlayingStream) const nowPlayingState = nowPlayingStoreState || get(nowPlayingStream)
const nowPlayingFromStream = Array.from(nowPlayingState.updates.values()).some((update) => update.isNowPlaying) const nowPlayingFromStream = Array.from(nowPlayingState.updates.values()).some(
(update) => update.isNowPlaying
)
console.log('Avatar - albumStream update:', { console.log('Avatar - albumStream update:', {
albumsCount: state.albums.length, albumsCount: state.albums.length,
hasNowPlayingInAlbums: hasNowPlaying, hasNowPlayingInAlbums: hasNowPlaying,
hasNowPlayingInStream: nowPlayingFromStream, hasNowPlayingInStream: nowPlayingFromStream,
albums: state.albums.map(a => ({ name: a.name, isNowPlaying: a.isNowPlaying })) albums: state.albums.map((a) => ({ name: a.name, isNowPlaying: a.isNowPlaying }))
}) })
// Update isPlayingMusic based on whether any album is now playing from either source // Update isPlayingMusic based on whether any album is now playing from either source
@ -122,8 +127,8 @@
style="transform: scale({scale.current})" style="transform: scale({scale.current})"
> >
<AvatarSVG> <AvatarSVG>
<!-- Face group --> <!-- Face group -->
<g slot="face" class="face" class:hover={isHovering} class:blink={isBlinking}> <g slot="face" class="face" class:hover={isHovering} class:blink={isBlinking}>
<!-- Normal face --> <!-- Normal face -->
<g class="normal"> <g class="normal">
<path <path

View file

@ -1,10 +1,4 @@
<svg <svg width="497" height="497" viewBox="0 0 497 497" fill="none" xmlns="http://www.w3.org/2000/svg">
width="497"
height="497"
viewBox="0 0 497 497"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g> <g>
<!-- Common elements --> <!-- Common elements -->
<!-- Skin --> <!-- Skin -->

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View file

@ -13,9 +13,9 @@
// Responsive column configuration // Responsive column configuration
// These values work well with our existing design // These values work well with our existing design
let minColWidth = 200 // Minimum column width in px let minColWidth = 200 // Minimum column width in px
let maxColWidth = 400 // Maximum column width in px let maxColWidth = 400 // Maximum column width in px
let gap = 16 // Gap between items (equivalent to $unit-2x) let gap = 16 // Gap between items (equivalent to $unit-2x)
// On tablet/phone, we want larger minimum widths // On tablet/phone, we want larger minimum widths
let windowWidth = $state(0) let windowWidth = $state(0)

View file

@ -27,11 +27,7 @@
</div> </div>
{#if trackName} {#if trackName}
<div class="track-name-container" bind:this={containerElement}> <div class="track-name-container" bind:this={containerElement}>
<span <span class="track-name" class:marquee={shouldMarquee} bind:this={textElement}>
class="track-name"
class:marquee={shouldMarquee}
bind:this={textElement}
>
{trackName} {trackName}
{#if shouldMarquee} {#if shouldMarquee}
<span class="marquee-gap">&nbsp;&nbsp;&nbsp;&nbsp;</span> <span class="marquee-gap">&nbsp;&nbsp;&nbsp;&nbsp;</span>

View file

@ -12,18 +12,25 @@ const mockAlbums = [
url: 'https://www.last.fm/music/Radiohead/In+Rainbows', url: 'https://www.last.fm/music/Radiohead/In+Rainbows',
rank: 1, rank: 1,
images: { images: {
small: 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp', small:
medium: 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp', 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp',
large: 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp', medium:
extralarge: 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp', 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp',
large:
'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp',
extralarge:
'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp',
mega: 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp', mega: 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp',
default: 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp' default:
'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp'
}, },
isNowPlaying: false, isNowPlaying: false,
appleMusicData: { appleMusicData: {
appleMusicId: '1109714933', appleMusicId: '1109714933',
highResArtwork: 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp', highResArtwork:
previewUrl: 'https://audio-ssl.itunes.apple.com/itunes-assets/AudioPreview125/v4/5c/e8/e3/5ce8e347-3bea-3bb0-0664-a6e1c9125d3a/mzaf_7638610958907470670.plus.aac.p.m4a', 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp',
previewUrl:
'https://audio-ssl.itunes.apple.com/itunes-assets/AudioPreview125/v4/5c/e8/e3/5ce8e347-3bea-3bb0-0664-a6e1c9125d3a/mzaf_7638610958907470670.plus.aac.p.m4a',
genres: ['Alternative', 'Music'], genres: ['Alternative', 'Music'],
releaseDate: '2007-10-10', releaseDate: '2007-10-10',
trackCount: 10, trackCount: 10,
@ -40,19 +47,26 @@ const mockAlbums = [
url: 'https://www.last.fm/music/Radiohead/OK+Computer', url: 'https://www.last.fm/music/Radiohead/OK+Computer',
rank: 2, rank: 2,
images: { images: {
small: 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp', small:
medium: 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp', 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp',
large: 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp', medium:
extralarge: 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp', 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp',
large:
'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp',
extralarge:
'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp',
mega: 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp', mega: 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp',
default: 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp' default:
'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp'
}, },
isNowPlaying: true, isNowPlaying: true,
nowPlayingTrack: 'Paranoid Android', nowPlayingTrack: 'Paranoid Android',
appleMusicData: { appleMusicData: {
appleMusicId: '1097861387', appleMusicId: '1097861387',
highResArtwork: 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp', highResArtwork:
previewUrl: 'https://audio-ssl.itunes.apple.com/itunes-assets/AudioPreview125/v4/65/f2/85/65f285d2-5a99-f502-89f8-ca2c4da24d19/mzaf_1760708625972666865.plus.aac.p.m4a' 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp',
previewUrl:
'https://audio-ssl.itunes.apple.com/itunes-assets/AudioPreview125/v4/65/f2/85/65f285d2-5a99-f502-89f8-ca2c4da24d19/mzaf_1760708625972666865.plus.aac.p.m4a'
} }
}, },
{ {
@ -65,17 +79,24 @@ const mockAlbums = [
url: 'https://www.last.fm/music/Pink+Floyd/The+Dark+Side+of+the+Moon', url: 'https://www.last.fm/music/Pink+Floyd/The+Dark+Side+of+the+Moon',
rank: 3, rank: 3,
images: { images: {
small: 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp', small:
medium: 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp', 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp',
large: 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp', medium:
extralarge: 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp', 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp',
large:
'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp',
extralarge:
'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp',
mega: 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp', mega: 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp',
default: 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp' default:
'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp'
}, },
appleMusicData: { appleMusicData: {
appleMusicId: '1065973699', appleMusicId: '1065973699',
highResArtwork: 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp', highResArtwork:
previewUrl: 'https://audio-ssl.itunes.apple.com/itunes-assets/AudioPreview115/v4/57/15/fb/5715fb67-0424-8e6e-a1ff-2c0cf09e4bdc/mzaf_3641989451682986919.plus.aac.p.m4a' 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp',
previewUrl:
'https://audio-ssl.itunes.apple.com/itunes-assets/AudioPreview115/v4/57/15/fb/5715fb67-0424-8e6e-a1ff-2c0cf09e4bdc/mzaf_3641989451682986919.plus.aac.p.m4a'
} }
}, },
{ {
@ -88,16 +109,22 @@ const mockAlbums = [
url: 'https://www.last.fm/music/Joy+Division/Unknown+Pleasures', url: 'https://www.last.fm/music/Joy+Division/Unknown+Pleasures',
rank: 4, rank: 4,
images: { images: {
small: 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp', small:
medium: 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp', 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp',
large: 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp', medium:
extralarge: 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp', 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp',
large:
'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp',
extralarge:
'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp',
mega: 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp', mega: 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp',
default: 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp' default:
'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp'
}, },
appleMusicData: { appleMusicData: {
appleMusicId: '659989492', appleMusicId: '659989492',
highResArtwork: 'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp' highResArtwork:
'https://is1-ssl.mzstatic.com/image/thumb/Music126/v4/85/2e/2b/852e2b6c-93ec-806a-95b2-8f5eda2f775c/22UMGIM18886.rgb.jpg/592x592bb.webp'
} }
} }
] ]

View file

@ -169,11 +169,17 @@ export async function findAlbum(artist: string, album: string): Promise<AppleMus
} }
// Helper function to perform the album search and matching // Helper function to perform the album search and matching
async function searchAndMatch(searchAlbum: string, storefront: string = DEFAULT_STOREFRONT): Promise<{album: AppleMusicAlbum, storefront: string} | null> { async function searchAndMatch(
searchAlbum: string,
storefront: string = DEFAULT_STOREFRONT
): Promise<{ album: AppleMusicAlbum; storefront: string } | null> {
const searchQuery = `${artist} ${searchAlbum}` const searchQuery = `${artist} ${searchAlbum}`
const response = await searchAlbums(searchQuery, 5, storefront) const response = await searchAlbums(searchQuery, 5, storefront)
console.log(`Search results for "${searchQuery}" in ${storefront} storefront:`, JSON.stringify(response, null, 2)) console.log(
`Search results for "${searchQuery}" in ${storefront} storefront:`,
JSON.stringify(response, null, 2)
)
if (!response.results?.albums?.data?.length) { if (!response.results?.albums?.data?.length) {
console.log(`No albums found in ${storefront} storefront`) console.log(`No albums found in ${storefront} storefront`)
@ -209,7 +215,7 @@ export async function findAlbum(artist: string, album: string): Promise<AppleMus
) )
} }
return match ? {album: match, storefront} : null return match ? { album: match, storefront } : null
} }
try { try {
@ -226,7 +232,9 @@ export async function findAlbum(artist: string, album: string): Promise<AppleMus
if (!result) { if (!result) {
const cleanedAlbum = removeLeadingPunctuation(album) const cleanedAlbum = removeLeadingPunctuation(album)
if (cleanedAlbum !== album && cleanedAlbum.length > 0) { if (cleanedAlbum !== album && cleanedAlbum.length > 0) {
console.log(`No match found for "${album}", trying without leading punctuation: "${cleanedAlbum}"`) console.log(
`No match found for "${album}", trying without leading punctuation: "${cleanedAlbum}"`
)
result = await searchAndMatch(cleanedAlbum) result = await searchAndMatch(cleanedAlbum)
// Also try Japanese storefront with cleaned album name // Also try Japanese storefront with cleaned album name

View file

@ -43,10 +43,12 @@ function createAlbumStream() {
eventSource.addEventListener('albums', (event) => { eventSource.addEventListener('albums', (event) => {
try { try {
const albums: Album[] = JSON.parse(event.data) const albums: Album[] = JSON.parse(event.data)
const nowPlayingAlbum = albums.find(a => a.isNowPlaying) const nowPlayingAlbum = albums.find((a) => a.isNowPlaying)
console.log('Album stream received albums:', { console.log('Album stream received albums:', {
totalAlbums: albums.length, totalAlbums: albums.length,
nowPlayingAlbum: nowPlayingAlbum ? `${nowPlayingAlbum.artist.name} - ${nowPlayingAlbum.name}` : 'none' nowPlayingAlbum: nowPlayingAlbum
? `${nowPlayingAlbum.artist.name} - ${nowPlayingAlbum.name}`
: 'none'
}) })
update((state) => ({ update((state) => ({
...state, ...state,

View file

@ -69,7 +69,10 @@ export const GET: RequestHandler = async ({ request }) => {
// Check if this album is currently playing using duration-based detection // Check if this album is currently playing using duration-based detection
if (withAppleMusic.appleMusicData?.tracks && !withAppleMusic.isNowPlaying) { if (withAppleMusic.appleMusicData?.tracks && !withAppleMusic.isNowPlaying) {
const nowPlayingCheck = checkWithTracks(withAppleMusic.name, withAppleMusic.appleMusicData.tracks) const nowPlayingCheck = checkWithTracks(
withAppleMusic.name,
withAppleMusic.appleMusicData.tracks
)
if (nowPlayingCheck?.isNowPlaying) { if (nowPlayingCheck?.isNowPlaying) {
withAppleMusic.isNowPlaying = true withAppleMusic.isNowPlaying = true
withAppleMusic.nowPlayingTrack = nowPlayingCheck.nowPlayingTrack withAppleMusic.nowPlayingTrack = nowPlayingCheck.nowPlayingTrack
@ -85,9 +88,11 @@ export const GET: RequestHandler = async ({ request }) => {
) )
// Ensure only one album is marked as now playing in the enriched albums // Ensure only one album is marked as now playing in the enriched albums
const nowPlayingCount = enrichedAlbums.filter(a => a.isNowPlaying).length const nowPlayingCount = enrichedAlbums.filter((a) => a.isNowPlaying).length
if (nowPlayingCount > 1) { if (nowPlayingCount > 1) {
console.log(`Multiple enriched albums marked as now playing (${nowPlayingCount}), keeping only the most recent one`) console.log(
`Multiple enriched albums marked as now playing (${nowPlayingCount}), keeping only the most recent one`
)
// The albums are already in order from most recent to oldest // The albums are already in order from most recent to oldest
// So we keep the first now playing album and mark others as not playing // So we keep the first now playing album and mark others as not playing
@ -107,16 +112,19 @@ export const GET: RequestHandler = async ({ request }) => {
} }
// Check if album order has changed or now playing status changed // Check if album order has changed or now playing status changed
const currentAlbumOrder = enrichedAlbums.map(a => `${a.artist.name}:${a.name}`) const currentAlbumOrder = enrichedAlbums.map((a) => `${a.artist.name}:${a.name}`)
const albumOrderChanged = JSON.stringify(currentAlbumOrder) !== JSON.stringify(lastAlbumOrder) const albumOrderChanged =
JSON.stringify(currentAlbumOrder) !== JSON.stringify(lastAlbumOrder)
// Also check if any now playing status changed // Also check if any now playing status changed
let nowPlayingChanged = false let nowPlayingChanged = false
for (const album of enrichedAlbums) { for (const album of enrichedAlbums) {
const key = `${album.artist.name}:${album.name}` const key = `${album.artist.name}:${album.name}`
const lastState = lastNowPlayingState.get(key) const lastState = lastNowPlayingState.get(key)
if (album.isNowPlaying !== (lastState?.isPlaying || false) || if (
(album.isNowPlaying && album.nowPlayingTrack !== lastState?.track)) { album.isNowPlaying !== (lastState?.isPlaying || false) ||
(album.isNowPlaying && album.nowPlayingTrack !== lastState?.track)
) {
nowPlayingChanged = true nowPlayingChanged = true
break break
} }
@ -139,10 +147,12 @@ export const GET: RequestHandler = async ({ request }) => {
try { try {
const data = JSON.stringify(enrichedAlbums) const data = JSON.stringify(enrichedAlbums)
controller.enqueue(encoder.encode(`event: albums\ndata: ${data}\n\n`)) controller.enqueue(encoder.encode(`event: albums\ndata: ${data}\n\n`))
const nowPlayingAlbum = enrichedAlbums.find(a => a.isNowPlaying) const nowPlayingAlbum = enrichedAlbums.find((a) => a.isNowPlaying)
console.log('Sent album update with now playing status:', { console.log('Sent album update with now playing status:', {
totalAlbums: enrichedAlbums.length, totalAlbums: enrichedAlbums.length,
nowPlayingAlbum: nowPlayingAlbum ? `${nowPlayingAlbum.artist.name} - ${nowPlayingAlbum.name}` : 'none' nowPlayingAlbum: nowPlayingAlbum
? `${nowPlayingAlbum.artist.name} - ${nowPlayingAlbum.name}`
: 'none'
}) })
} catch (e) { } catch (e) {
isClosed = true isClosed = true
@ -158,7 +168,7 @@ export const GET: RequestHandler = async ({ request }) => {
// (Recent albums already have their now playing status included) // (Recent albums already have their now playing status included)
for (const album of nowPlayingAlbums) { for (const album of nowPlayingAlbums) {
const isInRecentAlbums = enrichedAlbums.some( const isInRecentAlbums = enrichedAlbums.some(
a => a.artist.name === album.artistName && a.name === album.albumName (a) => a.artist.name === album.artistName && a.name === album.albumName
) )
if (!isInRecentAlbums) { if (!isInRecentAlbums) {
@ -321,9 +331,11 @@ async function getNowPlayingAlbums(client: LastClient): Promise<NowPlayingUpdate
} }
// Ensure only one album is marked as now playing - keep the most recent one // Ensure only one album is marked as now playing - keep the most recent one
const nowPlayingAlbums = Array.from(albums.values()).filter(a => a.isNowPlaying) const nowPlayingAlbums = Array.from(albums.values()).filter((a) => a.isNowPlaying)
if (nowPlayingAlbums.length > 1) { if (nowPlayingAlbums.length > 1) {
console.log(`Multiple albums marked as now playing (${nowPlayingAlbums.length}), keeping only the most recent one`) console.log(
`Multiple albums marked as now playing (${nowPlayingAlbums.length}), keeping only the most recent one`
)
// Find the most recent track // Find the most recent track
let mostRecentTime = new Date(0) let mostRecentTime = new Date(0)
@ -331,7 +343,7 @@ async function getNowPlayingAlbums(client: LastClient): Promise<NowPlayingUpdate
for (const album of nowPlayingAlbums) { for (const album of nowPlayingAlbums) {
// Find the most recent track for this album // Find the most recent track for this album
const albumTracks = recentTracks.filter(t => t.albumName === album.albumName) const albumTracks = recentTracks.filter((t) => t.albumName === album.albumName)
if (albumTracks.length > 0) { if (albumTracks.length > 0) {
const latestTrack = albumTracks.reduce((latest, track) => const latestTrack = albumTracks.reduce((latest, track) =>
track.scrobbleTime > latest.scrobbleTime ? track : latest track.scrobbleTime > latest.scrobbleTime ? track : latest
@ -344,7 +356,7 @@ async function getNowPlayingAlbums(client: LastClient): Promise<NowPlayingUpdate
} }
// Mark all others as not playing // Mark all others as not playing
nowPlayingAlbums.forEach(album => { nowPlayingAlbums.forEach((album) => {
if (album !== mostRecentAlbum) { if (album !== mostRecentAlbum) {
const key = `${album.artistName}:${album.albumName}` const key = `${album.artistName}:${album.albumName}`
albums.set(key, { albums.set(key, {
@ -533,10 +545,7 @@ async function searchAppleMusicForAlbum(album: Album): Promise<Album> {
return album return album
} }
async function getRecentAlbums( async function getRecentAlbums(client: LastClient, limit: number = 4): Promise<Album[]> {
client: LastClient,
limit: number = 4
): Promise<Album[]> {
// Check cache for recent tracks // Check cache for recent tracks
const cacheKey = `lastfm:recent:${USERNAME}` const cacheKey = `lastfm:recent:${USERNAME}`
const cached = await redis.get(cacheKey) const cached = await redis.get(cacheKey)

View file

@ -17,7 +17,7 @@
let currentOffset = $state(data.pagination?.limit || 20) let currentOffset = $state(data.pagination?.limit || 20)
// Track loaded photo IDs to prevent duplicates // Track loaded photo IDs to prevent duplicates
let loadedPhotoIds = $state(new Set(data.photoItems?.map(item => item.id) || [])) let loadedPhotoIds = $state(new Set(data.photoItems?.map((item) => item.id) || []))
const error = $derived(data.error) const error = $derived(data.error)
const pageUrl = $derived($page.url.href) const pageUrl = $derived($page.url.href)
@ -121,7 +121,7 @@
<InfiniteLoader <InfiniteLoader
{loaderState} {loaderState}
triggerLoad={loadMore} triggerLoad={loadMore}
intersectionOptions={{ rootMargin: "0px 0px 200px 0px" }} intersectionOptions={{ rootMargin: '0px 0px 200px 0px' }}
> >
<!-- Empty content since we're rendering the grid above --> <!-- Empty content since we're rendering the grid above -->
<div style="height: 1px;"></div> <div style="height: 1px;"></div>
@ -138,9 +138,9 @@
<button <button
class="retry-button" class="retry-button"
onclick={() => { onclick={() => {
lastError = ''; lastError = ''
loaderState.reset(); loaderState.reset()
loadMore(); loadMore()
}} }}
> >
Try again Try again