mirror of
https://github.com/antebudimir/feishin.git
synced 2026-01-02 02:43:33 +00:00
Support tags, and better participants for servers
- Parses `tags` for Navidrome (mapping string: string[]) - Parses `Tags` (and fetches for it) for Jellyfin (map a string to empty, and display as a bool) - Clean parsing of participants for Navidrome/Subsonic - Only show `People` for Jellyfin, not clickable
This commit is contained in:
parent
89e27ec6ff
commit
b0d86ee5c9
9 changed files with 131 additions and 13 deletions
|
|
@ -27,6 +27,7 @@
|
||||||
"action_one": "action",
|
"action_one": "action",
|
||||||
"action_other": "actions",
|
"action_other": "actions",
|
||||||
"add": "add",
|
"add": "add",
|
||||||
|
"additionalParticipants": "additional participants",
|
||||||
"albumGain": "album gain",
|
"albumGain": "album gain",
|
||||||
"albumPeak": "album peak",
|
"albumPeak": "album peak",
|
||||||
"areYouSure": "are you sure?",
|
"areYouSure": "are you sure?",
|
||||||
|
|
@ -106,6 +107,7 @@
|
||||||
"share": "share",
|
"share": "share",
|
||||||
"size": "size",
|
"size": "size",
|
||||||
"sortOrder": "order",
|
"sortOrder": "order",
|
||||||
|
"tags": "tags",
|
||||||
"title": "title",
|
"title": "title",
|
||||||
"trackNumber": "track",
|
"trackNumber": "track",
|
||||||
"trackGain": "track gain",
|
"trackGain": "track gain",
|
||||||
|
|
|
||||||
|
|
@ -246,7 +246,7 @@ export const JellyfinController: ControllerEndpoint = {
|
||||||
userId: apiClientProps.server.userId,
|
userId: apiClientProps.server.userId,
|
||||||
},
|
},
|
||||||
query: {
|
query: {
|
||||||
Fields: 'Genres, DateCreated, ChildCount',
|
Fields: 'Genres, DateCreated, ChildCount, People, Tags',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -255,7 +255,7 @@ export const JellyfinController: ControllerEndpoint = {
|
||||||
userId: apiClientProps.server.userId,
|
userId: apiClientProps.server.userId,
|
||||||
},
|
},
|
||||||
query: {
|
query: {
|
||||||
Fields: 'Genres, DateCreated, MediaSources, ParentId',
|
Fields: 'Genres, DateCreated, MediaSources, ParentId, People, Tags',
|
||||||
IncludeItemTypes: 'Audio',
|
IncludeItemTypes: 'Audio',
|
||||||
ParentId: query.id,
|
ParentId: query.id,
|
||||||
SortBy: 'ParentIndexNumber,IndexNumber,SortName',
|
SortBy: 'ParentIndexNumber,IndexNumber,SortName',
|
||||||
|
|
@ -300,6 +300,7 @@ export const JellyfinController: ControllerEndpoint = {
|
||||||
query.artistIds && {
|
query.artistIds && {
|
||||||
ContributingArtistIds: query.artistIds[0],
|
ContributingArtistIds: query.artistIds[0],
|
||||||
}),
|
}),
|
||||||
|
Fields: 'People, Tags',
|
||||||
GenreIds: query.genres ? query.genres.join(',') : undefined,
|
GenreIds: query.genres ? query.genres.join(',') : undefined,
|
||||||
IncludeItemTypes: 'MusicAlbum',
|
IncludeItemTypes: 'MusicAlbum',
|
||||||
IsFavorite: query.favorite,
|
IsFavorite: query.favorite,
|
||||||
|
|
@ -523,7 +524,7 @@ export const JellyfinController: ControllerEndpoint = {
|
||||||
id: query.id,
|
id: query.id,
|
||||||
},
|
},
|
||||||
query: {
|
query: {
|
||||||
Fields: 'Genres, DateCreated, MediaSources, UserData, ParentId',
|
Fields: 'Genres, DateCreated, MediaSources, UserData, ParentId, People, Tags',
|
||||||
IncludeItemTypes: 'Audio',
|
IncludeItemTypes: 'Audio',
|
||||||
Limit: query.limit,
|
Limit: query.limit,
|
||||||
SortBy: query.sortBy ? songListSortMap.jellyfin[query.sortBy] : undefined,
|
SortBy: query.sortBy ? songListSortMap.jellyfin[query.sortBy] : undefined,
|
||||||
|
|
@ -564,7 +565,7 @@ export const JellyfinController: ControllerEndpoint = {
|
||||||
userId: apiClientProps.server?.userId,
|
userId: apiClientProps.server?.userId,
|
||||||
},
|
},
|
||||||
query: {
|
query: {
|
||||||
Fields: 'Genres, DateCreated, MediaSources, ParentId',
|
Fields: 'Genres, DateCreated, MediaSources, ParentId, People, Tags',
|
||||||
GenreIds: query.genre ? query.genre : undefined,
|
GenreIds: query.genre ? query.genre : undefined,
|
||||||
IncludeItemTypes: 'Audio',
|
IncludeItemTypes: 'Audio',
|
||||||
IsPlayed:
|
IsPlayed:
|
||||||
|
|
@ -719,7 +720,7 @@ export const JellyfinController: ControllerEndpoint = {
|
||||||
query: {
|
query: {
|
||||||
AlbumIds: albumIdsFilter,
|
AlbumIds: albumIdsFilter,
|
||||||
ArtistIds: artistIdsFilter,
|
ArtistIds: artistIdsFilter,
|
||||||
Fields: 'Genres, DateCreated, MediaSources, ParentId',
|
Fields: 'Genres, DateCreated, MediaSources, ParentId, People, Tags',
|
||||||
GenreIds: query.genreIds?.join(','),
|
GenreIds: query.genreIds?.join(','),
|
||||||
IncludeItemTypes: 'Audio',
|
IncludeItemTypes: 'Audio',
|
||||||
IsFavorite: query.favorite,
|
IsFavorite: query.favorite,
|
||||||
|
|
@ -754,7 +755,7 @@ export const JellyfinController: ControllerEndpoint = {
|
||||||
query: {
|
query: {
|
||||||
AlbumIds: albumIdsFilter,
|
AlbumIds: albumIdsFilter,
|
||||||
ArtistIds: artistIdsFilter,
|
ArtistIds: artistIdsFilter,
|
||||||
Fields: 'Genres, DateCreated, MediaSources, ParentId',
|
Fields: 'Genres, DateCreated, MediaSources, ParentId, People, Tags',
|
||||||
GenreIds: query.genreIds?.join(','),
|
GenreIds: query.genreIds?.join(','),
|
||||||
IncludeItemTypes: 'Audio',
|
IncludeItemTypes: 'Audio',
|
||||||
IsFavorite: query.favorite,
|
IsFavorite: query.favorite,
|
||||||
|
|
@ -967,6 +968,7 @@ export const JellyfinController: ControllerEndpoint = {
|
||||||
},
|
},
|
||||||
query: {
|
query: {
|
||||||
EnableTotalRecordCount: true,
|
EnableTotalRecordCount: true,
|
||||||
|
Fields: 'People, Tags',
|
||||||
ImageTypeLimit: 1,
|
ImageTypeLimit: 1,
|
||||||
IncludeItemTypes: 'MusicAlbum',
|
IncludeItemTypes: 'MusicAlbum',
|
||||||
Limit: query.albumLimit,
|
Limit: query.albumLimit,
|
||||||
|
|
@ -1014,7 +1016,7 @@ export const JellyfinController: ControllerEndpoint = {
|
||||||
},
|
},
|
||||||
query: {
|
query: {
|
||||||
EnableTotalRecordCount: true,
|
EnableTotalRecordCount: true,
|
||||||
Fields: 'Genres, DateCreated, MediaSources, ParentId',
|
Fields: 'Genres, DateCreated, MediaSources, ParentId, People, Tags',
|
||||||
IncludeItemTypes: 'Audio',
|
IncludeItemTypes: 'Audio',
|
||||||
Limit: query.songLimit,
|
Limit: query.songLimit,
|
||||||
Recursive: true,
|
Recursive: true,
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import {
|
||||||
Genre,
|
Genre,
|
||||||
ServerListItem,
|
ServerListItem,
|
||||||
ServerType,
|
ServerType,
|
||||||
|
RelatedArtist,
|
||||||
} from '/@/renderer/api/types';
|
} from '/@/renderer/api/types';
|
||||||
|
|
||||||
const getStreamUrl = (args: {
|
const getStreamUrl = (args: {
|
||||||
|
|
@ -121,6 +122,48 @@ const getPlaylistCoverArtUrl = (args: { baseUrl: string; item: JFPlaylist; size:
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type AlbumOrSong = z.infer<typeof jfType._response.song> | z.infer<typeof jfType._response.album>;
|
||||||
|
|
||||||
|
const getPeople = (item: AlbumOrSong): Record<string, RelatedArtist[]> | null => {
|
||||||
|
if (item.People) {
|
||||||
|
const participants: Record<string, RelatedArtist[]> = {};
|
||||||
|
|
||||||
|
for (const person of item.People) {
|
||||||
|
const key = person.Type || '';
|
||||||
|
const item: RelatedArtist = {
|
||||||
|
// for other roles, we just want to display this and not filter.
|
||||||
|
// filtering (and links) would require a separate field, PersonIds
|
||||||
|
id: '',
|
||||||
|
imageUrl: null,
|
||||||
|
name: person.Name,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (key in participants) {
|
||||||
|
participants[key].push(item);
|
||||||
|
} else {
|
||||||
|
participants[key] = [item];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return participants;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getTags = (item: AlbumOrSong): Record<string, string[]> | null => {
|
||||||
|
if (item.Tags) {
|
||||||
|
const tags: Record<string, string[]> = {};
|
||||||
|
for (const tag of item.Tags) {
|
||||||
|
tags[tag] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
const normalizeSong = (
|
const normalizeSong = (
|
||||||
item: z.infer<typeof jfType._response.song>,
|
item: z.infer<typeof jfType._response.song>,
|
||||||
server: ServerListItem | null,
|
server: ServerListItem | null,
|
||||||
|
|
@ -176,7 +219,7 @@ const normalizeSong = (
|
||||||
lastPlayedAt: null,
|
lastPlayedAt: null,
|
||||||
lyrics: null,
|
lyrics: null,
|
||||||
name: item.Name,
|
name: item.Name,
|
||||||
participants: null,
|
participants: getPeople(item),
|
||||||
path: (item.MediaSources && item.MediaSources[0]?.Path) || null,
|
path: (item.MediaSources && item.MediaSources[0]?.Path) || null,
|
||||||
peak: null,
|
peak: null,
|
||||||
playCount: (item.UserData && item.UserData.PlayCount) || 0,
|
playCount: (item.UserData && item.UserData.PlayCount) || 0,
|
||||||
|
|
@ -198,6 +241,7 @@ const normalizeSong = (
|
||||||
mediaSourceId: item.MediaSources?.[0]?.Id,
|
mediaSourceId: item.MediaSources?.[0]?.Id,
|
||||||
server,
|
server,
|
||||||
}),
|
}),
|
||||||
|
tags: getTags(item),
|
||||||
trackNumber: item.IndexNumber,
|
trackNumber: item.IndexNumber,
|
||||||
uniqueId: nanoid(),
|
uniqueId: nanoid(),
|
||||||
updatedAt: item.DateCreated,
|
updatedAt: item.DateCreated,
|
||||||
|
|
@ -247,7 +291,7 @@ const normalizeAlbum = (
|
||||||
mbzId: item.ProviderIds?.MusicBrainzAlbum || null,
|
mbzId: item.ProviderIds?.MusicBrainzAlbum || null,
|
||||||
name: item.Name,
|
name: item.Name,
|
||||||
originalDate: null,
|
originalDate: null,
|
||||||
participants: null,
|
participants: getPeople(item),
|
||||||
playCount: item.UserData?.PlayCount || 0,
|
playCount: item.UserData?.PlayCount || 0,
|
||||||
releaseDate: item.PremiereDate?.split('T')[0] || null,
|
releaseDate: item.PremiereDate?.split('T')[0] || null,
|
||||||
releaseYear: item.ProductionYear || null,
|
releaseYear: item.ProductionYear || null,
|
||||||
|
|
@ -256,6 +300,7 @@ const normalizeAlbum = (
|
||||||
size: null,
|
size: null,
|
||||||
songCount: item?.ChildCount || null,
|
songCount: item?.ChildCount || null,
|
||||||
songs: item.Songs?.map((song) => normalizeSong(song, server, '', imageSize)),
|
songs: item.Songs?.map((song) => normalizeSong(song, server, '', imageSize)),
|
||||||
|
tags: getTags(item),
|
||||||
uniqueId: nanoid(),
|
uniqueId: nanoid(),
|
||||||
updatedAt: item?.DateLastMediaAdded || item.DateCreated,
|
updatedAt: item?.DateLastMediaAdded || item.DateCreated,
|
||||||
userFavorite: item.UserData?.IsFavorite || false,
|
userFavorite: item.UserData?.IsFavorite || false,
|
||||||
|
|
|
||||||
|
|
@ -387,6 +387,12 @@ const genericItem = z.object({
|
||||||
Name: z.string(),
|
Name: z.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const participant = z.object({
|
||||||
|
Id: z.string(),
|
||||||
|
Name: z.string(),
|
||||||
|
Type: z.string().optional(),
|
||||||
|
});
|
||||||
|
|
||||||
const songDetailParameters = baseParameters;
|
const songDetailParameters = baseParameters;
|
||||||
|
|
||||||
const song = z.object({
|
const song = z.object({
|
||||||
|
|
@ -415,12 +421,14 @@ const song = z.object({
|
||||||
Name: z.string(),
|
Name: z.string(),
|
||||||
NormalizationGain: z.number().optional(),
|
NormalizationGain: z.number().optional(),
|
||||||
ParentIndexNumber: z.number(),
|
ParentIndexNumber: z.number(),
|
||||||
|
People: participant.array().optional(),
|
||||||
PlaylistItemId: z.string().optional(),
|
PlaylistItemId: z.string().optional(),
|
||||||
PremiereDate: z.string().optional(),
|
PremiereDate: z.string().optional(),
|
||||||
ProductionYear: z.number(),
|
ProductionYear: z.number(),
|
||||||
RunTimeTicks: z.number(),
|
RunTimeTicks: z.number(),
|
||||||
ServerId: z.string(),
|
ServerId: z.string(),
|
||||||
SortName: z.string(),
|
SortName: z.string(),
|
||||||
|
Tags: z.string().array().optional(),
|
||||||
Type: z.string(),
|
Type: z.string(),
|
||||||
UserData: userData.optional(),
|
UserData: userData.optional(),
|
||||||
});
|
});
|
||||||
|
|
@ -475,12 +483,14 @@ const album = z.object({
|
||||||
Name: z.string(),
|
Name: z.string(),
|
||||||
ParentLogoImageTag: z.string(),
|
ParentLogoImageTag: z.string(),
|
||||||
ParentLogoItemId: z.string(),
|
ParentLogoItemId: z.string(),
|
||||||
|
People: participant.array().optional(),
|
||||||
PremiereDate: z.string().optional(),
|
PremiereDate: z.string().optional(),
|
||||||
ProductionYear: z.number(),
|
ProductionYear: z.number(),
|
||||||
ProviderIds: providerIds.optional(),
|
ProviderIds: providerIds.optional(),
|
||||||
RunTimeTicks: z.number(),
|
RunTimeTicks: z.number(),
|
||||||
ServerId: z.string(),
|
ServerId: z.string(),
|
||||||
Songs: z.array(song).optional(), // This is not a native Jellyfin property -- this is used for combined album detail
|
Songs: z.array(song).optional(), // This is not a native Jellyfin property -- this is used for combined album detail
|
||||||
|
Tags: z.string().array().optional(),
|
||||||
Type: z.string(),
|
Type: z.string(),
|
||||||
UserData: userData.optional(),
|
UserData: userData.optional(),
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -191,6 +191,7 @@ const normalizeSong = (
|
||||||
serverType: ServerType.NAVIDROME,
|
serverType: ServerType.NAVIDROME,
|
||||||
size: item.size,
|
size: item.size,
|
||||||
streamUrl: `${server?.url}/rest/stream.view?id=${id}&v=1.13.0&c=Feishin&${server?.credential}`,
|
streamUrl: `${server?.url}/rest/stream.view?id=${id}&v=1.13.0&c=Feishin&${server?.credential}`,
|
||||||
|
tags: item.tags || null,
|
||||||
trackNumber: item.trackNumber,
|
trackNumber: item.trackNumber,
|
||||||
uniqueId: nanoid(),
|
uniqueId: nanoid(),
|
||||||
updatedAt: item.updatedAt,
|
updatedAt: item.updatedAt,
|
||||||
|
|
@ -236,6 +237,7 @@ const normalizeAlbum = (
|
||||||
isCompilation: item.compilation,
|
isCompilation: item.compilation,
|
||||||
itemType: LibraryItem.ALBUM,
|
itemType: LibraryItem.ALBUM,
|
||||||
lastPlayedAt: normalizePlayDate(item),
|
lastPlayedAt: normalizePlayDate(item),
|
||||||
|
|
||||||
mbzId: item.mbzAlbumId || null,
|
mbzId: item.mbzAlbumId || null,
|
||||||
name: item.name,
|
name: item.name,
|
||||||
originalDate: item.originalDate
|
originalDate: item.originalDate
|
||||||
|
|
@ -254,6 +256,7 @@ const normalizeAlbum = (
|
||||||
size: item.size,
|
size: item.size,
|
||||||
songCount: item.songCount,
|
songCount: item.songCount,
|
||||||
songs: item.songs ? item.songs.map((song) => normalizeSong(song, server)) : undefined,
|
songs: item.songs ? item.songs.map((song) => normalizeSong(song, server)) : undefined,
|
||||||
|
tags: item.tags || null,
|
||||||
uniqueId: nanoid(),
|
uniqueId: nanoid(),
|
||||||
updatedAt: item.updatedAt,
|
updatedAt: item.updatedAt,
|
||||||
userFavorite: item.starred,
|
userFavorite: item.starred,
|
||||||
|
|
|
||||||
|
|
@ -155,6 +155,7 @@ const album = z.object({
|
||||||
sortArtistName: z.string(),
|
sortArtistName: z.string(),
|
||||||
starred: z.boolean(),
|
starred: z.boolean(),
|
||||||
starredAt: z.string().optional(),
|
starredAt: z.string().optional(),
|
||||||
|
tags: z.record(z.string(), z.array(z.string())).optional(),
|
||||||
updatedAt: z.string(),
|
updatedAt: z.string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -181,6 +181,7 @@ const normalizeSong = (
|
||||||
serverType: ServerType.SUBSONIC,
|
serverType: ServerType.SUBSONIC,
|
||||||
size: item.size,
|
size: item.size,
|
||||||
streamUrl,
|
streamUrl,
|
||||||
|
tags: null,
|
||||||
trackNumber: item.track || 1,
|
trackNumber: item.track || 1,
|
||||||
uniqueId: nanoid(),
|
uniqueId: nanoid(),
|
||||||
updatedAt: '',
|
updatedAt: '',
|
||||||
|
|
@ -267,6 +268,7 @@ const normalizeAlbum = (
|
||||||
(item as z.infer<typeof ssType._response.album>).song?.map((song) =>
|
(item as z.infer<typeof ssType._response.album>).song?.map((song) =>
|
||||||
normalizeSong(song, server),
|
normalizeSong(song, server),
|
||||||
) || [],
|
) || [],
|
||||||
|
tags: item.tags || null,
|
||||||
uniqueId: nanoid(),
|
uniqueId: nanoid(),
|
||||||
updatedAt: item.created,
|
updatedAt: item.created,
|
||||||
userFavorite: item.starred || false,
|
userFavorite: item.starred || false,
|
||||||
|
|
|
||||||
|
|
@ -177,6 +177,7 @@ export type Album = {
|
||||||
size: number | null;
|
size: number | null;
|
||||||
songCount: number | null;
|
songCount: number | null;
|
||||||
songs?: Song[];
|
songs?: Song[];
|
||||||
|
tags: Record<string, string[]> | null;
|
||||||
uniqueId: string;
|
uniqueId: string;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
userFavorite: boolean;
|
userFavorite: boolean;
|
||||||
|
|
@ -224,6 +225,7 @@ export type Song = {
|
||||||
serverType: ServerType;
|
serverType: ServerType;
|
||||||
size: number;
|
size: number;
|
||||||
streamUrl: string;
|
streamUrl: string;
|
||||||
|
tags: Record<string, string[]> | null;
|
||||||
trackNumber: number;
|
trackNumber: number;
|
||||||
uniqueId: string;
|
uniqueId: string;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import { AppRoute } from '/@/renderer/router/routes';
|
||||||
import { Separator } from '/@/renderer/components/separator';
|
import { Separator } from '/@/renderer/components/separator';
|
||||||
import { useGenreRoute } from '/@/renderer/hooks/use-genre-route';
|
import { useGenreRoute } from '/@/renderer/hooks/use-genre-route';
|
||||||
import { formatDateRelative, formatRating } from '/@/renderer/utils/format';
|
import { formatDateRelative, formatRating } from '/@/renderer/utils/format';
|
||||||
|
import { SEPARATOR_STRING } from '/@/renderer/api/utils';
|
||||||
|
|
||||||
export type ItemDetailsModalProps = {
|
export type ItemDetailsModalProps = {
|
||||||
item: Album | AlbumArtist | Song;
|
item: Album | AlbumArtist | Song;
|
||||||
|
|
@ -277,9 +278,40 @@ const SongPropertyMapping: ItemDetailRow<Song>[] = [
|
||||||
{ label: 'filter.comment', render: formatComment },
|
{ label: 'filter.comment', render: formatComment },
|
||||||
];
|
];
|
||||||
|
|
||||||
const handleParticipants = (item: Album | Song) => {
|
const handleTags = (item: Album | Song, t: TFunction) => {
|
||||||
|
if (item.tags) {
|
||||||
|
const tags = Object.entries(item.tags).map(([tag, fields]) => {
|
||||||
|
return (
|
||||||
|
<tr key={tag}>
|
||||||
|
<td>
|
||||||
|
{tag.slice(0, 1).toLocaleUpperCase()}
|
||||||
|
{tag.slice(1)}
|
||||||
|
</td>
|
||||||
|
<td>{fields.length === 0 ? BoolField(true) : fields.join(SEPARATOR_STRING)}</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (tags.length) {
|
||||||
|
return [
|
||||||
|
<tr key="tags">
|
||||||
|
<td>
|
||||||
|
<h3>{t('common.tags', { postProcess: 'sentenceCase' })}</h3>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<h3>{tags.length}</h3>
|
||||||
|
</td>
|
||||||
|
</tr>,
|
||||||
|
].concat(tags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleParticipants = (item: Album | Song, t: TFunction) => {
|
||||||
if (item.participants) {
|
if (item.participants) {
|
||||||
return Object.entries(item.participants).map(([role, participants]) => {
|
const participants = Object.entries(item.participants).map(([role, participants]) => {
|
||||||
return (
|
return (
|
||||||
<tr key={role}>
|
<tr key={role}>
|
||||||
<td>
|
<td>
|
||||||
|
|
@ -290,6 +322,23 @@ const handleParticipants = (item: Album | Song) => {
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (participants.length) {
|
||||||
|
return [
|
||||||
|
<tr key="participants">
|
||||||
|
<td>
|
||||||
|
<h3>
|
||||||
|
{t('common.additionalParticipants', {
|
||||||
|
postProcess: 'sentenceCase',
|
||||||
|
})}
|
||||||
|
</h3>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<h3>{participants.length}</h3>
|
||||||
|
</td>
|
||||||
|
</tr>,
|
||||||
|
].concat(participants);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return [];
|
return [];
|
||||||
|
|
@ -302,14 +351,16 @@ export const ItemDetailsModal = ({ item }: ItemDetailsModalProps) => {
|
||||||
switch (item.itemType) {
|
switch (item.itemType) {
|
||||||
case LibraryItem.ALBUM:
|
case LibraryItem.ALBUM:
|
||||||
body = AlbumPropertyMapping.map((rule) => handleRow(t, item, rule));
|
body = AlbumPropertyMapping.map((rule) => handleRow(t, item, rule));
|
||||||
body.push(...handleParticipants(item));
|
body.push(...handleParticipants(item, t));
|
||||||
|
body.push(...handleTags(item, t));
|
||||||
break;
|
break;
|
||||||
case LibraryItem.ALBUM_ARTIST:
|
case LibraryItem.ALBUM_ARTIST:
|
||||||
body = AlbumArtistPropertyMapping.map((rule) => handleRow(t, item, rule));
|
body = AlbumArtistPropertyMapping.map((rule) => handleRow(t, item, rule));
|
||||||
break;
|
break;
|
||||||
case LibraryItem.SONG:
|
case LibraryItem.SONG:
|
||||||
body = SongPropertyMapping.map((rule) => handleRow(t, item, rule));
|
body = SongPropertyMapping.map((rule) => handleRow(t, item, rule));
|
||||||
body.push(...handleParticipants(item));
|
body.push(...handleParticipants(item, t));
|
||||||
|
body.push(...handleTags(item, t));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
body = [];
|
body = [];
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue