mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-21 23:05:46 +00:00
feat: Rust audio engine, genre mix, queue shuffle, and UX improvements (v1.2.0)
### Audio Engine - Replace Howler.js with native Rust/rodio backend (src-tauri/src/audio.rs) - audio_play/pause/resume/stop/seek/set_volume Tauri commands - Generation counter cancels stale in-flight downloads on skip - Wall-clock position tracking; audio:ended after 2 ticks near end ### Playback Persistence - Persist currentTrack, queue, queueIndex, currentTime to localStorage - Cold-start resume: play + seek to saved position on app restart - Position priority: server queue > localStorage ### Random Mix - Genre blacklist: excludeAudiobooks toggle + custom chip-based blacklist - Clickable genre chips in tracklist to blacklist on the fly - Super Genre Mix: 9 genres, progressive rendering, Load 10 more - Promise.allSettled + 45s timeout for large Metal/Rock libraries - Two-column filter panel at top of page ### Queue & UI - Shuffle button in queue header (Fisher-Yates, keeps current track at 0) - LiveSearch arrow key / Enter / Escape keyboard navigation - Multi-line tooltip support (data-tooltip-wrap attribute) - Update link uses Tauri shell open() to launch system browser - Sidebar min-width 180px → 200px (fixes Psysonic title clipping) - Tooltip z-index fix: main-content z-index:1 over queue panel Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import React, { useState, useRef } from 'react';
|
||||
import { Track, usePlayerStore } from '../store/playerStore';
|
||||
import { Play, Music, Star, X, Trash2, Save, FolderOpen } from 'lucide-react';
|
||||
import { Play, Music, Star, X, Trash2, Save, FolderOpen, Shuffle } from 'lucide-react';
|
||||
import { buildCoverArtUrl, getAlbum, getPlaylists, getPlaylist, createPlaylist, deletePlaylist, SubsonicPlaylist } from '../api/subsonic';
|
||||
import { useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -122,6 +122,7 @@ export default function QueuePanel() {
|
||||
const clearQueue = usePlayerStore(s => s.clearQueue);
|
||||
|
||||
const reorderQueue = usePlayerStore(s => s.reorderQueue);
|
||||
const shuffleQueue = usePlayerStore(s => s.shuffleQueue);
|
||||
const enqueue = usePlayerStore(s => s.enqueue);
|
||||
|
||||
const [draggedIdx, setDraggedIdx] = useState<number | null>(null);
|
||||
@@ -247,6 +248,9 @@ export default function QueuePanel() {
|
||||
<h2 style={{ fontSize: '14px', fontWeight: 600, margin: 0 }}>{t('queue.title')}</h2>
|
||||
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
|
||||
<button onClick={() => shuffleQueue()} style={{ color: 'var(--text-muted)', cursor: 'pointer', display: 'flex' }} aria-label={t('queue.shuffle')} data-tooltip={t('queue.shuffle')} disabled={queue.length < 2}>
|
||||
<Shuffle size={14} />
|
||||
</button>
|
||||
<button onClick={handleSave} style={{ color: 'var(--text-muted)', cursor: 'pointer', display: 'flex' }} aria-label={t('queue.savePlaylist')} data-tooltip={t('queue.save')}>
|
||||
<Save size={14} />
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user