mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-22 15:25:46 +00:00
7c724a642f
* chore(eslint): add eslint toolchain and configs * fix(eslint): resolve gradual-config errors (rules-of-hooks, no-empty, prefer-const, …) * chore(eslint): clear unused vars in config, contexts, app and test * chore(eslint): clear unused vars in api and music-network * chore(eslint): clear unused vars in utils * chore(eslint): clear unused vars in store * chore(eslint): clear unused vars in cover and hooks * chore(eslint): clear unused vars in components * chore(eslint): clear unused vars in pages * chore(eslint): remove explicit any in src * chore(eslint): align react-hooks exhaustive-deps * chore(eslint): zero gradual config on src * chore(eslint): strict hook rules in store and utils * chore(eslint): strict hook rules in hooks and cover * chore(eslint): strict hook rules in components * chore(eslint): strict hook rules in pages and contexts * chore(eslint): document scripts ignore in eslint config * chore(eslint): add lint script and zero strict findings * chore(eslint): address review round 1 (gradual 0/0, per-site disable reasons) * chore(eslint): tighten two set-state disable comments (review round 2 LOW) * chore(nix): sync npmDepsHash with package-lock.json * docs(changelog): add Under the Hood entry for ESLint setup (PR #1165) --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
123 lines
4.5 KiB
TypeScript
123 lines
4.5 KiB
TypeScript
import md5 from 'md5';
|
|
import { coverStorageKeyFromRef } from '../cover/storageKeys';
|
|
import { coverEntryToRef, resolveAlbumCoverEntry } from '../cover/resolveEntry';
|
|
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';
|
|
|
|
function coverArtQueryParams(username: string, password: string, id: string, size: number): URLSearchParams {
|
|
const salt = secureRandomSalt();
|
|
const token = md5(password + salt);
|
|
return new URLSearchParams({
|
|
id,
|
|
size: String(size),
|
|
u: username,
|
|
t: token,
|
|
s: salt,
|
|
v: '1.16.1',
|
|
c: SUBSONIC_CLIENT,
|
|
f: 'json',
|
|
});
|
|
}
|
|
|
|
function streamUrlFromProfile(
|
|
serverUrl: string,
|
|
username: string,
|
|
password: string,
|
|
id: string,
|
|
): string {
|
|
const baseUrl = restBaseFromUrl(serverUrl);
|
|
const salt = secureRandomSalt();
|
|
const token = md5(password + salt);
|
|
const p = new URLSearchParams({
|
|
id,
|
|
u: username,
|
|
t: token,
|
|
s: salt,
|
|
v: '1.16.1',
|
|
c: SUBSONIC_CLIENT,
|
|
f: 'json',
|
|
});
|
|
return `${baseUrl}/stream.view?${p.toString()}`;
|
|
}
|
|
|
|
export function buildStreamUrlForServer(serverId: string, id: string): string {
|
|
const server = findServerByIdOrIndexKey(serverId);
|
|
if (!server) return buildStreamUrl(id);
|
|
// Dual-address: route the stream through the cached connect endpoint.
|
|
return streamUrlFromProfile(connectBaseUrlForServer(server), server.username, server.password, id);
|
|
}
|
|
|
|
export function buildStreamUrl(id: string): string {
|
|
const { getBaseUrl, getActiveServer } = useAuthStore.getState();
|
|
const server = getActiveServer();
|
|
const baseUrl = getBaseUrl();
|
|
if (!server || !baseUrl) return streamUrlFromProfile('', '', '', id);
|
|
// `getBaseUrl()` already returns the cached connect URL; use it directly
|
|
// instead of re-normalizing `server.url`, which would bypass the dual-
|
|
// address connect cache.
|
|
return streamUrlFromProfile(baseUrl, server.username, server.password, id);
|
|
}
|
|
|
|
/** @deprecated Use `coverStorageKey` from `src/cover/storageKeys` — shim until migration. */
|
|
export function coverArtCacheKey(id: string, size = 256): string {
|
|
const entry = resolveAlbumCoverEntry(id, id);
|
|
const ref = coverEntryToRef(entry ?? { cacheKind: 'album', cacheEntityId: id, fetchCoverArtId: id });
|
|
return coverStorageKeyFromRef(ref, size as CoverArtTier);
|
|
}
|
|
|
|
/** @deprecated Use `coverStorageKey` from `src/cover/storageKeys` — shim until migration. */
|
|
export function coverArtCacheKeyForServer(serverIdOrKey: string, id: string, size = 256): string {
|
|
const server = findServerByIdOrIndexKey(serverIdOrKey);
|
|
if (!server) return `${serverIdOrKey}:cover:album:${id}:${size}`;
|
|
const entry = resolveAlbumCoverEntry(id, id);
|
|
const ref = coverEntryToRef(
|
|
entry ?? { cacheKind: 'album', cacheEntityId: id, fetchCoverArtId: id },
|
|
{
|
|
kind: 'server',
|
|
serverId: server.id,
|
|
url: server.url,
|
|
username: server.username,
|
|
password: server.password,
|
|
},
|
|
);
|
|
return coverStorageKeyFromRef(ref, size as CoverArtTier);
|
|
}
|
|
|
|
/** @deprecated Use `buildCoverArtFetchUrl` from `src/cover/fetchUrl` — shim until migration. */
|
|
export function buildCoverArtUrl(id: string, size = 256): string {
|
|
const { getBaseUrl, getActiveServer } = useAuthStore.getState();
|
|
const server = getActiveServer();
|
|
const baseUrl = getBaseUrl();
|
|
const p = coverArtQueryParams(server?.username ?? '', server?.password ?? '', id, size);
|
|
return `${baseUrl}/rest/getCoverArt.view?${p.toString()}`;
|
|
}
|
|
|
|
/** @deprecated Use `buildCoverArtFetchUrl` from `src/cover/fetchUrl` — shim until migration. */
|
|
export function buildCoverArtUrlForServer(
|
|
serverUrl: string,
|
|
username: string,
|
|
password: string,
|
|
id: string,
|
|
size = 256,
|
|
): string {
|
|
const p = coverArtQueryParams(username, password, id, size);
|
|
return `${restBaseFromUrl(serverUrl)}/getCoverArt.view?${p.toString()}`;
|
|
}
|
|
|
|
export function buildDownloadUrl(id: string): string {
|
|
const { getBaseUrl, getActiveServer } = useAuthStore.getState();
|
|
const server = getActiveServer();
|
|
const baseUrl = getBaseUrl();
|
|
const salt = secureRandomSalt();
|
|
const token = md5((server?.password ?? '') + salt);
|
|
const p = new URLSearchParams({
|
|
id,
|
|
u: server?.username ?? '',
|
|
t: token, s: salt, v: '1.16.1', c: SUBSONIC_CLIENT, f: 'json',
|
|
});
|
|
return `${baseUrl}/rest/download.view?${p.toString()}`;
|
|
}
|