remove similarSongs from autodj when music folder is selected (#1451)

- Similar songs queries for both Subsonic/Jellyfin do not support querying by musicFolderId
This commit is contained in:
jeffvli
2025-12-28 14:09:22 -08:00
parent d5d995de5f
commit 0a54f7c44c
5 changed files with 75 additions and 24 deletions
+5 -1
View File
@@ -567,7 +567,11 @@ export const controller: GeneralController = {
return apiController( return apiController(
'getSimilarSongs', 'getSimilarSongs',
server.type, server.type,
)?.({ ...args, apiClientProps: { ...args.apiClientProps, server } }); )?.({
...args,
apiClientProps: { ...args.apiClientProps, server },
query: mergeMusicFolderId(args.query, server),
});
}, },
getSongDetail(args) { getSongDetail(args) {
const server = getServerById(args.apiClientProps.serverId); const server = getServerById(args.apiClientProps.serverId);
@@ -923,7 +923,12 @@ export const JellyfinController: InternalControllerEndpoint = {
throw new Error('Failed to get server info'); throw new Error('Failed to get server info');
} }
const features = getFeatures(VERSION_INFO, res.body.Version); const defaultFeatures = {};
const features = {
...defaultFeatures,
...getFeatures(VERSION_INFO, res.body.Version),
};
return { return {
features, features,
@@ -8,6 +8,7 @@ import {
isShuffleEnabled, isShuffleEnabled,
mapShuffledToQueueIndex, mapShuffledToQueueIndex,
useAutoDJSettings, useAutoDJSettings,
useCurrentServer,
useCurrentServerId, useCurrentServerId,
usePlayerStore, usePlayerStore,
usePlayerStoreBase, usePlayerStoreBase,
@@ -15,16 +16,21 @@ import {
import { LogCategory, logFn } from '/@/renderer/utils/logger'; import { LogCategory, logFn } from '/@/renderer/utils/logger';
import { logMsg } from '/@/renderer/utils/logger-message'; import { logMsg } from '/@/renderer/utils/logger-message';
import { shuffleInPlace } from '/@/renderer/utils/shuffle'; import { shuffleInPlace } from '/@/renderer/utils/shuffle';
import { Played, SongListSort, SortOrder } from '/@/shared/types/domain-types'; import { hasFeature } from '/@/shared/api/utils';
import { Played, Song, SongListSort, SortOrder } from '/@/shared/types/domain-types';
import { ServerFeature } from '/@/shared/types/features-types';
import { Play } from '/@/shared/types/types'; import { Play } from '/@/shared/types/types';
export const useAutoDJ = () => { export const useAutoDJ = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const serverId = useCurrentServerId(); const serverId = useCurrentServerId();
const server = useCurrentServer();
const player = usePlayer(); const player = usePlayer();
const settings = useAutoDJSettings(); const settings = useAutoDJSettings();
const isFetching = useIsPlayerFetching(); const isFetching = useIsPlayerFetching();
const hasSimilarSongsMusicFolder = hasFeature(server, ServerFeature.SIMILAR_SONGS_MUSIC_FOLDER);
useEffect(() => { useEffect(() => {
const unsubscribe = usePlayerStoreBase.subscribe( const unsubscribe = usePlayerStoreBase.subscribe(
(state) => { (state) => {
@@ -61,35 +67,43 @@ export const useAutoDJ = () => {
}); });
try { try {
// First, try to fetch similar songs based on the current song
const similarSongs = await queryClient.fetchQuery({
...songsQueries.similar({
query: {
count: settings.itemCount,
songId: properties.song?.id,
},
serverId,
}),
queryKey: queryKeys.player.fetch({ similarSongs: properties.song?.id }),
});
const queue = usePlayerStore.getState().getQueue(); const queue = usePlayerStore.getState().getQueue();
const queueSongIdSet = new Set(queue.items.map((item) => item.id)); const queueSongIdSet = new Set(queue.items.map((item) => item.id));
const uniqueSimilarSongs = similarSongs.filter( let uniqueSimilarSongs: Song[] = [];
(song) => !queueSongIdSet.has(song.id),
); const hasMusicFolder = server?.musicFolderId && server.musicFolderId.length > 0;
const trySimilarSongs = hasMusicFolder && hasSimilarSongsMusicFolder;
// Skip similar songs fetch if a music folder is selected and does not support musicFolderId on similar songs
if (trySimilarSongs) {
// First, try to fetch similar songs based on the current song
const similarSongs = await queryClient.fetchQuery({
...songsQueries.similar({
query: {
count: settings.itemCount,
songId: properties.song?.id,
},
serverId,
}),
queryKey: queryKeys.player.fetch({ similarSongs: properties.song?.id }),
});
uniqueSimilarSongs = similarSongs.filter(
(song) => !queueSongIdSet.has(song.id),
);
}
// If not enough songs, try to fetch more similar songs based on the genre of the current song // If not enough songs, try to fetch more similar songs based on the genre of the current song
if (uniqueSimilarSongs.length < settings.itemCount) { if (uniqueSimilarSongs.length < settings.itemCount) {
const genre = properties.song?.genres?.[0]; const genre = properties.song?.genres?.[0];
if (genre) { if (genre) {
const genreLimit = 50;
const genreSimilarSongs = await queryClient.fetchQuery({ const genreSimilarSongs = await queryClient.fetchQuery({
...songsQueries.random({ ...songsQueries.random({
query: { query: {
genre: genre.id, genre: genre.id,
limit: 50, limit: genreLimit,
played: Played.All, played: Played.All,
}, },
serverId, serverId,
@@ -100,11 +114,35 @@ export const useAutoDJ = () => {
}), }),
}); });
uniqueSimilarSongs.push( const genreSongs = genreSimilarSongs.items.filter(
...genreSimilarSongs.items.filter( (song) => !queueSongIdSet.has(song.id),
(song) => !queueSongIdSet.has(song.id),
),
); );
// If trySimilarSongs is false, add variation by mixing in random songs
if (!trySimilarSongs) {
// Calculate how many random songs we need: 20% or at least 1
const randomSongCount = Math.max(1, Math.ceil(genreLimit * 0.2));
const randomSongs = await queryClient.fetchQuery({
...songsQueries.random({
query: { limit: randomSongCount, played: Played.All },
serverId,
}),
});
const uniqueRandomSongs = randomSongs.items.filter(
(song) => !queueSongIdSet.has(song.id),
);
// Add minimum required random songs for variation
const randomSongsToAdd = uniqueRandomSongs.slice(
0,
randomSongCount,
);
uniqueSimilarSongs.push(...randomSongsToAdd, ...genreSongs);
} else {
uniqueSimilarSongs.push(...genreSongs);
}
} }
} }
@@ -176,9 +214,11 @@ export const useAutoDJ = () => {
return () => unsubscribe(); return () => unsubscribe();
}, [ }, [
hasSimilarSongsMusicFolder,
isFetching, isFetching,
player, player,
queryClient, queryClient,
server,
serverId, serverId,
settings.enabled, settings.enabled,
settings.itemCount, settings.itemCount,
+1
View File
@@ -1584,6 +1584,7 @@ export type SimilarSongsArgs = BaseEndpointArgs & {
export type SimilarSongsQuery = { export type SimilarSongsQuery = {
count?: number; count?: number;
musicFolderId?: string | string[];
songId: string; songId: string;
}; };
+1
View File
@@ -10,6 +10,7 @@ export enum ServerFeature {
PUBLIC_PLAYLIST = 'publicPlaylist', PUBLIC_PLAYLIST = 'publicPlaylist',
SERVER_PLAY_QUEUE = 'serverPlayQueue', SERVER_PLAY_QUEUE = 'serverPlayQueue',
SHARING_ALBUM_SONG = 'sharingAlbumSong', SHARING_ALBUM_SONG = 'sharingAlbumSong',
SIMILAR_SONGS_MUSIC_FOLDER = 'similarSongsMusicFolder',
TAGS = 'tags', TAGS = 'tags',
TRACK_ALBUM_ARTIST_SEARCH = 'trackAlbumArtistSearch', TRACK_ALBUM_ARTIST_SEARCH = 'trackAlbumArtistSearch',
} }