Files
Psychotoxical-psysonic/src/utils/playAlbum.ts
T
Frank Stellmacher 9fac6eb490 refactor(player): E.42 — migrate playerStore re-exports to direct imports (#607)
Migrates ~74 call sites away from the playerStore re-export shims that
were kept during M0–E.41 to avoid touching 30+ imports per PR. Now
that the bigger refactor work is done, each helper goes back to its
real home:

- `initAudioListeners`, `installQueueUndoHotkey`, `flushPlayQueuePosition`
  → from their own store modules
- `getPlaybackProgressSnapshot`, `subscribePlaybackProgress`,
  `PlaybackProgressSnapshot` → from `playbackProgress`
- `resolveReplayGainDb`, `shuffleArray`, `songToTrack`
  → from `utils/*`
- `_resetQueueUndoStacksForTest`, `consumePendingQueueListScrollTop`,
  `registerQueueListScrollTopReader` → from `queueUndo`
- `PlayerState`, `Track` types → from `playerStoreTypes`

Drops the corresponding 13 re-export stubs from `playerStore.ts` and
the now-unused imports. Also drops dead section banners + per-wrapper
comments above one-line action delegates. Trims one stale "(separate
PR)" note in `transportLightActions.ts` since that follow-up landed
in E.39.

`playerStore.ts`: 180 → 112 LOC (−68). Down from Phase E's starting
3732 LOC.

`bootstrap.test.ts` mock target updated from `../store/playerStore`
to `../store/queueUndoHotkey` to keep the spy reachable after the
import change.
2026-05-12 22:46:13 +02:00

56 lines
2.0 KiB
TypeScript

import { getAlbum } from '../api/subsonic';
import { usePlayerStore } from '../store/playerStore';
import { songToTrack } from './songToTrack';
import { useOrbitStore } from '../store/orbitStore';
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);
});
}
export async function playAlbum(albumId: string): Promise<void> {
const albumData = await getAlbum(albumId);
const albumGenre = albumData.album.genre;
const tracks = albumData.songs.map(s => {
const track = songToTrack(s);
if (!track.genre && albumGenre) track.genre = albumGenre;
return track;
});
if (!tracks.length) return;
// In Orbit sessions, playAlbum is effectively an append operation (the
// playerStore bulk-gate also routes replaces into enqueue). Skip the
// fadeOut entirely — the current track keeps playing, the album goes
// onto the end of the queue after the user confirms the bulk dialog.
const orbitRole = useOrbitStore.getState().role;
if (orbitRole === 'host' || orbitRole === 'guest') {
usePlayerStore.getState().enqueue(tracks);
return;
}
const store = usePlayerStore.getState();
const { isPlaying, volume } = store;
if (isPlaying) {
await fadeOut(store.setVolume, volume, 700);
// Restore volume only in the Zustand store — do NOT call audio_set_volume here,
// otherwise the old track glitches back to full volume before playTrack stops it.
// playTrack reads state.volume and passes it to audio_play, so the new track
// starts at the correct volume without the Rust engine ever hearing a restore.
usePlayerStore.setState({ volume });
}
usePlayerStore.getState().playTrack(tracks[0], tracks);
}