fix(ui): replaygain for Artist Radio and Top Songs (#4328)
* Map replaygain info from getSimilarSongs2 * refactor: rename mapping function Signed-off-by: Deluan <deluan@navidrome.org> * refactor: Applied code review improvements Signed-off-by: Deluan <deluan@navidrome.org> --------- Signed-off-by: Deluan <deluan@navidrome.org>
This commit is contained in:
@@ -49,11 +49,21 @@ describe('ArtistActions', () => {
|
|||||||
// Mock console.error to suppress error logging in tests
|
// Mock console.error to suppress error logging in tests
|
||||||
vi.spyOn(console, 'error').mockImplementation(() => {})
|
vi.spyOn(console, 'error').mockImplementation(() => {})
|
||||||
|
|
||||||
|
const songWithReplayGain = {
|
||||||
|
id: 'rec1',
|
||||||
|
replayGain: {
|
||||||
|
albumGain: -5,
|
||||||
|
albumPeak: 1,
|
||||||
|
trackGain: -6,
|
||||||
|
trackPeak: 0.8,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
subsonic.getSimilarSongs2.mockResolvedValue({
|
subsonic.getSimilarSongs2.mockResolvedValue({
|
||||||
json: {
|
json: {
|
||||||
'subsonic-response': {
|
'subsonic-response': {
|
||||||
status: 'ok',
|
status: 'ok',
|
||||||
similarSongs2: { song: [{ id: 'rec1' }] },
|
similarSongs2: { song: [songWithReplayGain] },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -61,7 +71,7 @@ describe('ArtistActions', () => {
|
|||||||
json: {
|
json: {
|
||||||
'subsonic-response': {
|
'subsonic-response': {
|
||||||
status: 'ok',
|
status: 'ok',
|
||||||
topSongs: { song: [{ id: 'rec1' }] },
|
topSongs: { song: [songWithReplayGain] },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -93,6 +103,22 @@ describe('ArtistActions', () => {
|
|||||||
)
|
)
|
||||||
expect(mockDispatch).toHaveBeenCalled()
|
expect(mockDispatch).toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('maps replaygain info', async () => {
|
||||||
|
renderArtistActions()
|
||||||
|
clickActionButton('radio')
|
||||||
|
|
||||||
|
await waitFor(() =>
|
||||||
|
expect(subsonic.getSimilarSongs2).toHaveBeenCalledWith('ar1', 100),
|
||||||
|
)
|
||||||
|
const action = mockDispatch.mock.calls[0][0]
|
||||||
|
expect(action.data.rec1).toMatchObject({
|
||||||
|
rgAlbumGain: -5,
|
||||||
|
rgAlbumPeak: 1,
|
||||||
|
rgTrackGain: -6,
|
||||||
|
rgTrackPeak: 0.8,
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('Play action', () => {
|
describe('Play action', () => {
|
||||||
@@ -106,6 +132,22 @@ describe('ArtistActions', () => {
|
|||||||
expect(mockDispatch).toHaveBeenCalled()
|
expect(mockDispatch).toHaveBeenCalled()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('maps replaygain info for top songs', async () => {
|
||||||
|
renderArtistActions()
|
||||||
|
clickActionButton('topSongs')
|
||||||
|
|
||||||
|
await waitFor(() =>
|
||||||
|
expect(subsonic.getTopSongs).toHaveBeenCalledWith('Artist', 100),
|
||||||
|
)
|
||||||
|
const action = mockDispatch.mock.calls[0][0]
|
||||||
|
expect(action.data.rec1).toMatchObject({
|
||||||
|
rgAlbumGain: -5,
|
||||||
|
rgAlbumPeak: 1,
|
||||||
|
rgTrackGain: -6,
|
||||||
|
rgTrackPeak: 0.8,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
it('handles API rejection', async () => {
|
it('handles API rejection', async () => {
|
||||||
subsonic.getTopSongs.mockRejectedValue(new Error('Network error'))
|
subsonic.getTopSongs.mockRejectedValue(new Error('Network error'))
|
||||||
|
|
||||||
|
|||||||
+28
-12
@@ -1,6 +1,32 @@
|
|||||||
import subsonic from '../subsonic/index.js'
|
import subsonic from '../subsonic/index.js'
|
||||||
import { playTracks } from '../actions/index.js'
|
import { playTracks } from '../actions/index.js'
|
||||||
|
|
||||||
|
const mapReplayGain = (song) => {
|
||||||
|
const { replayGain: rg } = song
|
||||||
|
if (!rg) {
|
||||||
|
return song
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...song,
|
||||||
|
...(rg.albumGain !== undefined && { rgAlbumGain: rg.albumGain }),
|
||||||
|
...(rg.albumPeak !== undefined && { rgAlbumPeak: rg.albumPeak }),
|
||||||
|
...(rg.trackGain !== undefined && { rgTrackGain: rg.trackGain }),
|
||||||
|
...(rg.trackPeak !== undefined && { rgTrackPeak: rg.trackPeak }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const processSongsForPlayback = (songs) => {
|
||||||
|
const songData = {}
|
||||||
|
const ids = []
|
||||||
|
songs.forEach((s) => {
|
||||||
|
const song = mapReplayGain(s)
|
||||||
|
songData[song.id] = song
|
||||||
|
ids.push(song.id)
|
||||||
|
})
|
||||||
|
return { songData, ids }
|
||||||
|
}
|
||||||
|
|
||||||
export const playTopSongs = async (dispatch, notify, artistName) => {
|
export const playTopSongs = async (dispatch, notify, artistName) => {
|
||||||
const res = await subsonic.getTopSongs(artistName, 100)
|
const res = await subsonic.getTopSongs(artistName, 100)
|
||||||
const data = res.json['subsonic-response']
|
const data = res.json['subsonic-response']
|
||||||
@@ -17,12 +43,7 @@ export const playTopSongs = async (dispatch, notify, artistName) => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const songData = {}
|
const { songData, ids } = processSongsForPlayback(songs)
|
||||||
const ids = []
|
|
||||||
songs.forEach((s) => {
|
|
||||||
songData[s.id] = s
|
|
||||||
ids.push(s.id)
|
|
||||||
})
|
|
||||||
dispatch(playTracks(songData, ids))
|
dispatch(playTracks(songData, ids))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,12 +63,7 @@ export const playSimilar = async (dispatch, notify, id) => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const songData = {}
|
const { songData, ids } = processSongsForPlayback(songs)
|
||||||
const ids = []
|
|
||||||
songs.forEach((s) => {
|
|
||||||
songData[s.id] = s
|
|
||||||
ids.push(s.id)
|
|
||||||
})
|
|
||||||
dispatch(playTracks(songData, ids))
|
dispatch(playTracks(songData, ids))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user