fix(queue): respect rating filter in infinite queue top-up (#357)

Apply mix rating settings to infinite queue candidates and keep fetching random batches when filtered results are below the target size, so top-ups can still reach five tracks when possible.
This commit is contained in:
cucadmuh
2026-04-29 10:33:50 +03:00
committed by GitHub
parent cf24dc0e7b
commit 8a38127bf7
+29 -9
View File
@@ -17,6 +17,11 @@ import { useOrbitStore } from './orbitStore';
import { estimateLivePosition } from '../api/orbit'; import { estimateLivePosition } from '../api/orbit';
import { loudnessGainPlaceholderUntilCacheDb } from '../utils/loudnessPlaceholder'; import { loudnessGainPlaceholderUntilCacheDb } from '../utils/loudnessPlaceholder';
import { effectiveLoudnessPreAnalysisAttenuationDb } from '../utils/loudnessPreAnalysisSlider'; import { effectiveLoudnessPreAnalysisAttenuationDb } from '../utils/loudnessPreAnalysisSlider';
import {
enrichSongsForMixRatingFilter,
getMixMinRatingsConfigFromAuth,
passesMixMinRatings,
} from '../utils/mixRatingFilter';
export interface Track { export interface Track {
id: string; id: string;
@@ -121,6 +126,8 @@ async function buildInfiniteQueueCandidates(
existingIds: Set<string>, existingIds: Set<string>,
count = 5, count = 5,
): Promise<Track[]> { ): Promise<Track[]> {
const RANDOM_TOPUP_BATCH_SIZE = Math.max(10, count * 2);
const RANDOM_TOPUP_MAX_BATCHES = 8;
const artistId = seedTrack?.artistId?.trim() || null; const artistId = seedTrack?.artistId?.trim() || null;
const artistName = seedTrack?.artist?.trim() || null; const artistName = seedTrack?.artist?.trim() || null;
@@ -130,22 +137,35 @@ async function buildInfiniteQueueCandidates(
]); ]);
const seedId = seedTrack?.id ?? null; const seedId = seedTrack?.id ?? null;
const mixCandidates = shuffleArray( const mixCfg = getMixMinRatingsConfigFromAuth();
[...top, ...similar] const mixedSources = [...top, ...similar];
const filteredMixedSongs = mixCfg.enabled
? (await enrichSongsForMixRatingFilter(mixedSources, mixCfg)).filter(s => passesMixMinRatings(s, mixCfg))
: mixedSources;
const out: Track[] = shuffleArray(
filteredMixedSongs
.map(songToTrack) .map(songToTrack)
.filter(t => t.id !== seedId && !existingIds.has(t.id)), .filter(t => t.id !== seedId && !existingIds.has(t.id)),
) )
.slice(0, count) .slice(0, count)
.map(t => ({ ...t, autoAdded: true as const })); .map(t => ({ ...t, autoAdded: true as const }));
if (mixCandidates.length > 0) return mixCandidates; const seenIds = new Set<string>([...existingIds, ...out.map(t => t.id)]);
for (let b = 0; out.length < count && b < RANDOM_TOPUP_MAX_BATCHES; b++) {
const random = await getRandomSongs(RANDOM_TOPUP_BATCH_SIZE, seedTrack?.genre).catch(() => []);
if (!random.length) break;
const filteredRandomSongs = mixCfg.enabled
? (await enrichSongsForMixRatingFilter(random, mixCfg)).filter(s => passesMixMinRatings(s, mixCfg))
: random;
for (const track of shuffleArray(filteredRandomSongs.map(songToTrack))) {
if (track.id === seedId || seenIds.has(track.id)) continue;
out.push({ ...track, autoAdded: true as const });
seenIds.add(track.id);
if (out.length >= count) break;
}
}
const random = await getRandomSongs(count, seedTrack?.genre).catch(() => []); return out.slice(0, count);
return random
.map(songToTrack)
.filter(t => t.id !== seedId && !existingIds.has(t.id))
.slice(0, count)
.map(t => ({ ...t, autoAdded: true as const }));
} }
interface PlayerState { interface PlayerState {