mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-22 07:15:47 +00:00
707a41f615
* fix(scrobble): report Now Playing on playback server with local bytes Navidrome presence and play-count scrobbles no longer skip when audio plays from hot cache, offline pins, or favorites-auto, and reachability follows the queue/playback server instead of the browsed active server. * docs: changelog and credits for PR #1055
72 lines
2.4 KiB
TypeScript
72 lines
2.4 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
import { useAuthStore } from '../store/authStore';
|
|
import { usePlayerStore } from '../store/playerStore';
|
|
import { reportNowPlaying, scrobbleSong } from './subsonicScrobble';
|
|
import { shouldAttemptSubsonicForServer } from '../utils/network/subsonicNetworkGuard';
|
|
|
|
const { apiForServerMock } = vi.hoisted(() => ({
|
|
apiForServerMock: vi.fn(async () => ({})),
|
|
}));
|
|
|
|
vi.mock('./subsonicClient', () => ({
|
|
api: vi.fn(),
|
|
apiForServer: apiForServerMock,
|
|
}));
|
|
vi.mock('../utils/network/subsonicNetworkGuard', () => ({
|
|
shouldAttemptSubsonicForServer: vi.fn(() => true),
|
|
}));
|
|
|
|
describe('subsonicScrobble', () => {
|
|
beforeEach(() => {
|
|
apiForServerMock.mockClear();
|
|
vi.mocked(shouldAttemptSubsonicForServer).mockImplementation(() => true);
|
|
useAuthStore.setState({
|
|
servers: [
|
|
{ id: 'a', name: 'A', url: 'http://a.test', username: 'u', password: 'p' },
|
|
{ id: 'b', name: 'B', url: 'http://b.test', username: 'u', password: 'p' },
|
|
],
|
|
activeServerId: 'b',
|
|
isLoggedIn: true,
|
|
});
|
|
usePlayerStore.setState({
|
|
queueItems: [{ serverId: 'a', trackId: 't1' }],
|
|
queueServerId: 'a',
|
|
queueIndex: 0,
|
|
});
|
|
});
|
|
|
|
it('scrobbleSong targets the queue server when active server differs', async () => {
|
|
await scrobbleSong('t1', 1_700_000_000_000, 'a');
|
|
expect(apiForServerMock).toHaveBeenCalledWith(
|
|
'a',
|
|
'scrobble.view',
|
|
expect.objectContaining({ id: 't1', submission: true, time: 1_700_000_000_000 }),
|
|
);
|
|
});
|
|
|
|
it('reportNowPlaying and scrobbleSong use the presence guard without trackId', async () => {
|
|
vi.mocked(shouldAttemptSubsonicForServer).mockImplementation(
|
|
(_serverId: string, trackId?: string) => trackId === undefined,
|
|
);
|
|
|
|
await reportNowPlaying('t-local', 'a');
|
|
await scrobbleSong('t-local', 1_700_000_000_000, 'a');
|
|
|
|
expect(shouldAttemptSubsonicForServer).toHaveBeenCalledWith('a');
|
|
expect(shouldAttemptSubsonicForServer).not.toHaveBeenCalledWith('a', expect.anything());
|
|
expect(apiForServerMock).toHaveBeenCalledTimes(2);
|
|
expect(apiForServerMock).toHaveBeenNthCalledWith(
|
|
1,
|
|
'a',
|
|
'scrobble.view',
|
|
expect.objectContaining({ id: 't-local', submission: false }),
|
|
);
|
|
expect(apiForServerMock).toHaveBeenNthCalledWith(
|
|
2,
|
|
'a',
|
|
'scrobble.view',
|
|
expect.objectContaining({ id: 't-local', submission: true }),
|
|
);
|
|
});
|
|
});
|