mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-21 23:05:46 +00:00
fix(discord): resolve cover profile from the store, not getPlaybackServerId
Follow-up to the dual-address cover fix: a playback/active cover scope was
routed through getPlaybackServerId(), which returns a `string` that can be
empty or an index-key (not a profile id) for locally-cached tracks. The
`?? activeServerId` fallback never fired (empty string isn't nullish), so no
profile matched and the URL came back null — which the presence layer caches
per cover id, producing intermittent "cover shows, then doesn't".
A playback/active scope always means the active server (a cross-server track
gets an explicit `server` scope), so resolve the active profile directly.
(cherry picked from commit 8f93f30e6f)
This commit is contained in:
@@ -45,4 +45,25 @@ describe('coverArtUrlForDiscord', () => {
|
|||||||
|
|
||||||
expect(url).toContain('music.example.com');
|
expect(url).toContain('music.example.com');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('resolves a playback scope to the active profile (public address)', async () => {
|
||||||
|
// playback scope is the common case for locally-cached tracks; it must
|
||||||
|
// resolve to the active server, not yield a null cover.
|
||||||
|
const server = makeServer({
|
||||||
|
url: 'http://192.168.1.50:4533',
|
||||||
|
alternateUrl: 'https://music.example.com',
|
||||||
|
});
|
||||||
|
useAuthStore.setState({ servers: [server], activeServerId: server.id } as never);
|
||||||
|
|
||||||
|
const ref: CoverArtRef = {
|
||||||
|
cacheKind: 'album',
|
||||||
|
cacheEntityId: 'al-1',
|
||||||
|
fetchCoverArtId: 'al-1',
|
||||||
|
serverScope: { kind: 'playback' },
|
||||||
|
};
|
||||||
|
const url = await coverArtUrlForDiscord(ref);
|
||||||
|
|
||||||
|
expect(url).toContain('music.example.com');
|
||||||
|
expect(url).not.toContain('192.168.1.50');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,17 +1,7 @@
|
|||||||
import { buildCoverArtUrlForServer } from '../../api/subsonicStreamUrl';
|
import { buildCoverArtUrlForServer } from '../../api/subsonicStreamUrl';
|
||||||
import { serverShareBaseUrl } from '../../utils/server/serverEndpoint';
|
import { serverShareBaseUrl } from '../../utils/server/serverEndpoint';
|
||||||
import { getPlaybackServerId } from '../../utils/playback/playbackServer';
|
|
||||||
import { useAuthStore } from '../../store/authStore';
|
import { useAuthStore } from '../../store/authStore';
|
||||||
import type { CoverArtRef, CoverServerScope } from '../types';
|
import type { CoverArtRef } from '../types';
|
||||||
|
|
||||||
/** The saved profile id that a cover scope resolves to (active/playback/server). */
|
|
||||||
function serverIdForScope(scope: CoverServerScope): string | null {
|
|
||||||
if (scope.kind === 'server') return scope.serverId;
|
|
||||||
if (scope.kind === 'playback') {
|
|
||||||
return getPlaybackServerId() ?? useAuthStore.getState().activeServerId ?? null;
|
|
||||||
}
|
|
||||||
return useAuthStore.getState().activeServerId ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Discord large image — an https:// URL Discord's own servers can reach.
|
* Discord large image — an https:// URL Discord's own servers can reach.
|
||||||
@@ -21,13 +11,21 @@ function serverIdForScope(scope: CoverServerScope): string | null {
|
|||||||
* remotely, so a `http://192.168.x.x` address is unreachable and falls back to
|
* remotely, so a `http://192.168.x.x` address is unreachable and falls back to
|
||||||
* the app icon. Discord is an external consumer just like a share link, so use
|
* the app icon. Discord is an external consumer just like a share link, so use
|
||||||
* `serverShareBaseUrl` (public address preferred when both are set).
|
* `serverShareBaseUrl` (public address preferred when both are set).
|
||||||
|
*
|
||||||
|
* Resolve the profile straight from the store: a `playback`/`active` scope
|
||||||
|
* always means the active server (a cross-server track gets an explicit
|
||||||
|
* `server` scope), so we never route through `getPlaybackServerId()`, whose
|
||||||
|
* empty-string / index-key returns previously yielded a null cover URL on
|
||||||
|
* locally-cached tracks.
|
||||||
*/
|
*/
|
||||||
export async function coverArtUrlForDiscord(ref: CoverArtRef): Promise<string | null> {
|
export async function coverArtUrlForDiscord(ref: CoverArtRef): Promise<string | null> {
|
||||||
const { serverScope, fetchCoverArtId } = ref;
|
const { serverScope, fetchCoverArtId } = ref;
|
||||||
const serverId = serverIdForScope(serverScope);
|
const auth = useAuthStore.getState();
|
||||||
const profile = serverId
|
|
||||||
? useAuthStore.getState().servers.find(s => s.id === serverId)
|
const profile =
|
||||||
: undefined;
|
serverScope.kind === 'server'
|
||||||
|
? auth.servers.find(s => s.id === serverScope.serverId)
|
||||||
|
: auth.servers.find(s => s.id === auth.activeServerId);
|
||||||
|
|
||||||
if (profile) {
|
if (profile) {
|
||||||
return buildCoverArtUrlForServer(
|
return buildCoverArtUrlForServer(
|
||||||
|
|||||||
Reference in New Issue
Block a user