mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-21 14:55:43 +00:00
fix(music-network): keep API suffix for self-hosted paste-token providers (#1085)
* fix(music-network): keep API suffix for self-hosted paste-token providers The api_key_only connect strategy persisted the raw origin from the baseUrl field instead of the resolved API base, dropping the preset's selfHostedApiSuffix (e.g. /apis/listenbrainz). Scrobbles and now-playing then hit <origin>/1/submit-listens (404/405, silently unlogged) instead of <origin>/apis/listenbrainz/1/submit-listens, so nothing was recorded. Return the runtime-resolved ctx.baseUrl (origin + suffix) and fall back to the field only when it is absent. Fixes Koito and both Maloja compat surfaces (ListenBrainz and Audioscrobbler). Existing accounts must reconnect to re-persist the corrected base. * docs(changelog): self-hosted scrobble URL fix (#1085)
This commit is contained in:
@@ -210,6 +210,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## Fixed
|
||||
|
||||
### Music Network — self-hosted scrobbling reaches the server
|
||||
|
||||
**By [@Psychotoxical](https://github.com/Psychotoxical), PR [#1085](https://github.com/Psychotoxical/psysonic/pull/1085)**
|
||||
|
||||
* Self-hosted scrobble targets that use a pasted token (Koito, Maloja's ListenBrainz and Audioscrobbler compatibility surfaces) silently recorded nothing: the saved server address dropped the API path, so listens were sent to a route that does not exist while the account still showed as connected.
|
||||
* The correct API path is now kept when connecting. Reconnect an affected account once so it picks up the corrected address.
|
||||
|
||||
|
||||
|
||||
### Servers — complete border on the active server card
|
||||
|
||||
**By [@Psychotoxical](https://github.com/Psychotoxical), PR [#998](https://github.com/Psychotoxical/psysonic/pull/998)**
|
||||
|
||||
@@ -31,11 +31,17 @@ describe('apiKeyOnlyStrategy', () => {
|
||||
await expect(apiKeyOnlyStrategy.connect(ctx({}))).rejects.toBeInstanceOf(MusicNetworkError);
|
||||
});
|
||||
|
||||
it('prefers a field baseUrl over the context baseUrl', async () => {
|
||||
it('returns the resolved API base (with suffix), not the raw field origin', async () => {
|
||||
// The runtime passes ctx.baseUrl already resolved to origin + selfHostedApiSuffix
|
||||
// (e.g. /apis/listenbrainz for Koito). The strategy must persist that, not the
|
||||
// bare fields.baseUrl — otherwise scrobbles miss the /apis/listenbrainz path.
|
||||
const res = await apiKeyOnlyStrategy.connect(
|
||||
ctx({ token: 't', baseUrl: 'https://maloja.example' }, 'https://fallback'),
|
||||
ctx(
|
||||
{ token: 't', baseUrl: 'https://koito.example' },
|
||||
'https://koito.example/apis/listenbrainz',
|
||||
),
|
||||
);
|
||||
expect(res.baseUrl).toBe('https://maloja.example');
|
||||
expect(res.baseUrl).toBe('https://koito.example/apis/listenbrainz');
|
||||
});
|
||||
|
||||
it('falls back to the context baseUrl when no field given', async () => {
|
||||
|
||||
@@ -24,7 +24,12 @@ export const apiKeyOnlyStrategy: AuthStrategy = {
|
||||
providerId: ctx.presetId,
|
||||
});
|
||||
}
|
||||
const baseUrl = (ctx.fields.baseUrl ?? '').trim() || ctx.baseUrl;
|
||||
// ctx.baseUrl is the resolved API base (origin + selfHostedApiSuffix, e.g.
|
||||
// `/apis/listenbrainz` for Koito) built by the runtime. Prefer it over the
|
||||
// raw `fields.baseUrl`, which is the bare origin without the suffix — using
|
||||
// the latter sends scrobbles to `<origin>/1/submit-listens` (404/405) instead
|
||||
// of `<origin>/apis/listenbrainz/1/submit-listens`.
|
||||
const baseUrl = ctx.baseUrl || (ctx.fields.baseUrl ?? '').trim();
|
||||
return {
|
||||
sessionKey: token,
|
||||
username: (ctx.fields.username ?? '').trim(),
|
||||
|
||||
Reference in New Issue
Block a user