feat(ui): version cover art urls by content hash and skip absent art

This commit is contained in:
Deluan 2026-07-23 20:36:21 -04:00
parent dafcdf438f
commit 9f2b78ab2f
2 changed files with 52 additions and 6 deletions

View File

@ -81,25 +81,31 @@ const getAvatarUrl = (username, size) =>
)
const getCoverArtUrl = (record, size, square) => {
// Known-absent art would only ever return a placeholder; skip the round trip entirely.
if (record.imageAbsent) {
return ''
}
const suffix = record.imageHash ? '_' + record.imageHash : ''
const options = {
...(record.updatedAt && { _: record.updatedAt }),
// A hash-suffixed url is already pixel-versioned; the buster would defeat immutable caching.
...(!record.imageHash && record.updatedAt && { _: record.updatedAt }),
...(size && { size }),
...(square && { square }),
}
// TODO Move this logic to server
if (record.album) {
return baseUrl(url('getCoverArt', 'mf-' + record.id, options))
return baseUrl(url('getCoverArt', 'mf-' + record.id + suffix, options))
} else if (record.albumArtist) {
return baseUrl(url('getCoverArt', 'al-' + record.id, options))
return baseUrl(url('getCoverArt', 'al-' + record.id + suffix, options))
} else if (record.sync !== undefined) {
// This is a playlist
return baseUrl(url('getCoverArt', 'pl-' + record.id, options))
return baseUrl(url('getCoverArt', 'pl-' + record.id + suffix, options))
} else if (record.streamUrl !== undefined) {
// This is a radio station
return baseUrl(url('getCoverArt', 'ra-' + record.id, options))
return baseUrl(url('getCoverArt', 'ra-' + record.id + suffix, options))
} else {
return baseUrl(url('getCoverArt', 'ar-' + record.id, options))
return baseUrl(url('getCoverArt', 'ar-' + record.id + suffix, options))
}
}

View File

@ -120,6 +120,46 @@ describe('getCoverArtUrl', () => {
expect(url).toContain('ar-test-123')
expect(url).not.toContain('_=')
})
it('appends the content hash to the artwork id', () => {
const url = subsonic.getCoverArtUrl({
id: 'album-123',
albumArtist: 'AA',
imageHash: '0123456789abcdef',
})
expect(url).toContain('al-album-123_0123456789abcdef')
})
it('drops the updatedAt buster once the url is hash-versioned', () => {
const url = subsonic.getCoverArtUrl({
id: 'album-123',
albumArtist: 'AA',
updatedAt: '2023-01-01T00:00:00Z',
imageHash: '0123456789abcdef',
})
expect(url).toContain('al-album-123_0123456789abcdef')
expect(url).not.toContain('_=')
})
it('keeps the updatedAt buster while artwork is unresolved', () => {
const url = subsonic.getCoverArtUrl({
id: 'album-123',
albumArtist: 'AA',
updatedAt: '2023-01-01T00:00:00Z',
})
expect(url).toContain('al-album-123')
expect(url).toContain('_=')
})
it('returns an empty url for known-absent artwork', () => {
expect(
subsonic.getCoverArtUrl({
id: 'album-123',
albumArtist: 'AA',
imageAbsent: true,
}),
).toBe('')
})
})
describe('getDiscCoverArtUrl', () => {