refactor(subsonic): centralize the REST protocol version in one constant

The Subsonic protocol version (v=1.16.1) was hardcoded across six call sites.
Hoist it into a single SUBSONIC_API_VERSION constant next to SUBSONIC_CLIENT so
it can be reasoned about and adjusted in one place. No behaviour change.
This commit is contained in:
Psychotoxical
2026-06-10 00:01:40 +02:00
parent 36a0615dcb
commit a8bed6cc91
3 changed files with 16 additions and 7 deletions
+10 -1
View File
@@ -8,6 +8,15 @@ import { findServerByIdOrIndexKey, resolveServerIdForIndexKey } from '../utils/s
export const SUBSONIC_CLIENT = `psysonic/${version}`;
/**
* Subsonic REST protocol version sent on every request. A server returns
* error 30 ("Incompatible REST protocol version") if the client asks for a
* HIGHER version than it supports — surfaced to the user via the connection
* error toast. OpenSubsonic extensions are negotiated via the `openSubsonic`
* response flag, not this number.
*/
export const SUBSONIC_API_VERSION = '1.16.1';
export function secureRandomSalt(): string {
const buf = new Uint8Array(8);
crypto.getRandomValues(buf);
@@ -17,7 +26,7 @@ export function secureRandomSalt(): string {
export function getAuthParams(username: string, password: string) {
const salt = secureRandomSalt();
const token = md5(password + salt);
return { u: username, t: token, s: salt, v: '1.16.1', c: SUBSONIC_CLIENT, f: 'json' };
return { u: username, t: token, s: salt, v: SUBSONIC_API_VERSION, c: SUBSONIC_CLIENT, f: 'json' };
}
export function restBaseFromUrl(serverUrl: string): string {
+4 -4
View File
@@ -5,7 +5,7 @@ import type { CoverArtTier } from '../cover/types';
import { useAuthStore } from '../store/authStore';
import { connectBaseUrlForServer } from '../utils/server/serverEndpoint';
import { findServerByIdOrIndexKey } from '../utils/server/serverLookup';
import { restBaseFromUrl, SUBSONIC_CLIENT, secureRandomSalt } from './subsonicClient';
import { restBaseFromUrl, SUBSONIC_CLIENT, SUBSONIC_API_VERSION, secureRandomSalt } from './subsonicClient';
function coverArtQueryParams(username: string, password: string, id: string, size: number): URLSearchParams {
const salt = secureRandomSalt();
@@ -16,7 +16,7 @@ function coverArtQueryParams(username: string, password: string, id: string, siz
u: username,
t: token,
s: salt,
v: '1.16.1',
v: SUBSONIC_API_VERSION,
c: SUBSONIC_CLIENT,
f: 'json',
});
@@ -36,7 +36,7 @@ function streamUrlFromProfile(
u: username,
t: token,
s: salt,
v: '1.16.1',
v: SUBSONIC_API_VERSION,
c: SUBSONIC_CLIENT,
f: 'json',
});
@@ -116,7 +116,7 @@ export function buildDownloadUrl(id: string): string {
const p = new URLSearchParams({
id,
u: server?.username ?? '',
t: token, s: salt, v: '1.16.1', c: SUBSONIC_CLIENT, f: 'json',
t: token, s: salt, v: SUBSONIC_API_VERSION, c: SUBSONIC_CLIENT, f: 'json',
});
return `${baseUrl}/rest/download.view?${p.toString()}`;
}
+2 -2
View File
@@ -15,7 +15,7 @@
*/
import md5 from 'md5';
import { apiWithCredentials, restBaseFromUrl, secureRandomSalt, SUBSONIC_CLIENT } from '../../api/subsonicClient';
import { apiWithCredentials, restBaseFromUrl, secureRandomSalt, SUBSONIC_CLIENT, SUBSONIC_API_VERSION } from '../../api/subsonicClient';
import type { ServerProfile } from '../../store/authStoreTypes';
import { allNormalizedAddresses } from './serverEndpoint';
@@ -64,7 +64,7 @@ async function fetchPingFingerprint(
u: username,
t: token,
s: salt,
v: '1.16.1',
v: SUBSONIC_API_VERSION,
c: SUBSONIC_CLIENT,
f: 'json',
});