feat(ui): add playlist cover art display

Signed-off-by: Deluan <deluan@navidrome.org>
This commit is contained in:
Deluan
2025-05-25 23:22:55 -04:00
parent 0cb02bce06
commit 5c4fbdb7c1
3 changed files with 245 additions and 31 deletions
+9
View File
@@ -61,11 +61,20 @@ const getCoverArtUrl = (record, size, square) => {
...(square && { square }),
}
// For playlists, add a timestamp to prevent caching issues when switching between playlists
if (record.songCount !== undefined) {
// Add current timestamp to ensure fresh requests for playlists
options._ = record.updatedAt || new Date().getTime()
}
// TODO Move this logic to server. `song` and `album` should have a CoverArtID
if (record.album) {
return baseUrl(url('getCoverArt', 'mf-' + record.id, options))
} else if (record.albumArtist) {
return baseUrl(url('getCoverArt', 'al-' + record.id, options))
} else if (record.songCount !== undefined) {
// This is a playlist
return baseUrl(url('getCoverArt', 'pl-' + record.id, options))
} else {
return baseUrl(url('getCoverArt', 'ar-' + record.id, options))
}
+101
View File
@@ -0,0 +1,101 @@
import { vi } from 'vitest'
import subsonic from './index'
describe('getCoverArtUrl', () => {
beforeEach(() => {
// Mock window.location
delete window.location
window.location = { href: 'http://localhost:3000/app' }
// Mock localStorage values required by subsonic
const localStorageMock = {
getItem: vi.fn((key) => {
const values = {
username: 'testuser',
'subsonic-token': 'testtoken',
'subsonic-salt': 'testsalt',
}
return values[key] || null
}),
setItem: vi.fn(),
clear: vi.fn(),
}
Object.defineProperty(window, 'localStorage', { value: localStorageMock })
})
it('should return playlist cover art URL for records with songCount', () => {
const playlistRecord = {
id: 'playlist-123',
songCount: 10,
updatedAt: '2023-01-01T00:00:00Z',
}
const url = subsonic.getCoverArtUrl(playlistRecord, 300, true)
expect(url).toContain('pl-playlist-123')
expect(url).toContain('size=300')
expect(url).toContain('square=true')
expect(url).toContain('_=2023-01-01T00%3A00%3A00Z')
})
it('should add timestamp for playlists without updatedAt', () => {
const playlistRecord = {
id: 'playlist-123',
songCount: 5,
}
const url = subsonic.getCoverArtUrl(playlistRecord)
expect(url).toContain('pl-playlist-123')
expect(url).toMatch(/_=\d+/)
})
it('should return album cover art URL for records with albumArtist', () => {
const albumRecord = {
id: 'album-123',
albumArtist: 'Test Artist',
updatedAt: '2023-01-01T00:00:00Z',
}
const url = subsonic.getCoverArtUrl(albumRecord, 300)
expect(url).toContain('al-album-123')
expect(url).toContain('size=300')
})
it('should return media file cover art URL for records with album', () => {
const mediaFileRecord = {
id: 'mf-123',
album: 'Test Album',
updatedAt: '2023-01-01T00:00:00Z',
}
const url = subsonic.getCoverArtUrl(mediaFileRecord, 200)
expect(url).toContain('mf-mf-123')
expect(url).toContain('size=200')
})
it('should return artist cover art URL for other records', () => {
const artistRecord = {
id: 'artist-123',
updatedAt: '2023-01-01T00:00:00Z',
}
const url = subsonic.getCoverArtUrl(artistRecord, 150)
expect(url).toContain('ar-artist-123')
expect(url).toContain('size=150')
})
it('should handle records without updatedAt', () => {
const record = {
id: 'test-123',
}
const url = subsonic.getCoverArtUrl(record)
expect(url).toContain('ar-test-123')
expect(url).not.toContain('_=')
})
})