mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-14 04:20:07 +02:00
add player filters to omit songs from queue based on criteria
This commit is contained in:
@@ -3,6 +3,9 @@ import { QueryClient } from '@tanstack/react-query';
|
||||
import { api } from '/@/renderer/api';
|
||||
import { queryKeys } from '/@/renderer/api/query-keys';
|
||||
import { folderQueries } from '/@/renderer/features/folders/api/folder-api';
|
||||
import { PlayerFilter, useSettingsStore } from '/@/renderer/store';
|
||||
import { LogCategory, logFn } from '/@/renderer/utils/logger';
|
||||
import { logMsg } from '/@/renderer/utils/logger-message';
|
||||
import { sortSongList } from '/@/shared/api/utils';
|
||||
import {
|
||||
PlaylistSongListQuery,
|
||||
@@ -330,3 +333,131 @@ export const getSongById = async (args: {
|
||||
totalRecordCount: 1,
|
||||
};
|
||||
};
|
||||
|
||||
const getSongFieldValue = (song: Song, field: string): boolean | null | number | string => {
|
||||
switch (field) {
|
||||
case 'albumArtist':
|
||||
return song.albumArtists[0]?.name || '';
|
||||
case 'artist':
|
||||
return song.artistName || song.artists[0]?.name || '';
|
||||
case 'duration':
|
||||
return song.duration;
|
||||
case 'favorite':
|
||||
return song.userFavorite;
|
||||
case 'genre':
|
||||
return song.genres[0]?.name || '';
|
||||
case 'name':
|
||||
return song.name;
|
||||
case 'note':
|
||||
return song.comment || '';
|
||||
case 'path':
|
||||
return song.path || '';
|
||||
case 'playCount':
|
||||
return song.playCount;
|
||||
case 'rating':
|
||||
return song.userRating || 0;
|
||||
case 'year':
|
||||
return song.releaseYear || 0;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const matchesFilter = (song: Song, filter: PlayerFilter): boolean => {
|
||||
const songValue = getSongFieldValue(song, filter.field);
|
||||
const filterValue = filter.value;
|
||||
|
||||
// Handle null/undefined values
|
||||
if (songValue === null || songValue === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (filter.operator) {
|
||||
case 'contains':
|
||||
return String(songValue).toLowerCase().includes(String(filterValue).toLowerCase());
|
||||
case 'endsWith':
|
||||
return String(songValue).toLowerCase().endsWith(String(filterValue).toLowerCase());
|
||||
case 'is':
|
||||
return String(songValue).toLowerCase() === String(filterValue).toLowerCase();
|
||||
case 'isNot':
|
||||
return String(songValue).toLowerCase() !== String(filterValue).toLowerCase();
|
||||
case 'lt':
|
||||
return Number(songValue) < Number(filterValue);
|
||||
case 'notContains':
|
||||
return !String(songValue).toLowerCase().includes(String(filterValue).toLowerCase());
|
||||
case 'regex': {
|
||||
try {
|
||||
const regex = new RegExp(String(filterValue), 'i');
|
||||
return regex.test(String(songValue));
|
||||
} catch {
|
||||
// Invalid regex pattern, don't match
|
||||
return false;
|
||||
}
|
||||
}
|
||||
case 'gt':
|
||||
return Number(songValue) > Number(filterValue);
|
||||
case 'startsWith':
|
||||
return String(songValue).toLowerCase().startsWith(String(filterValue).toLowerCase());
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
export const filterSongsByPlayerFilters = (songs: Song[], filters: PlayerFilter[]): Song[] => {
|
||||
// Filter out invalid filters (missing field, operator, or value)
|
||||
const validFilters = filters.filter(
|
||||
(filter) =>
|
||||
filter.field &&
|
||||
filter.operator &&
|
||||
filter.value !== undefined &&
|
||||
filter.value !== null &&
|
||||
filter.value !== '',
|
||||
);
|
||||
|
||||
// If no valid filters, return all songs
|
||||
if (validFilters.length === 0) {
|
||||
return songs;
|
||||
}
|
||||
|
||||
// Track filtered songs and their matching conditions
|
||||
const filteredSongs: Array<{ filter: PlayerFilter; song: Song }> = [];
|
||||
|
||||
// Filter OUT songs that match any of the filters (exclude matching songs)
|
||||
const filtered = songs.filter((song) => {
|
||||
const matchingFilter = validFilters.find((filter) => matchesFilter(song, filter));
|
||||
if (matchingFilter) {
|
||||
filteredSongs.push({ filter: matchingFilter, song });
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if (filteredSongs.length > 0) {
|
||||
logFn.debug(logMsg[LogCategory.PLAYER].playerFiltersApplied, {
|
||||
category: LogCategory.PLAYER,
|
||||
meta: {
|
||||
filteredCount: filteredSongs.length,
|
||||
filteredSongs: filteredSongs.map(({ filter, song }) => ({
|
||||
artist: song.artistName,
|
||||
condition: {
|
||||
field: filter.field,
|
||||
operator: filter.operator,
|
||||
value: filter.value,
|
||||
},
|
||||
songId: song.id,
|
||||
songName: song.name,
|
||||
})),
|
||||
originalCount: songs.length,
|
||||
remainingCount: filtered.length,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return filtered;
|
||||
};
|
||||
|
||||
export const getPlayerFiltersAndFilterSongs = (songs: Song[]): Song[] => {
|
||||
const state = useSettingsStore.getState();
|
||||
const filters = state.playback.filters;
|
||||
return filterSongsByPlayerFilters(songs, filters);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user