Browse Source

Add types, normalize API returns

master
jeffvli 3 years ago
committed by Jeff
parent
commit
4af81d4404
  1. 14
      src/__tests__/App.test.tsx
  2. 744
      src/api/api.ts
  3. 108
      src/api/types.ts
  4. 10
      src/components/card/Card.tsx
  5. 56
      src/components/dashboard/Dashboard.tsx
  6. 16
      src/components/library/AlbumList.tsx
  7. 4
      src/components/library/AlbumView.tsx
  8. 4
      src/components/library/ArtistList.tsx
  9. 22
      src/components/library/ArtistView.tsx
  10. 10
      src/components/library/FolderList.tsx
  11. 6
      src/components/library/GenreList.tsx
  12. 29
      src/components/player/NowPlayingMiniView.tsx
  13. 28
      src/components/player/NowPlayingView.tsx
  14. 9
      src/components/playlist/PlaylistList.tsx
  15. 4
      src/components/playlist/PlaylistView.tsx
  16. 4
      src/components/search/SearchView.tsx
  17. 46
      src/components/settings/ListViewColumns.ts
  18. 26
      src/components/shared/ContextMenu.tsx
  19. 10
      src/components/starred/StarredView.tsx

14
src/__tests__/App.test.tsx

@ -220,7 +220,7 @@ const configState: ConfigPage = {
}, },
{ {
id: 'Title', id: 'Title',
dataKey: 'name', dataKey: 'title',
alignment: 'left', alignment: 'left',
flexGrow: 5, flexGrow: 5,
label: 'Title', label: 'Title',
@ -276,11 +276,11 @@ const configState: ConfigPage = {
label: 'CoverArt', label: 'CoverArt',
}, },
{ {
id: 'Name', id: 'Title',
dataKey: 'name', dataKey: 'title',
alignment: 'left', alignment: 'left',
flexGrow: 5, flexGrow: 5,
label: 'Name', label: 'Title',
}, },
{ {
id: 'Albums', id: 'Albums',
@ -311,11 +311,11 @@ const configState: ConfigPage = {
label: '#', label: '#',
}, },
{ {
id: 'Name', id: 'Title',
dataKey: 'name', dataKey: 'title',
alignment: 'left', alignment: 'left',
flexGrow: 5, flexGrow: 5,
label: 'Name', label: 'Tame',
}, },
{ {
id: 'Albums', id: 'Albums',

744
src/api/api.ts

@ -5,6 +5,7 @@ import settings from 'electron-settings';
import { nanoid } from 'nanoid/non-secure'; import { nanoid } from 'nanoid/non-secure';
import axiosRetry from 'axios-retry'; import axiosRetry from 'axios-retry';
import { mockSettings } from '../shared/mockSettings'; import { mockSettings } from '../shared/mockSettings';
import { Item } from './types';
const legacyAuth = const legacyAuth =
process.env.NODE_ENV === 'test' process.env.NODE_ENV === 'test'
@ -31,6 +32,23 @@ const getAuth = (useLegacyAuth: boolean) => {
const auth = getAuth(legacyAuth); const auth = getAuth(legacyAuth);
const API_BASE_URL = `${auth.server}/rest`; const API_BASE_URL = `${auth.server}/rest`;
const authParams = legacyAuth
? {
u: auth.username,
p: auth.password,
v: '1.15.0',
c: 'sonixd',
f: 'json',
}
: {
u: auth.username,
s: auth.salt,
t: auth.hash,
v: '1.15.0',
c: 'sonixd',
f: 'json',
};
export const api = axios.create({ export const api = axios.create({
baseURL: API_BASE_URL, baseURL: API_BASE_URL,
}); });
@ -65,54 +83,6 @@ axiosRetry(api, {
}, },
}); });
export const autoFailApi = axios.create({
baseURL: API_BASE_URL,
validateStatus: () => {
return false;
},
});
autoFailApi.interceptors.request.use((config) => {
config.params = config.params || {};
config.params.u = auth.username;
config.params.s = auth.salt;
config.params.t = auth.hash;
config.params.v = '1.15.0';
config.params.c = 'sonixd';
config.params.f = 'json';
return config;
});
autoFailApi.interceptors.response.use(
(res) => {
// Return the subsonic response directly
res.data = res.data['subsonic-response'];
return res;
},
(err) => {
return Promise.reject(err);
}
);
axiosRetry(autoFailApi, {
retries: 5,
retryCondition: (e: any) => {
return e.response.data['subsonic-response'].status !== 'ok';
},
retryDelay: (retryCount) => {
return retryCount * 1000;
},
});
const authParams = {
u: auth.username,
s: auth.salt,
t: auth.hash,
v: '1.15.0',
c: 'sonixd',
f: 'json',
};
const getCoverArtUrl = (item: any, useLegacyAuth: boolean, size?: number) => { const getCoverArtUrl = (item: any, useLegacyAuth: boolean, size?: number) => {
if (!item.coverArt && !item.artistImageUrl) { if (!item.coverArt && !item.artistImageUrl) {
return 'img/placeholder.jpg'; return 'img/placeholder.jpg';
@ -127,38 +97,14 @@ const getCoverArtUrl = (item: any, useLegacyAuth: boolean, size?: number) => {
} }
if (useLegacyAuth) { if (useLegacyAuth) {
if (!size) {
return (
`${API_BASE_URL}/getCoverArt` +
`?id=${item.coverArt}` +
`&u=${auth.username}` +
`&s=${auth.salt}` +
`&t=${auth.hash}` +
`&v=1.15.0` +
`&c=sonixd`
);
}
return ( return (
`${API_BASE_URL}/getCoverArt` + `${API_BASE_URL}/getCoverArt` +
`?id=${item.coverArt}` + `?id=${item.coverArt}` +
`&u=${auth.username}` + `&u=${auth.username}` +
`&s=${auth.salt}` + `&p=${auth.password}` +
`&t=${auth.hash}` +
`&v=1.15.0` + `&v=1.15.0` +
`&c=sonixd` + `&c=sonixd` +
`&size=${size}` `${size ? `&size=${size}` : ''}`
);
}
if (!size) {
return (
`${API_BASE_URL}/getCoverArt` +
`?id=${item.coverArt}` +
`&u=${auth.username}` +
`&s=${auth.salt}` +
`&t=${auth.hash}` +
`&v=1.15.0` +
`&c=sonixd`
); );
} }
@ -170,7 +116,7 @@ const getCoverArtUrl = (item: any, useLegacyAuth: boolean, size?: number) => {
`&t=${auth.hash}` + `&t=${auth.hash}` +
`&v=1.15.0` + `&v=1.15.0` +
`&c=sonixd` + `&c=sonixd` +
`&size=${size}` `${size ? `&size=${size}` : ''}`
); );
}; };
@ -220,205 +166,147 @@ const getStreamUrl = (id: string, useLegacyAuth: boolean) => {
); );
}; };
export const getPlaylists = async (sortBy: string) => { const normalizeSong = (item: any) => {
const { data } = await api.get('/getPlaylists'); return {
id: item.id,
const newData = parent: item.parent,
sortBy === 'dateCreated' isDir: item.isDir,
? data.playlists?.playlist.sort((a: any, b: any) => { title: item.title,
return a.created > b.created ? -1 : a.created < b.created ? 1 : 0; album: item.album,
}) albumId: item.albumId,
: sortBy === 'dateModified' artist: item.artist,
? data.playlists?.playlist.sort((a: any, b: any) => { artistId: item.artistId,
return a.changed > b.changed ? -1 : a.changed < b.changed ? 1 : 0; track: item.track,
}) year: item.year,
: sortBy === 'name' genre: item.genre,
? _.orderBy(data.playlists.playlist || [], [(entry) => entry.name.toLowerCase()], 'asc') size: item.size,
: data.playlists?.playlist; contentType: item.contentType,
suffix: item.suffix,
return (newData || []).map((playlist: any) => ({ duration: item.duration,
...playlist, bitRate: item.bitRate,
name: playlist.name, path: item.path,
image: playCount: item.playCount,
playlist.songCount > 0 ? getCoverArtUrl(playlist, legacyAuth, 350) : 'img/placeholder.jpg', discNumber: item.discNumber,
type: 'playlist', created: item.created,
streamUrl: getStreamUrl(item.id, legacyAuth),
image: getCoverArtUrl(item, legacyAuth, 150),
starred: item.starred,
type: Item.Music,
uniqueId: nanoid(), uniqueId: nanoid(),
})); };
}; };
export const getPlaylist = async (id: string) => { const normalizeAlbum = (item: any) => {
const { data } = await api.get(`/getPlaylist?id=${id}`);
return { return {
...data.playlist, id: item.id,
entry: null, // Normalize to 'song' instead of 'entry' title: item.name,
song: (data.playlist.entry || []).map((entry: any, index: any) => ({ albumId: item.id,
...entry, artist: item.artist,
streamUrl: getStreamUrl(entry.id, legacyAuth), artistId: item.artistId,
image: getCoverArtUrl(entry, legacyAuth, 150), songCount: item.songCount,
type: 'music', duration: item.duration,
index, created: item.created,
year: item.year,
genre: item.genre,
image: getCoverArtUrl(item, legacyAuth, 350),
isDir: false,
starred: item.starred,
type: Item.Album,
uniqueId: nanoid(), uniqueId: nanoid(),
})), song: (item.song || []).map((entry: any) => normalizeSong(entry)),
image:
data.playlist.songCount > 0
? getCoverArtUrl(data.playlist, legacyAuth, 350)
: 'img/placeholder.jpg',
}; };
}; };
export const getPing = async () => { const normalizeArtist = (item: any) => {
const { data } = await api.get(`/ping`); return {
id: item.id,
return data; title: item.name,
albumCount: item.albumCount,
image: getCoverArtUrl(item, legacyAuth, 350),
starred: item.starred,
type: Item.Artist,
uniqueId: nanoid(),
album: (item.album || []).map((entry: any) => normalizeAlbum(entry)),
}; };
export const getStream = async (id: string) => {
const { data } = await api.get(`/stream?id=${id}`);
return data;
}; };
export const getDownload = async (options: { id: string }) => { const normalizeArtistInfo = (item: any) => {
const { data } = await api.get(`/download?id=${options.id}`, { return {
responseType: 'blob', biography: item.biography,
onDownloadProgress: (progressEvent) => { lastFmUrl: item.lastFmUrl,
const percentCompleted = Math.floor((progressEvent.loaded / progressEvent.total) * 100); imageUrl: item.largeImageUrl || item.mediumImageUrl || item.smallImageUrl,
console.log(`percentCompleted`, percentCompleted); similarArtist: (item.similarArtist || []).map((entry: any) => normalizeArtist(entry)),
}, };
});
return data;
}; };
export const getPlayQueue = async () => { const normalizePlaylist = (item: any) => {
const { data } = await api.get(`/getPlayQueue`);
return { return {
...data.playQueue, id: item.id,
entry: (data.playQueue.entry || []).map((entry: any, index: any) => ({ title: item.name,
...entry, comment: item.comment,
streamUrl: getStreamUrl(entry.id, legacyAuth), owner: item.owner,
index, public: item.public,
})), songCount: item.songCount,
duration: item.duration,
created: item.created,
changed: item.changed,
image: item.songCount > 0 ? getCoverArtUrl(item, legacyAuth, 350) : 'img/placeholder.jpg',
type: Item.Playlist,
uniqueId: nanoid(),
song: (item.entry || []).map((entry: any) => normalizeSong(entry)),
}; };
}; };
export const getStarred = async (options: { musicFolderId?: string | number }) => { const normalizeGenre = (item: any) => {
const { data } = await api.get(`/getStarred2`, { params: options });
return { return {
...data.starred2, id: item.id,
album: (data.starred2.album || []).map((entry: any, index: any) => ({ title: item.value,
...entry, songCount: item.songCount,
title: entry.name, albumCount: item.albumCount,
albumId: entry.id, type: Item.Genre,
image: getCoverArtUrl(entry, legacyAuth, 350),
starred: entry.starred || undefined,
type: 'album',
isDir: false,
index,
uniqueId: nanoid(),
})),
song: (data.starred2.song || []).map((entry: any, index: any) => ({
...entry,
streamUrl: getStreamUrl(entry.id, legacyAuth),
image: getCoverArtUrl(entry, legacyAuth, 150),
starred: entry.starred || undefined,
type: 'music',
index,
uniqueId: nanoid(),
})),
artist: (data.starred2.artist || []).map((entry: any, index: any) => ({
...entry,
albumCount: entry.albumCount || undefined,
coverArt: getCoverArtUrl(entry, legacyAuth),
image: getCoverArtUrl(entry, legacyAuth, 350),
starred: entry.starred || Date.now(), // Airsonic does not return the starred date
type: 'artist',
index,
uniqueId: nanoid(), uniqueId: nanoid(),
})),
}; };
}; };
export const getAlbums = async (options: { const normalizeFolder = (item: any) => {
type:
| 'random'
| 'newest'
| 'highest'
| 'frequent'
| 'recent'
| 'alphabeticalByName'
| 'alphabeticalByArtist'
| 'starred'
| 'byYear'
| 'byGenre';
size?: number;
offset?: number;
fromYear?: number;
toYear?: number;
genre?: string;
musicFolderId?: string | number;
}) => {
const { data } = await api.get(`/getAlbumList2`, {
params: options,
});
return { return {
...data.albumList2, id: item.id,
album: (data.albumList2.album || []).map((entry: any, index: any) => ({ title: item.name || item.title,
...entry, isDir: true,
title: entry.name, image: getCoverArtUrl(item, legacyAuth, 150),
albumId: entry.id, type: Item.Folder,
image: getCoverArtUrl(entry, legacyAuth, 350),
starred: entry.starred || undefined,
type: 'album',
isDir: false,
index,
uniqueId: nanoid(), uniqueId: nanoid(),
})),
}; };
}; };
export const getAlbumsDirect = async (options: { export const getPlaylist = async (id: string) => {
type: const { data } = await api.get(`/getPlaylist?id=${id}`);
| 'random' return normalizePlaylist(data.playlist);
| 'newest' };
| 'highest'
| 'frequent'
| 'recent'
| 'alphabeticalByName'
| 'alphabeticalByArtist'
| 'starred'
| 'byYear'
| 'byGenre';
size?: number;
offset?: number;
fromYear?: number;
toYear?: number;
genre?: string;
musicFolderId?: string | number;
}) => {
const { data } = await api.get(`/getAlbumList2`, {
params: options,
});
const albums = (data.albumList2.album || []).map((entry: any, index: any) => ({ export const getPlaylists = async () => {
...entry, const { data } = await api.get('/getPlaylists');
title: entry.name, return (data.playlists?.playlist || []).map((playlist: any) => normalizePlaylist(playlist));
albumId: entry.id, };
image: getCoverArtUrl(entry, legacyAuth, 350),
starred: entry.starred || undefined,
type: 'album',
isDir: false,
index,
uniqueId: nanoid(),
}));
return albums; export const getStarred = async (options: { musicFolderId?: string | number }) => {
const { data } = await api.get(`/getStarred2`, { params: options });
return {
album: (data.starred2.album || []).map((entry: any) => normalizeAlbum(entry)),
song: (data.starred2.song || []).map((entry: any) => normalizeSong(entry)),
artist: (data.starred2.artist || []).map((entry: any) => normalizeArtist(entry)),
};
}; };
export const getAllAlbums = ( export const getAlbum = async (options: { id: string }) => {
const { data } = await api.get(`/getAlbum`, { params: options });
return normalizeAlbum(data.album);
};
export const getAlbums = async (
options: { options: {
type: type:
| string // Handle generic genres
| 'random' | 'random'
| 'newest' | 'newest'
| 'highest' | 'highest'
@ -435,9 +323,11 @@ export const getAllAlbums = (
toYear?: number; toYear?: number;
genre?: string; genre?: string;
musicFolderId?: string | number; musicFolderId?: string | number;
recursive?: boolean;
}, },
data: any[] = [] recursiveData: any[] = []
) => { ) => {
if (options.recursive) {
const albums: any = api const albums: any = api
.get(`/getAlbumList2`, { .get(`/getAlbumList2`, {
params: { params: {
@ -446,7 +336,9 @@ export const getAllAlbums = (
: 'byGenre', : 'byGenre',
size: 500, size: 500,
offset: options.offset, offset: options.offset,
genre: options.type.match('alphabeticalByName|alphabeticalByArtist|frequent|newest|recent') genre: options.type.match(
'alphabeticalByName|alphabeticalByArtist|frequent|newest|recent'
)
? undefined ? undefined
: options.type, : options.type,
musicFolderId: options.musicFolderId, musicFolderId: options.musicFolderId,
@ -454,60 +346,31 @@ export const getAllAlbums = (
}) })
.then((res) => { .then((res) => {
if (!res.data.albumList2.album || res.data.albumList2.album.length === 0) { if (!res.data.albumList2.album || res.data.albumList2.album.length === 0) {
// Flatten the array and return once there are no more albums left // Flatten and return once there are no more albums left
const flattened = _.flatten(data); const flattenedAlbums = _.flatten(recursiveData);
return flattened.map((entry: any, index: any) => ({ return (flattenedAlbums || []).map((entry: any) => normalizeAlbum(entry));
...entry,
title: entry.name,
albumId: entry.id,
image: getCoverArtUrl(entry, legacyAuth, 350),
starred: entry.starred || undefined,
type: 'album',
isDir: false,
index,
uniqueId: nanoid(),
}));
} }
// On every iteration, push the existing combined album array and increase the offset // On every iteration, push the existing combined album array and increase the offset
data.push(res.data.albumList2.album); recursiveData.push(res.data.albumList2.album);
return getAllAlbums( return getAlbums(
{ {
type: options.type, type: options.type,
size: options.size, size: options.size,
offset: options.offset + options.size, offset: options.offset + options.size,
musicFolderId: options.musicFolderId, musicFolderId: options.musicFolderId,
recursive: true,
}, },
data recursiveData
); );
}) })
.catch((err) => console.log(err)); .catch((err) => console.log(err));
return albums; return albums;
}; }
export const getAlbum = async (id: string) => {
const { data } = await api.get(`/getAlbum`, {
params: {
id,
},
});
return { const { data } = await api.get(`/getAlbumList2`, { params: options });
...data.album, return (data.albumList2.album || []).map((entry: any) => normalizeAlbum(entry));
image: getCoverArtUrl(data.album, legacyAuth, 350),
type: 'album',
isDir: false,
song: (data.album.song || []).map((entry: any, index: any) => ({
...entry,
streamUrl: getStreamUrl(entry.id, legacyAuth),
image: getCoverArtUrl(entry, legacyAuth, 150),
type: 'music',
starred: entry.starred || undefined,
index,
uniqueId: nanoid(),
})),
};
}; };
export const getRandomSongs = async (options: { export const getRandomSongs = async (options: {
@ -517,103 +380,47 @@ export const getRandomSongs = async (options: {
toYear?: number; toYear?: number;
musicFolderId?: number; musicFolderId?: number;
}) => { }) => {
const { data } = await api.get(`/getRandomSongs`, { const { data } = await api.get(`/getRandomSongs`, { params: options });
params: options, return (data.randomSongs.song || []).map((entry: any) => normalizeSong(entry));
});
return {
...data.randomSongs,
song: (data.randomSongs.song || []).map((entry: any, index: any) => ({
...entry,
streamUrl: getStreamUrl(entry.id, legacyAuth),
image: getCoverArtUrl(entry, legacyAuth, 150),
starred: entry.starred || undefined,
index,
uniqueId: nanoid(),
})),
}; };
export const getArtist = async (options: { id: string }) => {
const { data } = await api.get(`/getArtist`, { params: options });
return normalizeArtist(data.artist);
}; };
export const getArtists = async (options: { musicFolderId?: string | number }) => { export const getArtists = async (options: { musicFolderId?: string | number }) => {
const { data } = await api.get(`/getArtists`, { const { data } = await api.get(`/getArtists`, { params: options });
params: options,
});
const artistList: any[] = [];
const artists = (data.artists?.index || []).flatMap((index: any) => index.artist); const artists = (data.artists?.index || []).flatMap((index: any) => index.artist);
return (artists || []).map((entry: any) => normalizeArtist(entry));
artists.map((artist: any) =>
artistList.push({
...artist,
image: getCoverArtUrl(artist, legacyAuth, 350),
type: 'artist',
uniqueId: nanoid(),
})
);
return artistList;
}; };
export const getArtist = async (id: string) => { export const getArtistInfo = async (options: { id: string; count: number }) => {
const { data } = await api.get(`/getArtist`, { const { data } = await api.get(`/getArtistInfo2`, { params: options });
params: { return normalizeArtistInfo(data.artistInfo2);
id,
},
});
return {
...data.artist,
image: getCoverArtUrl(data.artist, legacyAuth, 350),
type: 'artist',
album: (data.artist.album || []).map((entry: any, index: any) => ({
...entry,
albumId: entry.id,
type: 'album',
isDir: false,
image: getCoverArtUrl(entry, legacyAuth, 350),
starred: entry.starred || undefined,
index,
uniqueId: nanoid(),
})),
};
};
export const getArtistInfo = async (id: string, count = 10) => {
const { data } = await api.get(`/getArtistInfo2`, {
params: {
id,
count,
},
});
return {
...data.artistInfo2,
};
}; };
export const getAllArtistSongs = async (id: string) => { export const getArtistSongs = async (options: { id: string }) => {
const promises = []; const promises = [];
const artist = await getArtist(id); const artist = await getArtist({ id: options.id });
for (let i = 0; i < artist.album.length; i += 1) { for (let i = 0; i < artist.album.length; i += 1) {
promises.push(getAlbum(artist.album[i].id)); promises.push(getAlbum({ id: artist.album[i].id }));
} }
const res = await Promise.all(promises); const res = await Promise.all(promises);
return _.flatten(_.map(res, 'song')); return (_.flatten(_.map(res, 'song')) || []).map((entry: any) => normalizeSong(entry));
}; };
export const startScan = async () => { export const startScan = async () => {
const { data } = await api.get(`/startScan`); const { data } = await api.get(`/startScan`);
const scanStatus = data?.scanStatus; const scanStatus = data?.scanStatus;
return scanStatus; return scanStatus;
}; };
export const getScanStatus = async () => { export const getScanStatus = async () => {
const { data } = await api.get(`/getScanStatus`); const { data } = await api.get(`/getScanStatus`);
const scanStatus = data?.scanStatus; const scanStatus = data?.scanStatus;
return scanStatus; return scanStatus;
}; };
@ -668,13 +475,7 @@ export const batchStar = async (ids: string[], type: string) => {
params.append(key, value); params.append(key, value);
}); });
res.push( res.push((await api.get(`/star`, { params })).data);
(
await api.get(`/star`, {
params,
})
).data
);
} }
return res; return res;
@ -707,42 +508,20 @@ export const batchUnstar = async (ids: string[], type: string) => {
params.append(key, value); params.append(key, value);
}); });
res.push( res.push((await api.get(`/unstar`, { params })).data);
(
await api.get(`/unstar`, {
params,
})
).data
);
} }
return res; return res;
}; };
export const setRating = async (id: string, rating: number) => { export const setRating = async (id: string, rating: number) => {
const { data } = await api.get(`/setRating`, { const { data } = await api.get(`/setRating`, { params: { id, rating } });
params: {
id,
rating,
},
});
return data; return data;
}; };
export const getSimilarSongs = async (id: string, count: number) => { export const getSimilarSongs = async (id: string, count: number) => {
const { data } = await api.get(`/getSimilarSongs2`, { const { data } = await api.get(`/getSimilarSongs2`, { params: { id, count } });
params: { id, count }, return (data.similarSongs2.song || []).map((entry: any) => normalizeSong(entry));
});
return {
song: (data.similarSongs2.song || []).map((entry: any, index: any) => ({
...entry,
image: getCoverArtUrl(entry, legacyAuth, 150),
index,
uniqueId: nanoid(),
})),
};
}; };
export const updatePlaylistSongs = async (id: string, entry: any[]) => { export const updatePlaylistSongs = async (id: string, entry: any[]) => {
@ -793,22 +572,12 @@ export const updatePlaylistSongsLg = async (playlistId: string, entry: any[]) =>
}; };
export const deletePlaylist = async (id: string) => { export const deletePlaylist = async (id: string) => {
const { data } = await api.get(`/deletePlaylist`, { const { data } = await api.get(`/deletePlaylist`, { params: { id } });
params: {
id,
},
});
return data; return data;
}; };
export const createPlaylist = async (name: string) => { export const createPlaylist = async (name: string) => {
const { data } = await api.get(`/createPlaylist`, { const { data } = await api.get(`/createPlaylist`, { params: { name } });
params: {
name,
},
});
return data; return data;
}; };
@ -832,71 +601,13 @@ export const updatePlaylist = async (
export const clearPlaylist = async (playlistId: string) => { export const clearPlaylist = async (playlistId: string) => {
// Specifying the playlistId without any songs will empty the existing playlist // Specifying the playlistId without any songs will empty the existing playlist
const { data } = await api.get(`/createPlaylist`, { const { data } = await api.get(`/createPlaylist`, { params: { playlistId, songId: '' } });
params: {
playlistId,
songId: '',
},
});
return data; return data;
}; };
export const getGenres = async () => { export const getGenres = async () => {
const { data } = await api.get(`/getGenres`); const { data } = await api.get(`/getGenres`);
return (data.genres.genre || []).map((entry: any) => normalizeGenre(entry));
return (data.genres.genre || []).map((entry: any, index: any) => ({
id: entry.value, // List view uses id to match the playing song so we need an arbitrary id here
...entry,
name: entry.value,
index,
uniqueId: nanoid(),
}));
};
export const search2 = async (options: {
query: string;
artistCount?: number;
artistOffset?: 0;
albumCount?: number;
albumOffset?: 0;
songCount?: number;
songOffset?: 0;
musicFolderId?: string | number;
}) => {
const { data } = await api.get(`/search2`, { params: options });
const results = data.searchResult3;
return {
artist: (results.artist || []).map((entry: any, index: any) => ({
...entry,
image: getCoverArtUrl(entry, legacyAuth, 350),
starred: entry.starred || undefined,
type: 'artist',
index,
uniqueId: nanoid(),
})),
album: (results.album || []).map((entry: any, index: any) => ({
...entry,
albumId: entry.id,
image: getCoverArtUrl(entry, legacyAuth, 350),
starred: entry.starred || undefined,
type: 'album',
isDir: false,
index,
uniqueId: nanoid(),
})),
song: (results.song || []).map((entry: any, index: any) => ({
...entry,
streamUrl: getStreamUrl(entry.id, legacyAuth),
image: getCoverArtUrl(entry, legacyAuth, 150),
type: 'music',
starred: entry.starred || undefined,
index,
uniqueId: nanoid(),
})),
};
}; };
export const search3 = async (options: { export const search3 = async (options: {
@ -911,44 +622,15 @@ export const search3 = async (options: {
}) => { }) => {
const { data } = await api.get(`/search3`, { params: options }); const { data } = await api.get(`/search3`, { params: options });
const results = data.searchResult3;
return { return {
artist: (results.artist || []).map((entry: any, index: any) => ({ artist: (data.searchResult3.artist || []).map((entry: any) => normalizeArtist(entry)),
...entry, album: (data.searchResult3.album || []).map((entry: any) => normalizeAlbum(entry)),
image: getCoverArtUrl(entry, legacyAuth, 350), song: (data.searchResult3.song || []).map((entry: any) => normalizeSong(entry)),
starred: entry.starred || undefined,
type: 'artist',
index,
uniqueId: nanoid(),
})),
album: (results.album || []).map((entry: any, index: any) => ({
...entry,
albumId: entry.id,
image: getCoverArtUrl(entry, legacyAuth, 350),
starred: entry.starred || undefined,
type: 'album',
isDir: false,
index,
uniqueId: nanoid(),
})),
song: (results.song || []).map((entry: any, index: any) => ({
...entry,
streamUrl: getStreamUrl(entry.id, legacyAuth),
image: getCoverArtUrl(entry, legacyAuth, 150),
type: 'music',
starred: entry.starred || undefined,
index,
uniqueId: nanoid(),
})),
}; };
}; };
export const scrobble = async (options: { id: string; time?: number; submission?: boolean }) => { export const scrobble = async (options: { id: string; time?: number; submission?: boolean }) => {
const { data } = await api.get(`/scrobble`, { const { data } = await api.get(`/scrobble`, { params: options });
params: options,
});
return data; return data;
}; };
@ -956,97 +638,49 @@ export const getIndexes = async (options: {
musicFolderId?: string | number; musicFolderId?: string | number;
ifModifiedSince?: any; ifModifiedSince?: any;
}) => { }) => {
const { data } = await api.get(`/getIndexes`, { const { data } = await api.get(`/getIndexes`, { params: options });
params: options,
});
let folders: any[] = []; const folders: any[] = [];
data.indexes.index.forEach((entry: any) => { data.indexes.index.forEach((entry: any) => {
entry.artist.forEach((folder: any) => { entry.artist.forEach((folder: any) => {
folders.push({ folders.push(normalizeFolder(folder));
...folder,
title: folder.name,
isDir: true,
image: getCoverArtUrl(folder, legacyAuth, 150),
uniqueId: nanoid(),
type: 'folder',
});
}); });
}); });
folders = _.flatten(folders); const child = (data.indexes?.child || []).map((entry: any) => normalizeSong(entry));
return _.concat(_.flatten(folders), child);
const child: any[] = [];
(data.indexes?.child || []).forEach((song: any, index: any) =>
child.push({
...song,
index,
type: 'music',
streamUrl: getStreamUrl(song.id, legacyAuth),
image: getCoverArtUrl(song, legacyAuth, 150),
uniqueId: nanoid(),
})
);
return _.concat(folders, child);
}; };
export const getMusicFolders = async () => { export const getMusicFolders = async () => {
const { data } = await api.get(`/getMusicFolders`); const { data } = await api.get(`/getMusicFolders`);
return (data?.musicFolders?.musicFolder || []).map((entry: any) => normalizeFolder(entry));
return data?.musicFolders?.musicFolder;
}; };
export const getMusicDirectory = async (options: { id: string }) => { export const getMusicDirectory = async (options: { id: string }) => {
const { data } = await api.get(`/getMusicDirectory`, { const { data } = await api.get(`/getMusicDirectory`, { params: options });
params: options,
});
const child: any[] = []; const child: any[] = [];
const folders = data.directory?.child?.filter((entry: any) => entry.isDir); const folders = data.directory?.child?.filter((entry: any) => entry.isDir);
const songs = data.directory?.child?.filter((entry: any) => entry.isDir === false); const songs = data.directory?.child?.filter((entry: any) => entry.isDir === false);
(folders || []).forEach((folder: any) => (folders || []).forEach((folder: any) => child.push(normalizeFolder(folder)));
child.push({ (songs || []).forEach((entry: any) => child.push(normalizeSong(entry)));
...folder,
image: getCoverArtUrl(folder, legacyAuth, 150),
uniqueId: nanoid(),
type: 'folder',
})
);
(songs || []).forEach((song: any, index: any) =>
child.push({
...song,
index,
type: 'music',
streamUrl: getStreamUrl(song.id, legacyAuth),
image: getCoverArtUrl(song, legacyAuth, 150),
uniqueId: nanoid(),
})
);
return { return {
...data.directory, ...data.directory,
title: data.directory?.name,
child, child,
}; };
}; };
export const getAllDirectorySongs = async (options: { id: string }, data: any[] = []) => { export const getDirectorySongs = async (options: { id: string }, data: any[] = []) => {
if (options.id === 'stop') { if (options.id === 'stop') {
const songs: any[] = []; const songs: any[] = [];
(data || []).forEach((song: any, index: any) => { (data || []).forEach((song: any) => {
(song?.child || []).forEach((entry: any) => { (song?.child || []).forEach((entry: any) => {
if (entry.isDir === false) { if (entry.isDir === false) {
songs.push({ songs.push(normalizeSong(entry));
...entry,
index,
type: 'music',
streamUrl: getStreamUrl(entry.id, legacyAuth),
image: getCoverArtUrl(entry, legacyAuth, 150),
uniqueId: nanoid(),
});
} }
}); });
}); });
@ -1059,17 +693,17 @@ export const getAllDirectorySongs = async (options: { id: string }, data: any[]
if (res.child.filter((entry: any) => entry.isDir === true).length === 0) { if (res.child.filter((entry: any) => entry.isDir === true).length === 0) {
// Add the last directory if there are no other directories // Add the last directory if there are no other directories
data.push(res); data.push(res);
return getAllDirectorySongs({ id: 'stop' }, data); return getDirectorySongs({ id: 'stop' }, data);
} }
data.push(res); data.push(res);
const nestedFolders = res.child.filter((entry: any) => entry.isDir === true); const nestedFolders = res.child.filter((entry: any) => entry.isDir === true);
for (let i = 0; i < nestedFolders.length; i += 1) { for (let i = 0; i < nestedFolders.length; i += 1) {
await getAllDirectorySongs({ id: nestedFolders[i].id }, data); await getDirectorySongs({ id: nestedFolders[i].id }, data);
} }
return getAllDirectorySongs({ id: 'stop' }, data); return getDirectorySongs({ id: 'stop' }, data);
}) })
.catch((err) => console.log(err)); .catch((err) => console.log(err));

108
src/api/types.ts

@ -3,6 +3,112 @@ export enum Server {
Jellyfin = 'jellyfin', Jellyfin = 'jellyfin',
} }
export type ServerType = Server.Subsonic | Server.Jellyfin; export enum Item {
Album = 'album',
Artist = 'artist',
Folder = 'folder',
Genre = 'genre',
Music = 'music',
Playlist = 'playlist',
}
export type ServerType = Server.Subsonic | Server.Jellyfin;
export type APIEndpoints = 'getPlaylist' | 'getPlaylists'; export type APIEndpoints = 'getPlaylist' | 'getPlaylists';
export interface Album {
id: string;
title: string;
isDir?: boolean;
albumId: string;
artist?: string;
artistId?: string;
songCount: number;
duration: number;
created: string;
year?: number;
genre?: string;
image: string;
starred?: string;
type: Item.Album;
uniqueId: string;
song?: Song[];
}
export interface Artist {
id: string;
title: string;
albumCount: number;
image: string;
starred?: string;
type: Item.Artist;
uniqueId: string;
album: Album[];
}
export interface ArtistInfo {
biography?: string;
lastFmUrl?: string;
imageUrl?: string;
similarArtist?: Artist[];
}
export interface Folder {
id: string;
title: string;
isDir?: boolean;
image: string;
type: Item.Folder;
uniqueId: string;
}
export interface Genre {
id: string;
title: string;
songCount?: number;
albumCount?: number;
type: Item.Genre;
uniqueId: string;
}
export interface Playlist {
id: string;
title: string;
comment?: string;
owner: string;
public?: boolean;
songCount: number;
duration: number;
created: string;
changed: string;
image: string;
type: Item.Playlist;
uniqueId: string;
song?: Song[];
}
export interface Song {
id: string;
parent?: string;
title: string;
isDir?: boolean;
album: string;
albumId?: string;
artist: string;
artistId?: string;
track?: number;
year?: number;
genre?: string;
size: number;
contentType?: string;
suffix?: string;
duration?: number;
bitRate?: number;
path?: string;
playCount?: number;
discNumber?: number;
created: string;
streamUrl: string;
image: string;
starred?: string;
type: Item.Music;
uniqueId: string;
}

10
src/components/card/Card.tsx

@ -2,7 +2,7 @@ import React from 'react';
import { Icon } from 'rsuite'; import { Icon } from 'rsuite';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
import cacheImage from '../shared/cacheImage'; import cacheImage from '../shared/cacheImage';
import { getAlbum, getPlaylist, getAllArtistSongs } from '../../api/api'; import { getAlbum, getPlaylist, getArtistSongs } from '../../api/api';
import { useAppDispatch, useAppSelector } from '../../redux/hooks'; import { useAppDispatch, useAppSelector } from '../../redux/hooks';
import { import {
appendPlayQueue, appendPlayQueue,
@ -76,7 +76,7 @@ const Card = ({
} }
if (playClick.type === 'album') { if (playClick.type === 'album') {
const res = await getAlbum(playClick.id); const res = await getAlbum({ id: playClick.id });
const songs = filterPlayQueue(config.playback.filters, res.song); const songs = filterPlayQueue(config.playback.filters, res.song);
if (songs.entries.length > 0) { if (songs.entries.length > 0) {
@ -92,7 +92,7 @@ const Card = ({
} }
if (playClick.type === 'artist') { if (playClick.type === 'artist') {
const res = await getAllArtistSongs(playClick.id); const res = await getArtistSongs({ id: playClick.id });
const songs = filterPlayQueue(config.playback.filters, res); const songs = filterPlayQueue(config.playback.filters, res);
if (songs.entries.length > 0) { if (songs.entries.length > 0) {
@ -122,7 +122,7 @@ const Card = ({
} }
if (playClick.type === 'album') { if (playClick.type === 'album') {
const res = await getAlbum(playClick.id); const res = await getAlbum({ id: playClick.id });
const songs = filterPlayQueue(config.playback.filters, res.song); const songs = filterPlayQueue(config.playback.filters, res.song);
if (songs.entries.length > 0) { if (songs.entries.length > 0) {
@ -134,7 +134,7 @@ const Card = ({
} }
if (playClick.type === 'artist') { if (playClick.type === 'artist') {
const res = await getAllArtistSongs(playClick.id); const res = await getArtistSongs({ id: playClick.id });
const songs = filterPlayQueue(config.playback.filters, res); const songs = filterPlayQueue(config.playback.filters, res);
if (songs.entries.length > 0) { if (songs.entries.length > 0) {

56
src/components/dashboard/Dashboard.tsx

@ -28,22 +28,22 @@ const Dashboard = () => {
const { isLoading: isLoadingRecent, data: recentAlbums }: any = useQuery( const { isLoading: isLoadingRecent, data: recentAlbums }: any = useQuery(
['recentAlbums', musicFolder], ['recentAlbums', musicFolder],
() => getAlbums({ type: 'recent', size: 20, musicFolderId: musicFolder }) () => getAlbums({ type: 'recent', size: 20, offset: 0, musicFolderId: musicFolder })
); );
const { isLoading: isLoadingNewest, data: newestAlbums }: any = useQuery( const { isLoading: isLoadingNewest, data: newestAlbums }: any = useQuery(
['newestAlbums', musicFolder], ['newestAlbums', musicFolder],
() => getAlbums({ type: 'newest', size: 20, musicFolderId: musicFolder }) () => getAlbums({ type: 'newest', size: 20, offset: 0, musicFolderId: musicFolder })
); );
const { isLoading: isLoadingRandom, data: randomAlbums }: any = useQuery( const { isLoading: isLoadingRandom, data: randomAlbums }: any = useQuery(
['randomAlbums', musicFolder], ['randomAlbums', musicFolder],
() => getAlbums({ type: 'random', size: 20, musicFolderId: musicFolder }) () => getAlbums({ type: 'random', size: 20, offset: 0, musicFolderId: musicFolder })
); );
const { isLoading: isLoadingFrequent, data: frequentAlbums }: any = useQuery( const { isLoading: isLoadingFrequent, data: frequentAlbums }: any = useQuery(
['frequentAlbums', musicFolder], ['frequentAlbums', musicFolder],
() => getAlbums({ type: 'frequent', size: 20, musicFolderId: musicFolder }) () => getAlbums({ type: 'frequent', size: 20, offset: 0, musicFolderId: musicFolder })
); );
const handleFavorite = async (rowData: any) => { const handleFavorite = async (rowData: any) => {
@ -51,33 +51,33 @@ const Dashboard = () => {
await star(rowData.id, 'album'); await star(rowData.id, 'album');
dispatch(setStar({ id: [rowData.id], type: 'star' })); dispatch(setStar({ id: [rowData.id], type: 'star' }));
queryClient.setQueryData(['recentAlbums', musicFolder], (oldData: any) => { queryClient.setQueryData(['recentAlbums', musicFolder], (oldData: any) => {
const starredIndices = _.keys(_.pickBy(oldData?.album, { id: rowData.id })); const starredIndices = _.keys(_.pickBy(oldData, { id: rowData.id }));
starredIndices.forEach((index) => { starredIndices.forEach((index) => {
oldData.album[index].starred = Date.now(); oldData[index].starred = Date.now();
}); });
return oldData; return oldData;
}); });
queryClient.setQueryData(['newestAlbums', musicFolder], (oldData: any) => { queryClient.setQueryData(['newestAlbums', musicFolder], (oldData: any) => {
const starredIndices = _.keys(_.pickBy(oldData?.album, { id: rowData.id })); const starredIndices = _.keys(_.pickBy(oldData, { id: rowData.id }));
starredIndices.forEach((index) => { starredIndices.forEach((index) => {
oldData.album[index].starred = Date.now(); oldData[index].starred = Date.now();
}); });
return oldData; return oldData;
}); });
queryClient.setQueryData(['randomAlbums', musicFolder], (oldData: any) => { queryClient.setQueryData(['randomAlbums', musicFolder], (oldData: any) => {
const starredIndices = _.keys(_.pickBy(oldData?.album, { id: rowData.id })); const starredIndices = _.keys(_.pickBy(oldData, { id: rowData.id }));
starredIndices.forEach((index) => { starredIndices.forEach((index) => {
oldData.album[index].starred = Date.now(); oldData[index].starred = Date.now();
}); });
return oldData; return oldData;
}); });
queryClient.setQueryData(['frequentAlbums', musicFolder], (oldData: any) => { queryClient.setQueryData(['frequentAlbums', musicFolder], (oldData: any) => {
const starredIndices = _.keys(_.pickBy(oldData?.album, { id: rowData.id })); const starredIndices = _.keys(_.pickBy(oldData, { id: rowData.id }));
starredIndices.forEach((index) => { starredIndices.forEach((index) => {
oldData.album[index].starred = Date.now(); oldData[index].starred = Date.now();
}); });
return oldData; return oldData;
@ -86,33 +86,33 @@ const Dashboard = () => {
await unstar(rowData.id, 'album'); await unstar(rowData.id, 'album');
dispatch(setStar({ id: [rowData.id], type: 'unstar' })); dispatch(setStar({ id: [rowData.id], type: 'unstar' }));
queryClient.setQueryData(['recentAlbums', musicFolder], (oldData: any) => { queryClient.setQueryData(['recentAlbums', musicFolder], (oldData: any) => {
const starredIndices = _.keys(_.pickBy(oldData?.album, { id: rowData.id })); const starredIndices = _.keys(_.pickBy(oldData, { id: rowData.id }));
starredIndices.forEach((index) => { starredIndices.forEach((index) => {
oldData.album[index].starred = undefined; oldData[index].starred = undefined;
}); });
return oldData; return oldData;
}); });
queryClient.setQueryData(['newestAlbums', musicFolder], (oldData: any) => { queryClient.setQueryData(['newestAlbums', musicFolder], (oldData: any) => {
const starredIndices = _.keys(_.pickBy(oldData?.album, { id: rowData.id })); const starredIndices = _.keys(_.pickBy(oldData, { id: rowData.id }));
starredIndices.forEach((index) => { starredIndices.forEach((index) => {
oldData.album[index].starred = undefined; oldData[index].starred = undefined;
}); });
return oldData; return oldData;
}); });
queryClient.setQueryData(['randomAlbums', musicFolder], (oldData: any) => { queryClient.setQueryData(['randomAlbums', musicFolder], (oldData: any) => {
const starredIndices = _.keys(_.pickBy(oldData?.album, { id: rowData.id })); const starredIndices = _.keys(_.pickBy(oldData, { id: rowData.id }));
starredIndices.forEach((index) => { starredIndices.forEach((index) => {
oldData.album[index].starred = undefined; oldData[index].starred = undefined;
}); });
return oldData; return oldData;
}); });
queryClient.setQueryData(['frequentAlbums', musicFolder], (oldData: any) => { queryClient.setQueryData(['frequentAlbums', musicFolder], (oldData: any) => {
const starredIndices = _.keys(_.pickBy(oldData?.album, { id: rowData.id })); const starredIndices = _.keys(_.pickBy(oldData, { id: rowData.id }));
starredIndices.forEach((index) => { starredIndices.forEach((index) => {
oldData.album[index].starred = undefined; oldData[index].starred = undefined;
}); });
return oldData; return oldData;
@ -134,10 +134,10 @@ const Dashboard = () => {
<> <>
<ScrollingMenu <ScrollingMenu
title="Recently Played" title="Recently Played"
data={recentAlbums.album} data={recentAlbums}
cardTitle={{ cardTitle={{
prefix: '/library/album', prefix: '/library/album',
property: 'name', property: 'title',
urlProperty: 'albumId', urlProperty: 'albumId',
}} }}
cardSubtitle={{ cardSubtitle={{
@ -158,10 +158,10 @@ const Dashboard = () => {
<ScrollingMenu <ScrollingMenu
title="Recently Added" title="Recently Added"
data={newestAlbums.album} data={newestAlbums}
cardTitle={{ cardTitle={{
prefix: '/library/album', prefix: '/library/album',
property: 'name', property: 'title',
urlProperty: 'albumId', urlProperty: 'albumId',
}} }}
cardSubtitle={{ cardSubtitle={{
@ -182,10 +182,10 @@ const Dashboard = () => {
<ScrollingMenu <ScrollingMenu
title="Random" title="Random"
data={randomAlbums.album} data={randomAlbums}
cardTitle={{ cardTitle={{
prefix: '/library/album', prefix: '/library/album',
property: 'name', property: 'title',
urlProperty: 'albumId', urlProperty: 'albumId',
}} }}
cardSubtitle={{ cardSubtitle={{
@ -206,10 +206,10 @@ const Dashboard = () => {
<ScrollingMenu <ScrollingMenu
title="Most Played" title="Most Played"
data={frequentAlbums.album} data={frequentAlbums}
cardTitle={{ cardTitle={{
prefix: '/library/album', prefix: '/library/album',
property: 'name', property: 'title',
urlProperty: 'albumId', urlProperty: 'albumId',
}} }}
cardSubtitle={{ cardSubtitle={{

16
src/components/library/AlbumList.tsx

@ -9,7 +9,7 @@ import ListViewType from '../viewtypes/ListViewType';
import useSearchQuery from '../../hooks/useSearchQuery'; import useSearchQuery from '../../hooks/useSearchQuery';
import GenericPageHeader from '../layout/GenericPageHeader'; import GenericPageHeader from '../layout/GenericPageHeader';
import GenericPage from '../layout/GenericPage'; import GenericPage from '../layout/GenericPage';
import { getAlbumsDirect, getAllAlbums, getGenres, star, unstar } from '../../api/api'; import { getAlbums, getGenres, star, unstar } from '../../api/api';
import PageLoader from '../loader/PageLoader'; import PageLoader from '../loader/PageLoader';
import { useAppDispatch, useAppSelector } from '../../redux/hooks'; import { useAppDispatch, useAppSelector } from '../../redux/hooks';
import { import {
@ -56,16 +56,18 @@ const AlbumList = () => {
['albumList', album.active.filter, musicFolder], ['albumList', album.active.filter, musicFolder],
() => () =>
album.active.filter === 'random' album.active.filter === 'random'
? getAlbumsDirect({ ? getAlbums({
type: 'random', type: 'random',
size: config.lookAndFeel.gridView.cardSize, size: config.lookAndFeel.gridView.cardSize,
offset: 0,
musicFolderId: musicFolder, musicFolderId: musicFolder,
}) })
: getAllAlbums({ : getAlbums({
type: album.active.filter, type: album.active.filter,
size: 500, size: 500,
offset: 0, offset: 0,
musicFolderId: musicFolder, musicFolderId: musicFolder,
recursive: true,
}), }),
{ {
cacheTime: 3600000, // Stay in cache for 1 hour cacheTime: 3600000, // Stay in cache for 1 hour
@ -77,8 +79,8 @@ const AlbumList = () => {
return res.map((genre: any) => { return res.map((genre: any) => {
if (genre.albumCount !== 0) { if (genre.albumCount !== 0) {
return { return {
label: `${genre.value} (${genre.albumCount})`, label: `${genre.title} (${genre.albumCount})`,
value: genre.value, value: genre.title,
role: 'Genre', role: 'Genre',
}; };
} }
@ -86,7 +88,7 @@ const AlbumList = () => {
}); });
}); });
const filteredData = useSearchQuery(misc.searchQuery, albums, [ const filteredData = useSearchQuery(misc.searchQuery, albums, [
'name', 'title',
'artist', 'artist',
'genre', 'genre',
'year', 'year',
@ -228,7 +230,7 @@ const AlbumList = () => {
data={misc.searchQuery !== '' ? filteredData : albums} data={misc.searchQuery !== '' ? filteredData : albums}
cardTitle={{ cardTitle={{
prefix: '/library/album', prefix: '/library/album',
property: 'name', property: 'title',
urlProperty: 'albumId', urlProperty: 'albumId',
}} }}
cardSubtitle={{ cardSubtitle={{

4
src/components/library/AlbumView.tsx

@ -68,7 +68,7 @@ const AlbumView = ({ ...rest }: any) => {
const albumId = rest.id ? rest.id : id; const albumId = rest.id ? rest.id : id;
const { isLoading, isError, data, error }: any = useQuery(['album', albumId], () => const { isLoading, isError, data, error }: any = useQuery(['album', albumId], () =>
getAlbum(albumId) getAlbum({ id: albumId })
); );
const filteredData = useSearchQuery(misc.searchQuery, data?.song, [ const filteredData = useSearchQuery(misc.searchQuery, data?.song, [
'title', 'title',
@ -240,7 +240,7 @@ const AlbumView = ({ ...rest }: any) => {
id: data.albumId, id: data.albumId,
}} }}
imageHeight={200} imageHeight={200}
title={data.name} title={data.title}
showTitleTooltip showTitleTooltip
subtitle={ subtitle={
<div> <div>

4
src/components/library/ArtistList.tsx

@ -45,7 +45,7 @@ const ArtistList = () => {
staleTime: Infinity, // Only allow manual refresh staleTime: Infinity, // Only allow manual refresh
} }
); );
const filteredData = useSearchQuery(misc.searchQuery, artists, ['name']); const filteredData = useSearchQuery(misc.searchQuery, artists, ['title']);
let timeout: any = null; let timeout: any = null;
const handleRowClick = (e: any, rowData: any, tableData: any) => { const handleRowClick = (e: any, rowData: any, tableData: any) => {
@ -151,7 +151,7 @@ const ArtistList = () => {
data={misc.searchQuery !== '' ? filteredData : artists} data={misc.searchQuery !== '' ? filteredData : artists}
cardTitle={{ cardTitle={{
prefix: '/library/artist', prefix: '/library/artist',
property: 'name', property: 'title',
urlProperty: 'id', urlProperty: 'id',
}} }}
cardSubtitle={{ cardSubtitle={{

22
src/components/library/ArtistView.tsx

@ -16,7 +16,7 @@ import {
} from '../shared/ToolbarButtons'; } from '../shared/ToolbarButtons';
import { import {
getAlbum, getAlbum,
getAllArtistSongs, getArtistSongs,
getArtist, getArtist,
getArtistInfo, getArtistInfo,
getDownloadUrl, getDownloadUrl,
@ -75,17 +75,17 @@ const ArtistView = ({ ...rest }: any) => {
const { id } = useParams<ArtistParams>(); const { id } = useParams<ArtistParams>();
const artistId = rest.id ? rest.id : id; const artistId = rest.id ? rest.id : id;
const { isLoading, isError, data, error }: any = useQuery(['artist', artistId], () => const { isLoading, isError, data, error }: any = useQuery(['artist', artistId], () =>
getArtist(artistId) getArtist({ id: artistId })
); );
const { const {
isLoading: isLoadingAI, isLoading: isLoadingAI,
isError: isErrorAI, isError: isErrorAI,
data: artistInfo, data: artistInfo,
error: errorAI, error: errorAI,
}: any = useQuery(['artistInfo', artistId], () => getArtistInfo(artistId, 8)); }: any = useQuery(['artistInfo', artistId], () => getArtistInfo({ id: artistId, count: 8 }));
const filteredData = useSearchQuery(misc.searchQuery, data?.album, [ const filteredData = useSearchQuery(misc.searchQuery, data?.album, [
'name', 'title',
'artist', 'artist',
'genre', 'genre',
'year', 'year',
@ -125,7 +125,7 @@ const ArtistView = ({ ...rest }: any) => {
}; };
const handlePlay = async () => { const handlePlay = async () => {
const res = await getAllArtistSongs(data.id); const res = await getArtistSongs({ id: data.id });
const songs = filterPlayQueue(config.playback.filters, res); const songs = filterPlayQueue(config.playback.filters, res);
if (songs.entries.length > 0) { if (songs.entries.length > 0) {
@ -141,7 +141,7 @@ const ArtistView = ({ ...rest }: any) => {
}; };
const handlePlayAppend = async (type: 'next' | 'later') => { const handlePlayAppend = async (type: 'next' | 'later') => {
const res = await getAllArtistSongs(data.id); const res = await getArtistSongs({ id: data.id });
const songs = filterPlayQueue(config.playback.filters, res); const songs = filterPlayQueue(config.playback.filters, res);
if (songs.entries.length > 0) { if (songs.entries.length > 0) {
@ -189,11 +189,11 @@ const ArtistView = ({ ...rest }: any) => {
for (let i = 0; i < data.album.length; i += 1) { for (let i = 0; i < data.album.length; i += 1) {
// eslint-disable-next-line no-await-in-loop // eslint-disable-next-line no-await-in-loop
const albumRes = await getAlbum(data.album[i].id); const albumRes = await getAlbum({ id: data.album[i].id });
if (albumRes.song[0]?.parent) { if (albumRes.song[0]?.parent) {
downloadUrls.push(getDownloadUrl(albumRes.song[0].parent)); downloadUrls.push(getDownloadUrl(albumRes.song[0].parent));
} else { } else {
notifyToast('warning', `[${albumRes.name}] No parent album found`); notifyToast('warning', `[${albumRes.title}] No parent album found`);
} }
} }
@ -295,7 +295,7 @@ const ArtistView = ({ ...rest }: any) => {
id: data.id, id: data.id,
}} }}
imageHeight={185} imageHeight={185}
title={data.name} title={data.title}
showTitleTooltip showTitleTooltip
subtitle={ subtitle={
<> <>
@ -393,7 +393,7 @@ const ArtistView = ({ ...rest }: any) => {
} }
}} }}
> >
{artist.name} {artist.title}
</StyledTag> </StyledTag>
))} ))}
</TagGroup> </TagGroup>
@ -455,7 +455,7 @@ const ArtistView = ({ ...rest }: any) => {
data={misc.searchQuery !== '' ? filteredData : data.album} data={misc.searchQuery !== '' ? filteredData : data.album}
cardTitle={{ cardTitle={{
prefix: '/library/album', prefix: '/library/album',
property: 'name', property: 'title',
urlProperty: 'albumId', urlProperty: 'albumId',
}} }}
cardSubtitle={{ cardSubtitle={{

10
src/components/library/FolderList.tsx

@ -61,7 +61,7 @@ const FolderList = () => {
const filteredData = useSearchQuery( const filteredData = useSearchQuery(
misc.searchQuery, misc.searchQuery,
folderData?.id ? folderData?.child : indexData, folderData?.id ? folderData?.child : indexData,
['name', 'title', 'artist', 'album', 'year', 'genre', 'path'] ['title', 'artist', 'album', 'year', 'genre', 'path']
); );
useEffect(() => { useEffect(() => {
@ -98,7 +98,7 @@ const FolderList = () => {
const selected = folderData?.id ? folderData?.child : indexData?.child; const selected = folderData?.id ? folderData?.child : indexData?.child;
dispatch( dispatch(
setPlayQueueByRowClick({ setPlayQueueByRowClick({
entries: selected.filter((entry: any) => entry.isDir === false), entries: selected.filter((entry: any) => entry?.isDir === false),
currentIndex: rowData.index, currentIndex: rowData.index,
currentSongId: rowData.id, currentSongId: rowData.id,
uniqueSongId: rowData.uniqueId, uniqueSongId: rowData.uniqueId,
@ -149,8 +149,8 @@ const FolderList = () => {
header={ header={
<GenericPageHeader <GenericPageHeader
title={`${ title={`${
folderData?.name folderData?.title
? folderData.name ? folderData.title
: isLoadingFolderData : isLoadingFolderData
? 'Loading...' ? 'Loading...'
: 'Select a folder' : 'Select a folder'
@ -167,7 +167,7 @@ const FolderList = () => {
data={musicFolders} data={musicFolders}
defaultValue={musicFolder} defaultValue={musicFolder}
valueKey="id" valueKey="id"
labelKey="name" labelKey="title"
onChange={(e: any) => { onChange={(e: any) => {
setMusicFolder(e); setMusicFolder(e);
}} }}

6
src/components/library/GenreList.tsx

@ -27,7 +27,7 @@ const GenreList = () => {
const res = await getGenres(); const res = await getGenres();
return _.orderBy(res, 'songCount', 'desc'); return _.orderBy(res, 'songCount', 'desc');
}); });
const filteredData = useSearchQuery(misc.searchQuery, genres, ['value']); const filteredData = useSearchQuery(misc.searchQuery, genres, ['title']);
let timeout: any = null; let timeout: any = null;
const handleRowClick = (e: any, rowData: any, tableData: any) => { const handleRowClick = (e: any, rowData: any, tableData: any) => {
@ -48,12 +48,12 @@ const GenreList = () => {
const handleRowDoubleClick = (rowData: any) => { const handleRowDoubleClick = (rowData: any) => {
window.clearTimeout(timeout); window.clearTimeout(timeout);
timeout = null; timeout = null;
dispatch(setActive({ ...album.active, filter: rowData.value })); dispatch(setActive({ ...album.active, filter: rowData.title }));
dispatch(clearSelected()); dispatch(clearSelected());
// Needs a small delay or the filter won't set properly when navigating to the album list // Needs a small delay or the filter won't set properly when navigating to the album list
setTimeout(() => { setTimeout(() => {
history.push(`/library/album?sortType=${rowData.value}`); history.push(`/library/album?sortType=${rowData.title}`);
}, 50); }, 50);
}; };

29
src/components/player/NowPlayingMiniView.tsx

@ -82,8 +82,8 @@ const NowPlayingMiniView = () => {
const genresOrderedBySongCount = _.orderBy(res, 'songCount', 'desc'); const genresOrderedBySongCount = _.orderBy(res, 'songCount', 'desc');
return genresOrderedBySongCount.map((genre: any) => { return genresOrderedBySongCount.map((genre: any) => {
return { return {
label: `${genre.value} (${genre.songCount})`, label: `${genre.title} (${genre.songCount})`,
value: genre.value, title: genre.title,
role: 'Genre', role: 'Genre',
}; };
}); });
@ -190,7 +190,7 @@ const NowPlayingMiniView = () => {
const cleanedSongs = filterPlayQueue( const cleanedSongs = filterPlayQueue(
config.playback.filters, config.playback.filters,
res.song.filter((song: any) => { res.filter((song: any) => {
// Remove invalid songs that may break the player // Remove invalid songs that may break the player
return song.bitRate && song.duration; return song.bitRate && song.duration;
}) })
@ -210,7 +210,7 @@ const NowPlayingMiniView = () => {
notifyToast( notifyToast(
'info', 'info',
getPlayedSongsNotification({ getPlayedSongsNotification({
original: res.song.length, original: res.length,
filtered: cleanedSongs.count.filtered, filtered: cleanedSongs.count.filtered,
type: 'play', type: 'play',
}) })
@ -224,7 +224,7 @@ const NowPlayingMiniView = () => {
notifyToast( notifyToast(
'info', 'info',
getPlayedSongsNotification({ getPlayedSongsNotification({
original: res.song.length, original: res.length,
filtered: cleanedSongs.count.filtered, filtered: cleanedSongs.count.filtered,
type: 'add', type: 'add',
}) })
@ -238,7 +238,7 @@ const NowPlayingMiniView = () => {
notifyToast( notifyToast(
'info', 'info',
getPlayedSongsNotification({ getPlayedSongsNotification({
original: res.song.length, original: res.length,
filtered: cleanedSongs.count.filtered, filtered: cleanedSongs.count.filtered,
type: 'add', type: 'add',
}) })
@ -300,7 +300,8 @@ const NowPlayingMiniView = () => {
<Whisper <Whisper
ref={autoPlaylistTriggerRef} ref={autoPlaylistTriggerRef}
placement="autoVertical" placement="autoVertical"
trigger="none" trigger="click"
enterable
speaker={ speaker={
<StyledPopover> <StyledPopover>
<ControlLabel>How many tracks? (1-500)*</ControlLabel> <ControlLabel>How many tracks? (1-500)*</ControlLabel>
@ -356,6 +357,8 @@ const NowPlayingMiniView = () => {
container={() => genrePickerContainerRef.current} container={() => genrePickerContainerRef.current}
data={!isLoadingGenres ? genres : []} data={!isLoadingGenres ? genres : []}
value={randomPlaylistGenre} value={randomPlaylistGenre}
valueKey="title"
labelKey="label"
virtualized virtualized
onChange={(e: string) => setRandomPlaylistGenre(e)} onChange={(e: string) => setRandomPlaylistGenre(e)}
/> />
@ -370,7 +373,7 @@ const NowPlayingMiniView = () => {
data={!isLoadingMusicFolders ? musicFolders : []} data={!isLoadingMusicFolders ? musicFolders : []}
defaultValue={musicFolder} defaultValue={musicFolder}
valueKey="id" valueKey="id"
labelKey="name" labelKey="title"
onChange={(e: any) => { onChange={(e: any) => {
setMusicFolder(e); setMusicFolder(e);
}} }}
@ -411,15 +414,7 @@ const NowPlayingMiniView = () => {
</StyledPopover> </StyledPopover>
} }
> >
<AutoPlaylistButton <AutoPlaylistButton size="xs" noText />
size="xs"
noText
onClick={() =>
autoPlaylistTriggerRef.current.state.isOverlayShown
? autoPlaylistTriggerRef.current.close()
: autoPlaylistTriggerRef.current.open()
}
/>
</Whisper> </Whisper>
<MoveTopButton <MoveTopButton
size="xs" size="xs"

28
src/components/player/NowPlayingView.tsx

@ -97,8 +97,8 @@ const NowPlayingView = () => {
const genresOrderedBySongCount = _.orderBy(res, 'songCount', 'desc'); const genresOrderedBySongCount = _.orderBy(res, 'songCount', 'desc');
return genresOrderedBySongCount.map((genre: any) => { return genresOrderedBySongCount.map((genre: any) => {
return { return {
label: `${genre.value} (${genre.songCount})`, label: `${genre.title} (${genre.songCount})`,
value: genre.value, title: genre.title,
role: 'Genre', role: 'Genre',
}; };
}); });
@ -201,7 +201,7 @@ const NowPlayingView = () => {
const cleanedSongs = filterPlayQueue( const cleanedSongs = filterPlayQueue(
config.playback.filters, config.playback.filters,
res.song.filter((song: any) => { res.filter((song: any) => {
// Remove invalid songs that may break the player // Remove invalid songs that may break the player
return song.bitRate && song.duration; return song.bitRate && song.duration;
}) })
@ -221,7 +221,7 @@ const NowPlayingView = () => {
notifyToast( notifyToast(
'info', 'info',
getPlayedSongsNotification({ getPlayedSongsNotification({
original: res.song.length, original: res.length,
filtered: cleanedSongs.count.filtered, filtered: cleanedSongs.count.filtered,
type: 'play', type: 'play',
}) })
@ -235,7 +235,7 @@ const NowPlayingView = () => {
notifyToast( notifyToast(
'info', 'info',
getPlayedSongsNotification({ getPlayedSongsNotification({
original: res.song.length, original: res.length,
filtered: cleanedSongs.count.filtered, filtered: cleanedSongs.count.filtered,
type: 'add', type: 'add',
}) })
@ -249,7 +249,7 @@ const NowPlayingView = () => {
notifyToast( notifyToast(
'info', 'info',
getPlayedSongsNotification({ getPlayedSongsNotification({
original: res.song.length, original: res.length,
filtered: cleanedSongs.count.filtered, filtered: cleanedSongs.count.filtered,
type: 'add', type: 'add',
}) })
@ -310,7 +310,8 @@ const NowPlayingView = () => {
<Whisper <Whisper
ref={autoPlaylistTriggerRef} ref={autoPlaylistTriggerRef}
placement="autoVertical" placement="autoVertical"
trigger="none" trigger="click"
enterable
speaker={ speaker={
<StyledPopover> <StyledPopover>
<ControlLabel>How many tracks? (1-500)*</ControlLabel> <ControlLabel>How many tracks? (1-500)*</ControlLabel>
@ -367,6 +368,8 @@ const NowPlayingView = () => {
container={() => genrePickerContainerRef.current} container={() => genrePickerContainerRef.current}
data={genres} data={genres}
value={randomPlaylistGenre} value={randomPlaylistGenre}
valueKey="title"
labelKey="label"
virtualized virtualized
onChange={(e: string) => setRandomPlaylistGenre(e)} onChange={(e: string) => setRandomPlaylistGenre(e)}
/> />
@ -381,7 +384,7 @@ const NowPlayingView = () => {
data={musicFolders} data={musicFolders}
defaultValue={musicFolder} defaultValue={musicFolder}
valueKey="id" valueKey="id"
labelKey="name" labelKey="title"
onChange={(e: any) => { onChange={(e: any) => {
setMusicFolder(e); setMusicFolder(e);
}} }}
@ -421,14 +424,7 @@ const NowPlayingView = () => {
</StyledPopover> </StyledPopover>
} }
> >
<AutoPlaylistButton <AutoPlaylistButton size="sm" />
size="sm"
onClick={() =>
autoPlaylistTriggerRef.current.state.isOverlayShown
? autoPlaylistTriggerRef.current.close()
: autoPlaylistTriggerRef.current.open()
}
/>
</Whisper> </Whisper>
<ButtonGroup> <ButtonGroup>
<MoveTopButton <MoveTopButton

9
src/components/playlist/PlaylistList.tsx

@ -29,13 +29,12 @@ const PlaylistList = () => {
const config = useAppSelector((state) => state.config); const config = useAppSelector((state) => state.config);
const misc = useAppSelector((state) => state.misc); const misc = useAppSelector((state) => state.misc);
const playlistTriggerRef = useRef<any>(); const playlistTriggerRef = useRef<any>();
const [sortBy] = useState('name');
const [newPlaylistName, setNewPlaylistName] = useState(''); const [newPlaylistName, setNewPlaylistName] = useState('');
const [viewType, setViewType] = useState(settings.getSync('playlistViewType') || 'list'); const [viewType, setViewType] = useState(settings.getSync('playlistViewType') || 'list');
const { isLoading, isError, data: playlists, error }: any = useQuery(['playlists', sortBy], () => const { isLoading, isError, data: playlists, error }: any = useQuery(['playlists'], () =>
getPlaylists(sortBy) getPlaylists()
); );
const filteredData = useSearchQuery(misc.searchQuery, playlists, ['name', 'comment', 'owner']); const filteredData = useSearchQuery(misc.searchQuery, playlists, ['title', 'comment', 'owner']);
const handleCreatePlaylist = async (name: string) => { const handleCreatePlaylist = async (name: string) => {
try { try {
@ -174,7 +173,7 @@ const PlaylistList = () => {
data={misc.searchQuery === '' ? playlists : filteredData} data={misc.searchQuery === '' ? playlists : filteredData}
cardTitle={{ cardTitle={{
prefix: 'playlist', prefix: 'playlist',
property: 'name', property: 'title',
urlProperty: 'id', urlProperty: 'id',
}} }}
cardSubtitle={{ cardSubtitle={{

4
src/components/playlist/PlaylistView.tsx

@ -135,7 +135,7 @@ const PlaylistView = ({ ...rest }) => {
useEffect(() => { useEffect(() => {
// Set the local playlist data on any changes // Set the local playlist data on any changes
dispatch(setPlaylistData(data?.song)); dispatch(setPlaylistData(data?.song));
setEditName(data?.name); setEditName(data?.title);
setEditDescription(data?.comment); setEditDescription(data?.comment);
setEditPublic(data?.public); setEditPublic(data?.public);
}, [data, dispatch]); }, [data, dispatch]);
@ -402,7 +402,7 @@ const PlaylistView = ({ ...rest }) => {
id: data.id, id: data.id,
}} }}
imageHeight={184} imageHeight={184}
title={data.name} title={data.title}
subtitle={ subtitle={
<div> <div>
<PageHeaderSubtitleDataLine $top> <PageHeaderSubtitleDataLine $top>

4
src/components/search/SearchView.tsx

@ -164,7 +164,7 @@ const SearchView = () => {
data={data.artist} data={data.artist}
cardTitle={{ cardTitle={{
prefix: '/library/artist', prefix: '/library/artist',
property: 'name', property: 'title',
urlProperty: 'id', urlProperty: 'id',
}} }}
cardSubtitle={{ cardSubtitle={{
@ -181,7 +181,7 @@ const SearchView = () => {
data={data.album} data={data.album}
cardTitle={{ cardTitle={{
prefix: '/library/album', prefix: '/library/album',
property: 'name', property: 'title',
urlProperty: 'albumId', urlProperty: 'albumId',
}} }}
cardSubtitle={{ cardSubtitle={{

46
src/components/settings/ListViewColumns.ts

@ -476,7 +476,7 @@ export const albumColumnList = [
label: 'Title', label: 'Title',
value: { value: {
id: 'Title', id: 'Title',
dataKey: 'name', dataKey: 'title',
alignment: 'left', alignment: 'left',
resizable: true, resizable: true,
width: 300, width: 300,
@ -594,7 +594,7 @@ export const albumColumnListAuto = [
label: 'Title', label: 'Title',
value: { value: {
id: 'Title', id: 'Title',
dataKey: 'name', dataKey: 'title',
alignment: 'left', alignment: 'left',
flexGrow: 5, flexGrow: 5,
label: 'Title', label: 'Title',
@ -752,7 +752,7 @@ export const playlistColumnList = [
label: 'Title', label: 'Title',
value: { value: {
id: 'Title', id: 'Title',
dataKey: 'name', dataKey: 'title',
alignment: 'left', alignment: 'left',
resizable: true, resizable: true,
width: 300, width: 300,
@ -879,7 +879,7 @@ export const playlistColumnListAuto = [
label: 'Title', label: 'Title',
value: { value: {
id: 'Title', id: 'Title',
dataKey: 'name', dataKey: 'title',
alignment: 'left', alignment: 'left',
flexGrow: 5, flexGrow: 5,
label: 'Title', label: 'Title',
@ -956,14 +956,14 @@ export const artistColumnList = [
}, },
}, },
{ {
label: 'Name', label: 'Title',
value: { value: {
id: 'Name', id: 'Title',
dataKey: 'name', dataKey: 'title',
alignment: 'left', alignment: 'left',
resizable: true, resizable: true,
width: 300, width: 300,
label: 'Name', label: 'Title',
}, },
}, },
]; ];
@ -1012,13 +1012,13 @@ export const artistColumnListAuto = [
}, },
}, },
{ {
label: 'Name', label: 'Title',
value: { value: {
id: 'Name', id: 'Title',
dataKey: 'name', dataKey: 'title',
alignment: 'left', alignment: 'left',
flexGrow: 5, flexGrow: 5,
label: 'Name', label: 'Title',
}, },
}, },
]; ];
@ -1028,13 +1028,13 @@ export const artistColumnPicker = [
{ label: 'Album Count' }, { label: 'Album Count' },
{ label: 'CoverArt' }, { label: 'CoverArt' },
{ label: 'Favorite' }, { label: 'Favorite' },
{ label: 'Name' }, { label: 'Title' },
]; ];
export const genreColumnPicker = [ export const genreColumnPicker = [
{ label: '#' }, { label: '#' },
{ label: 'Album Count' }, { label: 'Album Count' },
{ label: 'Name' }, { label: 'Title' },
{ label: 'Track Count' }, { label: 'Track Count' },
]; ];
@ -1062,14 +1062,14 @@ export const genreColumnList = [
}, },
}, },
{ {
label: 'Name', label: 'Title',
value: { value: {
id: 'Name', id: 'Title',
dataKey: 'name', dataKey: 'title',
alignment: 'left', alignment: 'left',
resizable: true, resizable: true,
width: 300, width: 300,
label: 'Name', label: 'Title',
}, },
}, },
{ {
@ -1108,17 +1108,17 @@ export const genreColumnListAuto = [
}, },
}, },
{ {
label: 'Name', label: 'Title',
value: { value: {
id: 'Name', id: 'Title',
dataKey: 'name', dataKey: 'title',
alignment: 'left', alignment: 'left',
flexGrow: 5, flexGrow: 5,
label: 'Name', label: 'Title',
}, },
}, },
{ {
label: 'Tracks', label: 'Track Count',
value: { value: {
id: 'Tracks', id: 'Tracks',
dataKey: 'songCount', dataKey: 'songCount',

26
src/components/shared/ContextMenu.tsx

@ -14,8 +14,8 @@ import {
getAlbum, getAlbum,
getPlaylist, getPlaylist,
deletePlaylist, deletePlaylist,
getAllArtistSongs, getArtistSongs,
getAllDirectorySongs, getDirectorySongs,
} from '../../api/api'; } from '../../api/api';
import { useAppDispatch, useAppSelector } from '../../redux/hooks'; import { useAppDispatch, useAppSelector } from '../../redux/hooks';
import { import {
@ -120,7 +120,7 @@ export const GlobalContextMenu = () => {
const [indexToMoveTo, setIndexToMoveTo] = useState(0); const [indexToMoveTo, setIndexToMoveTo] = useState(0);
const playlistPickerContainerRef = useRef(null); const playlistPickerContainerRef = useRef(null);
const { data: playlists }: any = useQuery(['playlists', 'name'], () => getPlaylists('name')); const { data: playlists }: any = useQuery(['playlists'], () => getPlaylists());
const handlePlay = async () => { const handlePlay = async () => {
dispatch(setContextMenu({ show: false })); dispatch(setContextMenu({ show: false }));
@ -135,7 +135,7 @@ export const GlobalContextMenu = () => {
}); });
for (let i = 0; i < folders.length; i += 1) { for (let i = 0; i < folders.length; i += 1) {
promises.push(getAllDirectorySongs({ id: folders[i].id })); promises.push(getDirectorySongs({ id: folders[i].id }));
} }
const res = await Promise.all(promises); const res = await Promise.all(promises);
@ -172,7 +172,7 @@ export const GlobalContextMenu = () => {
notifyToast('info', getPlayedSongsNotification({ ...songs.count, type: 'play' })); notifyToast('info', getPlayedSongsNotification({ ...songs.count, type: 'play' }));
} else if (misc.contextMenu.type === 'album') { } else if (misc.contextMenu.type === 'album') {
for (let i = 0; i < multiSelect.selected.length; i += 1) { for (let i = 0; i < multiSelect.selected.length; i += 1) {
promises.push(getAlbum(multiSelect.selected[i].id)); promises.push(getAlbum({ id: multiSelect.selected[i].id }));
} }
const res = await Promise.all(promises); const res = await Promise.all(promises);
@ -191,7 +191,7 @@ export const GlobalContextMenu = () => {
notifyToast('info', getPlayedSongsNotification({ ...songs.count, type: 'play' })); notifyToast('info', getPlayedSongsNotification({ ...songs.count, type: 'play' }));
} else if (misc.contextMenu.type === 'artist') { } else if (misc.contextMenu.type === 'artist') {
for (let i = 0; i < multiSelect.selected.length; i += 1) { for (let i = 0; i < multiSelect.selected.length; i += 1) {
promises.push(getAllArtistSongs(multiSelect.selected[i].id)); promises.push(getArtistSongs({ id: multiSelect.selected[i].id }));
} }
const res = await Promise.all(promises); const res = await Promise.all(promises);
@ -223,7 +223,7 @@ export const GlobalContextMenu = () => {
}); });
for (let i = 0; i < folders.length; i += 1) { for (let i = 0; i < folders.length; i += 1) {
promises.push(getAllDirectorySongs({ id: multiSelect.selected[i].id })); promises.push(getDirectorySongs({ id: multiSelect.selected[i].id }));
} }
const res = await Promise.all(promises); const res = await Promise.all(promises);
@ -252,7 +252,7 @@ export const GlobalContextMenu = () => {
notifyToast('info', getPlayedSongsNotification({ ...songs.count, type: 'add' })); notifyToast('info', getPlayedSongsNotification({ ...songs.count, type: 'add' }));
} else if (misc.contextMenu.type === 'album') { } else if (misc.contextMenu.type === 'album') {
for (let i = 0; i < multiSelect.selected.length; i += 1) { for (let i = 0; i < multiSelect.selected.length; i += 1) {
promises.push(getAlbum(multiSelect.selected[i].id)); promises.push(getAlbum({ id: multiSelect.selected[i].id }));
} }
const res = await Promise.all(promises); const res = await Promise.all(promises);
@ -266,7 +266,7 @@ export const GlobalContextMenu = () => {
notifyToast('info', getPlayedSongsNotification({ ...songs.count, type: 'add' })); notifyToast('info', getPlayedSongsNotification({ ...songs.count, type: 'add' }));
} else if (misc.contextMenu.type === 'artist') { } else if (misc.contextMenu.type === 'artist') {
for (let i = 0; i < multiSelect.selected.length; i += 1) { for (let i = 0; i < multiSelect.selected.length; i += 1) {
promises.push(getAllArtistSongs(multiSelect.selected[i].id)); promises.push(getArtistSongs({ id: multiSelect.selected[i].id }));
} }
const res = await Promise.all(promises); const res = await Promise.all(promises);
@ -298,7 +298,7 @@ export const GlobalContextMenu = () => {
notifyToast( notifyToast(
'success', 'success',
`Added ${songCount} song(s) to playlist ${ `Added ${songCount} song(s) to playlist ${
playlists.find((pl: any) => pl.id === playlistId)?.name playlists.find((pl: any) => pl.id === playlistId)?.title
}`, }`,
<> <>
<StyledButton <StyledButton
@ -332,7 +332,7 @@ export const GlobalContextMenu = () => {
}); });
for (let i = 0; i < folders.length; i += 1) { for (let i = 0; i < folders.length; i += 1) {
promises.push(getAllDirectorySongs({ id: multiSelect.selected[i].id })); promises.push(getDirectorySongs({ id: multiSelect.selected[i].id }));
} }
const folderSongs = await Promise.all(promises); const folderSongs = await Promise.all(promises);
@ -363,7 +363,7 @@ export const GlobalContextMenu = () => {
} }
} else if (misc.contextMenu.type === 'album') { } else if (misc.contextMenu.type === 'album') {
for (let i = 0; i < multiSelect.selected.length; i += 1) { for (let i = 0; i < multiSelect.selected.length; i += 1) {
promises.push(getAlbum(multiSelect.selected[i].id)); promises.push(getAlbum({ id: multiSelect.selected[i].id }));
} }
res = await Promise.all(promises); res = await Promise.all(promises);
@ -700,7 +700,7 @@ export const GlobalContextMenu = () => {
data={playlists} data={playlists}
placement="autoVerticalStart" placement="autoVerticalStart"
virtualized virtualized
labelKey="name" labelKey="title"
valueKey="id" valueKey="id"
width={200} width={200}
onChange={(e: any) => setSelectedPlaylistId(e)} onChange={(e: any) => setSelectedPlaylistId(e)}

10
src/components/starred/StarredView.tsx

@ -50,10 +50,10 @@ const StarredView = () => {
? data?.album ? data?.album
: data?.artist, : data?.artist,
favorite.active.tab === 'tracks' favorite.active.tab === 'tracks'
? ['title', 'artist', 'album', 'name', 'genre'] ? ['title', 'artist', 'album', 'genre']
: favorite.active.tab === 'albums' : favorite.active.tab === 'albums'
? ['name', 'artist', 'genre', 'year'] ? ['title', 'artist', 'genre', 'year']
: ['name'] : ['title']
); );
let timeout: any = null; let timeout: any = null;
@ -230,7 +230,7 @@ const StarredView = () => {
data={misc.searchQuery !== '' ? filteredData : data.album} data={misc.searchQuery !== '' ? filteredData : data.album}
cardTitle={{ cardTitle={{
prefix: '/library/album', prefix: '/library/album',
property: 'name', property: 'title',
urlProperty: 'albumId', urlProperty: 'albumId',
}} }}
cardSubtitle={{ cardSubtitle={{
@ -279,7 +279,7 @@ const StarredView = () => {
data={misc.searchQuery !== '' ? filteredData : data.artist} data={misc.searchQuery !== '' ? filteredData : data.artist}
cardTitle={{ cardTitle={{
prefix: '/library/artist', prefix: '/library/artist',
property: 'name', property: 'title',
urlProperty: 'id', urlProperty: 'id',
}} }}
cardSubtitle={{ cardSubtitle={{

Loading…
Cancel
Save