mirror of
https://github.com/kilyabin/psysonic.git
synced 2026-07-22 06:25:41 +00:00
72030f17fd
First Phase F slice. Splits the 1333-LOC `api/subsonic.ts` along its two most obvious axes: - `subsonicTypes.ts` — all ~24 exported interfaces + type aliases (album/song/artist/playlist/directory/genre/now-playing/radio, random-songs filters, three statistics shapes, search + starred results, AlbumInfo, structured-lyrics types, etc.) plus the `RADIO_PAGE_SIZE` constant. - `subsonicClient.ts` — token-auth + `getClient` + `api<T>()` + `libraryFilterParams` + `secureRandomSalt` / `getAuthParams` / `SUBSONIC_CLIENT`. The credential-bearing API helpers (`pingWithCredentials`, `apiWithCredentials`, `restBaseFromUrl`, `probeInstantMixWithCredentials`) stay in `subsonic.ts` for now — they could move into the client module in a follow-up. 66 external call sites migrated to direct imports from the new modules (no re-export shims in `subsonic.ts`). Pure code-move; contract test stays green. subsonic.ts: 1333 → 1078 LOC (−255).
62 lines
2.0 KiB
TypeScript
62 lines
2.0 KiB
TypeScript
import type { SubsonicSong } from '../api/subsonicTypes';
|
|
import { songToTrack } from '../utils/songToTrack';
|
|
import { usePlayerStore } from '../store/playerStore';
|
|
function fadeOut(setVolume: (v: number) => void, from: number, durationMs: number): Promise<void> {
|
|
return new Promise(resolve => {
|
|
const steps = 16;
|
|
const stepMs = durationMs / steps;
|
|
let step = 0;
|
|
const id = setInterval(() => {
|
|
step++;
|
|
setVolume(Math.max(0, from * (1 - step / steps)));
|
|
if (step >= steps) {
|
|
clearInterval(id);
|
|
resolve();
|
|
}
|
|
}, stepMs);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Play a single song. When `queue` is provided, surrounds the chosen song with that queue
|
|
* so Next/Prev work — pass the rail / pool the click came from. Mirrors playAlbum's fade-out.
|
|
*/
|
|
export async function playSongNow(song: SubsonicSong, queue?: SubsonicSong[]): Promise<void> {
|
|
const track = songToTrack(song);
|
|
const tracks = queue && queue.length > 0
|
|
? queue.map(songToTrack)
|
|
: [track];
|
|
|
|
const store = usePlayerStore.getState();
|
|
const { isPlaying, volume } = store;
|
|
|
|
if (isPlaying) {
|
|
await fadeOut(store.setVolume, volume, 700);
|
|
usePlayerStore.setState({ volume });
|
|
}
|
|
|
|
usePlayerStore.getState().playTrack(track, tracks);
|
|
}
|
|
|
|
/**
|
|
* Append the song to the existing queue (if not already there) and immediately jump to it.
|
|
* Existing queue stays intact — different from playSongNow which replaces the queue.
|
|
*/
|
|
export async function enqueueAndPlay(song: SubsonicSong): Promise<void> {
|
|
const track = songToTrack(song);
|
|
const store = usePlayerStore.getState();
|
|
const { isPlaying, volume, queue } = store;
|
|
|
|
if (isPlaying) {
|
|
await fadeOut(store.setVolume, volume, 700);
|
|
usePlayerStore.setState({ volume });
|
|
}
|
|
|
|
if (!queue.some(t => t.id === track.id)) {
|
|
usePlayerStore.getState().enqueue([track]);
|
|
}
|
|
// playTrack with no queue arg uses the current state.queue, finds the track by id,
|
|
// and sets queueIndex accordingly.
|
|
usePlayerStore.getState().playTrack(track);
|
|
}
|