refactor(api): F.52 — move credential helpers to subsonicClient (#617)

Move `apiWithCredentials` + `restBaseFromUrl` from `api/subsonic.ts`
into `api/subsonicClient.ts` where the other token-auth primitives
already live. They are the credential-bearing variants of `api`,
sitting in the same client module is the natural home.

`api/subsonic.ts` now holds only the connection-probing domain:
`ping`, `pingWithCredentials`, `probeInstantMixWithCredentials`,
`scheduleInstantMixProbeForServer`. Clear, cohesive, stable import
path for the call sites that still use it.

Pure code-move. subsonic.ts: 144 → 119 LOC. Total Phase F journey:
1333 → 119 LOC (~91% reduction).
This commit is contained in:
Frank Stellmacher
2026-05-13 01:08:48 +02:00
committed by GitHub
parent f7b2799d39
commit e04980626d
2 changed files with 26 additions and 26 deletions
+1 -26
View File
@@ -9,7 +9,7 @@ import {
import {
SUBSONIC_CLIENT,
api,
getAuthParams,
apiWithCredentials,
secureRandomSalt,
} from './subsonicClient';
import type { PingWithCredentialsResult, SubsonicSong } from './subsonicTypes';
@@ -53,31 +53,6 @@ export async function pingWithCredentials(
}
}
function restBaseFromUrl(serverUrl: string): string {
const base = serverUrl.startsWith('http') ? serverUrl.replace(/\/$/, '') : `http://${serverUrl.replace(/\/$/, '')}`;
return `${base}/rest`;
}
async function apiWithCredentials<T>(
serverUrl: string,
username: string,
password: string,
endpoint: string,
extra: Record<string, unknown> = {},
timeout = 15000,
): Promise<T> {
const params = { ...getAuthParams(username, password), ...extra };
const resp = await axios.get(`${restBaseFromUrl(serverUrl)}/${endpoint}`, {
params,
paramsSerializer: { indexes: null },
timeout,
});
const data = resp.data?.['subsonic-response'];
if (!data) throw new Error('Invalid response from server (possibly not a Subsonic server)');
if (data.status !== 'ok') throw new Error(data.error?.message ?? 'Subsonic API error');
return data as T;
}
const INSTANT_MIX_PROBE_RANDOM_SIZE = 8;
const INSTANT_MIX_PROBE_SIMILAR_COUNT = 12;
const INSTANT_MIX_PROBE_MAX_TRACKS = 4;
+25
View File
@@ -17,6 +17,31 @@ export function getAuthParams(username: string, password: string) {
return { u: username, t: token, s: salt, v: '1.16.1', c: SUBSONIC_CLIENT, f: 'json' };
}
export function restBaseFromUrl(serverUrl: string): string {
const base = serverUrl.startsWith('http') ? serverUrl.replace(/\/$/, '') : `http://${serverUrl.replace(/\/$/, '')}`;
return `${base}/rest`;
}
export async function apiWithCredentials<T>(
serverUrl: string,
username: string,
password: string,
endpoint: string,
extra: Record<string, unknown> = {},
timeout = 15000,
): Promise<T> {
const params = { ...getAuthParams(username, password), ...extra };
const resp = await axios.get(`${restBaseFromUrl(serverUrl)}/${endpoint}`, {
params,
paramsSerializer: { indexes: null },
timeout,
});
const data = resp.data?.['subsonic-response'];
if (!data) throw new Error('Invalid response from server (possibly not a Subsonic server)');
if (data.status !== 'ok') throw new Error(data.error?.message ?? 'Subsonic API error');
return data as T;
}
export function getClient() {
const { getBaseUrl, getActiveServer } = useAuthStore.getState();
const server = getActiveServer();