fix(player): minor hot-cache eviction, prefetch budget, and settings live size

Small fix for hot playback cache: eviction keeps current and next only;
prefetch up to five tracks when under cap, always fetch the immediate next;
grace for the previous current until debounce; run evict immediately on
MB or folder changes; re-read cap after download; optional Track.size;
live disk usage on Audio settings.
This commit is contained in:
Maxim Isaev
2026-04-11 03:29:59 +03:00
parent 20dabbfd03
commit b4c3124168
5 changed files with 205 additions and 25 deletions
+6 -3
View File
@@ -1,9 +1,11 @@
import { create } from 'zustand';
import { persist, createJSONStorage } from 'zustand/middleware';
import { invoke } from '@tauri-apps/api/core';
import { isHotCachePreviousTrackUnderGrace } from '../utils/hotCacheGate';
import type { Track } from './playerStore';
const PREFETCH_AHEAD = 5;
/** How many queue slots after the current index are eviction-protected (1 = current + next only). */
export const HOT_CACHE_PROTECT_AFTER_CURRENT = 1;
export interface HotCacheEntry {
localPath: string;
@@ -22,7 +24,7 @@ interface HotCacheState {
touchPlayed: (trackId: string, serverId: string) => void;
removeEntry: (trackId: string, serverId: string) => void;
totalBytes: () => number;
/** Evict until total size ≤ maxBytes. Respects queue tail first, protects current + next N. */
/** Evict until total size ≤ maxBytes. Protects current + next (+ grace for last «previous» track). */
evictToFit: (
queue: Track[],
queueIndex: number,
@@ -103,7 +105,7 @@ export const useHotCacheStore = create<HotCacheState>()(
if (maxBytes <= 0) return;
const protectLo = Math.max(0, queueIndex);
const protectHi = Math.min(queue.length - 1, queueIndex + PREFETCH_AHEAD);
const protectHi = Math.min(queue.length - 1, queueIndex + HOT_CACHE_PROTECT_AFTER_CURRENT);
const protectedIds = new Set<string>();
for (let i = protectLo; i <= protectHi; i++) {
protectedIds.add(queue[i].id);
@@ -127,6 +129,7 @@ export const useHotCacheStore = create<HotCacheState>()(
if (!parsed) continue;
const { serverId, trackId } = parsed;
if (protectedIds.has(trackId) && serverId === activeServerId) continue;
if (isHotCachePreviousTrackUnderGrace(trackId, serverId)) continue;
const meta = entries[key];
const lru = lruStamp(meta);
+3
View File
@@ -32,6 +32,8 @@ export interface Track {
genre?: string;
samplingRate?: number;
bitDepth?: number;
/** Subsonic `size` in bytes when provided by the server (helps hot-cache budgeting). */
size?: number;
autoAdded?: boolean;
radioAdded?: boolean;
}
@@ -58,6 +60,7 @@ export function songToTrack(song: SubsonicSong): Track {
genre: song.genre,
samplingRate: song.samplingRate,
bitDepth: song.bitDepth,
size: song.size,
};
}