mirror of
https://github.com/kilyabin/psysonic.git
synced 2026-07-22 06:25:41 +00:00
feat(cli): expand remote player, opaque play id, tray without libindicator
Add CLI commands for stop, shuffle, repeat, mute, star, rating, reload, server list/switch, and Subsonic search; publish extra snapshot fields and server/search JSON for tooling. Single `play <id>` resolves track, album, or artist; artists play a shuffled full discography. Guard Linux tray creation with catch_unwind when Ayatana/AppIndicator .so is missing. Harden bash completion against zsh sourcing (compopt). Narrow AudioEngine field visibility to silence private_interfaces warnings.
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
import { getAlbum, getArtist } from '../api/subsonic';
|
||||
import { shuffleArray, songToTrack, usePlayerStore } from '../store/playerStore';
|
||||
|
||||
/**
|
||||
* All tracks from the artist’s albums, shuffled — same idea as Artist page “shuffle play”.
|
||||
*/
|
||||
export async function playArtistShuffled(artistId: string): Promise<void> {
|
||||
const { albums } = await getArtist(artistId);
|
||||
if (albums.length === 0) {
|
||||
throw new Error('play_artist_no_tracks');
|
||||
}
|
||||
|
||||
const results = await Promise.all(albums.map(a => getAlbum(a.id)));
|
||||
const sorted = [...results].sort((a, b) => (a.album.year ?? 0) - (b.album.year ?? 0));
|
||||
const tracks = sorted.flatMap(r =>
|
||||
[...r.songs].sort((a, b) => (a.track ?? 0) - (b.track ?? 0)).map(songToTrack),
|
||||
);
|
||||
|
||||
if (tracks.length === 0) {
|
||||
throw new Error('play_artist_no_tracks');
|
||||
}
|
||||
|
||||
const shuffled = shuffleArray(tracks);
|
||||
usePlayerStore.getState().playTrack(shuffled[0], shuffled);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { getAlbum, getSong } from '../api/subsonic';
|
||||
import { playAlbum } from './playAlbum';
|
||||
import { playArtistShuffled } from './playArtistShuffled';
|
||||
import { songToTrack, usePlayerStore } from '../store/playerStore';
|
||||
|
||||
/**
|
||||
* `getSong` → `getAlbum` → `getArtist`: one opaque Subsonic id may refer to a track,
|
||||
* album, or artist depending on the server.
|
||||
*/
|
||||
export async function playByOpaqueId(id: string): Promise<void> {
|
||||
const trimmed = id.trim();
|
||||
if (!trimmed) return;
|
||||
|
||||
const song = await getSong(trimmed);
|
||||
if (song) {
|
||||
usePlayerStore.getState().playTrack(songToTrack(song));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const { songs } = await getAlbum(trimmed);
|
||||
if (songs.length > 0) {
|
||||
await playAlbum(trimmed);
|
||||
return;
|
||||
}
|
||||
} catch {
|
||||
/* not an album */
|
||||
}
|
||||
|
||||
try {
|
||||
await playArtistShuffled(trimmed);
|
||||
} catch {
|
||||
throw new Error('play_by_id_not_found');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user