diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json
index ac60ebc58..15c2eaf1c 100644
--- a/src/i18n/locales/en.json
+++ b/src/i18n/locales/en.json
@@ -1086,6 +1086,7 @@
"bpm": "$t(common.bpm)",
"channels": "$t(common.channel_other)",
"codec": "$t(common.codec)",
+ "composer": "composer",
"dateAdded": "date added",
"discNumber": "disc number",
"duration": "$t(common.duration)",
diff --git a/src/renderer/components/item-list/item-table-list/columns/composer-column.module.css b/src/renderer/components/item-list/item-table-list/columns/composer-column.module.css
new file mode 100644
index 000000000..0fcce5237
--- /dev/null
+++ b/src/renderer/components/item-list/item-table-list/columns/composer-column.module.css
@@ -0,0 +1,16 @@
+.composers-container {
+ display: -webkit-box;
+ overflow: hidden;
+ -webkit-line-clamp: 2;
+ -webkit-box-orient: vertical;
+ color: var(--theme-colors-foreground-muted);
+ user-select: none;
+}
+
+.composers-container.compact {
+ -webkit-line-clamp: 1;
+}
+
+.composers-container.large {
+ -webkit-line-clamp: 3;
+}
diff --git a/src/renderer/components/item-list/item-table-list/columns/composer-column.tsx b/src/renderer/components/item-list/item-table-list/columns/composer-column.tsx
new file mode 100644
index 000000000..c655bb1e2
--- /dev/null
+++ b/src/renderer/components/item-list/item-table-list/columns/composer-column.tsx
@@ -0,0 +1,50 @@
+import clsx from 'clsx';
+import { memo } from 'react';
+
+import styles from './composer-column.module.css';
+
+import {
+ ColumnNullFallback,
+ ColumnSkeletonVariable,
+ ItemTableListInnerColumn,
+ TableColumnContainer,
+} from '/@/renderer/components/item-list/item-table-list/item-table-list-column';
+import { JoinedArtists } from '/@/renderer/features/albums/components/joined-artists';
+import { Album, RelatedArtist, Song } from '/@/shared/types/domain-types';
+
+const ComposerColumn = (props: ItemTableListInnerColumn) => {
+ const rowItem = props.getRowItem?.(props.rowIndex) ?? (props.data as any[])[props.rowIndex];
+ const item = rowItem as Album | Song | undefined;
+
+ const composers = item?.participants?.composer || [];
+
+ if (composers && Array.isArray(composers) && composers.length > 0) {
+ return (
+
+
+
+
+
+ );
+ }
+
+ if (composers?.length === 0 || item === null || item === undefined) {
+ return ;
+ }
+
+ return ;
+};
+
+export const ComposerColumnMemo = memo(ComposerColumn);
+
+export { ComposerColumnMemo as ComposerColumn };
diff --git a/src/renderer/components/item-list/item-table-list/default-columns.ts b/src/renderer/components/item-list/item-table-list/default-columns.ts
index e35a0dd40..fdba40291 100644
--- a/src/renderer/components/item-list/item-table-list/default-columns.ts
+++ b/src/renderer/components/item-list/item-table-list/default-columns.ts
@@ -94,6 +94,15 @@ export const SONG_TABLE_COLUMNS: DefaultTableColumn[] = [
value: TableColumn.ARTIST,
width: 300,
},
+ {
+ align: 'start',
+ autoSize: false,
+ isEnabled: false,
+ label: i18n.t('table.config.label.composer', { postProcess: 'titleCase' }),
+ pinned: null,
+ value: TableColumn.COMPOSER,
+ width: 300,
+ },
{
align: 'start',
autoSize: false,
@@ -360,6 +369,15 @@ export const ALBUM_TABLE_COLUMNS: DefaultTableColumn[] = [
value: TableColumn.ARTIST,
width: 300,
},
+ {
+ align: 'start',
+ autoSize: false,
+ isEnabled: false,
+ label: i18n.t('table.config.label.composer', { postProcess: 'titleCase' }),
+ pinned: null,
+ value: TableColumn.COMPOSER,
+ width: 300,
+ },
{
align: 'center',
autoSize: false,
diff --git a/src/renderer/components/item-list/item-table-list/item-table-list-column.tsx b/src/renderer/components/item-list/item-table-list/item-table-list-column.tsx
index 43a3a062a..4985577c1 100644
--- a/src/renderer/components/item-list/item-table-list/item-table-list-column.tsx
+++ b/src/renderer/components/item-list/item-table-list/item-table-list-column.tsx
@@ -26,6 +26,7 @@ import { ActionsColumn } from '/@/renderer/components/item-list/item-table-list/
import { AlbumArtistsColumn } from '/@/renderer/components/item-list/item-table-list/columns/album-artists-column';
import { AlbumColumn } from '/@/renderer/components/item-list/item-table-list/columns/album-column';
import { ArtistsColumn } from '/@/renderer/components/item-list/item-table-list/columns/artists-column';
+import { ComposerColumn } from '/@/renderer/components/item-list/item-table-list/columns/composer-column';
import { CountColumn } from '/@/renderer/components/item-list/item-table-list/columns/count-column';
import {
AbsoluteDateColumn,
@@ -482,6 +483,9 @@ export const ItemTableListColumn = (props: ItemTableListColumn) => {
case TableColumn.TRACK_NUMBER:
return ;
+ case TableColumn.COMPOSER:
+ return ;
+
case TableColumn.DATE_ADDED:
return ;
@@ -1126,6 +1130,9 @@ const columnLabelMap: Record = {
[TableColumn.CHANNELS]: i18n.t('table.column.channels', { postProcess: 'upperCase' }) as string,
[TableColumn.CODEC]: i18n.t('table.column.codec', { postProcess: 'upperCase' }) as string,
[TableColumn.COMMENT]: i18n.t('table.column.comment', { postProcess: 'upperCase' }) as string,
+ [TableColumn.COMPOSER]: i18n.t('table.config.label.composer', {
+ postProcess: 'upperCase',
+ }) as string,
[TableColumn.DATE_ADDED]: i18n.t('table.column.dateAdded', {
postProcess: 'upperCase',
}) as string,
diff --git a/src/renderer/store/settings.store.ts b/src/renderer/store/settings.store.ts
index b66fe3daa..e7463c0e0 100644
--- a/src/renderer/store/settings.store.ts
+++ b/src/renderer/store/settings.store.ts
@@ -1941,10 +1941,52 @@ export const useSettingsStore = createWithEqualityFn()(
});
}
+ if (version <= 21) {
+ // Add COMPOSER column to SONG and ALBUM table configs
+ const composerColumn: ItemTableListColumnConfig = {
+ align: 'start',
+ autoSize: false,
+ id: TableColumn.COMPOSER,
+ isEnabled: false,
+ pinned: null,
+ width: 300,
+ };
+
+ const listKeysToUpdate: (LibraryItem | string)[] = [
+ LibraryItem.SONG,
+ LibraryItem.ALBUM,
+ LibraryItem.PLAYLIST_SONG,
+ LibraryItem.QUEUE_SONG,
+ ItemListKey.ALBUM_DETAIL,
+ ItemListKey.FULL_SCREEN,
+ ItemListKey.SIDE_QUEUE,
+ ];
+
+ listKeysToUpdate.forEach((listKey) => {
+ const listConfig = state.lists[listKey];
+ if (listConfig?.table?.columns) {
+ const columns = listConfig.table.columns;
+ const hasComposer = columns.some(
+ (col) => col.id === TableColumn.COMPOSER,
+ );
+ if (!hasComposer) {
+ const artistIndex = columns.findIndex(
+ (col) => col.id === TableColumn.ARTIST,
+ );
+ if (artistIndex >= 0) {
+ columns.splice(artistIndex + 1, 0, composerColumn);
+ } else {
+ columns.push(composerColumn);
+ }
+ }
+ }
+ });
+ }
+
return persistedState;
},
name: 'store_settings',
- version: 21,
+ version: 22,
},
),
);
diff --git a/src/shared/types/types.ts b/src/shared/types/types.ts
index 0aa56b385..9a46079a7 100644
--- a/src/shared/types/types.ts
+++ b/src/shared/types/types.ts
@@ -168,6 +168,7 @@ export enum TableColumn {
CHANNELS = 'channels',
CODEC = 'container',
COMMENT = 'comment',
+ COMPOSER = 'composer',
DATE_ADDED = 'createdAt',
DISC_NUMBER = 'discNumber',
DURATION = 'duration',