mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-22 07:15:47 +00:00
feat(lyrics): configurable sources with drag-to-reorder + feat(audio): ReplayGain pre-gain & fallback
Lyrics Sources: - Replace lyricsServerFirst + enableNeteaselyrics toggles with a new Settings section — three sources (Server, LRCLIB, Netease) each individually toggled and drag-reorderable via psy-drop DnD - useLyrics.ts iterates sources in user-defined order, skipping disabled ones; embedded SYLT from local files still wins unconditionally - onRehydrateStorage migration from legacy fields on first load ReplayGain Pre-Gain: - New authStore fields: replayGainPreGainDb (0…+6 dB) and replayGainFallbackDb (-6…0 dB, untagged / radio) - audio.rs compute_gain applies pre_gain_db and fallback_db - Settings sliders under ReplayGain mode selector - Radio HTML5 volume scaled by fallbackDb factor on playRadio i18n(ru): incorporate PR #148 translation improvements (kilyabin) All 7 locales updated for new keys. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+15
-14
@@ -1,4 +1,5 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useShallow } from 'zustand/react/shallow';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { fetchLyrics, parseLrc, LrcLine } from '../api/lrclib';
|
||||
import { fetchNeteaselyrics } from '../api/netease';
|
||||
@@ -47,8 +48,7 @@ export interface UseLyricsResult {
|
||||
|
||||
export function useLyrics(currentTrack: Track | null): UseLyricsResult {
|
||||
const cached = currentTrack ? lyricsCache.get(currentTrack.id) : undefined;
|
||||
const lyricsServerFirst = useAuthStore(s => s.lyricsServerFirst);
|
||||
const enableNeteaselyrics = useAuthStore(s => s.enableNeteaselyrics);
|
||||
const lyricsSources = useAuthStore(useShallow(s => s.lyricsSources));
|
||||
|
||||
const [loading, setLoading] = useState(!cached && !!currentTrack);
|
||||
const [syncedLines, setSyncedLines] = useState<LrcLine[] | null>(cached?.syncedLines ?? null);
|
||||
@@ -159,29 +159,30 @@ export function useLyrics(currentTrack: Track | null): UseLyricsResult {
|
||||
}
|
||||
};
|
||||
|
||||
const fetchFns: Record<string, () => Promise<boolean>> = {
|
||||
server: fetchServer,
|
||||
lrclib: fetchLrclibFn,
|
||||
netease: fetchNetease,
|
||||
};
|
||||
|
||||
(async () => {
|
||||
// Embedded lyrics from local file always win (most accurate SYLT data).
|
||||
if (cancelled) return;
|
||||
if (await fetchEmbedded()) return;
|
||||
|
||||
const [first, second] = lyricsServerFirst
|
||||
? [fetchServer, fetchLrclibFn]
|
||||
: [fetchLrclibFn, fetchServer];
|
||||
|
||||
if (cancelled) return;
|
||||
if (await first()) return;
|
||||
if (cancelled) return;
|
||||
if (await second()) return;
|
||||
// Netease as last fallback — only when explicitly enabled
|
||||
if (enableNeteaselyrics) {
|
||||
// Try enabled sources in user-defined order.
|
||||
for (const src of lyricsSources) {
|
||||
if (!src.enabled) continue;
|
||||
const fn = fetchFns[src.id];
|
||||
if (!fn) continue;
|
||||
if (cancelled) return;
|
||||
if (await fetchNetease()) return;
|
||||
if (await fn()) return;
|
||||
}
|
||||
if (!cancelled) store({ syncedLines: null, plainLyrics: null, source: null, notFound: true });
|
||||
})();
|
||||
|
||||
return () => { cancelled = true; };
|
||||
}, [currentTrack?.id, enableNeteaselyrics]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
}, [currentTrack?.id, lyricsSources]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
return { syncedLines, plainLyrics, source, loading, notFound };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user