mirror of
https://github.com/jeffvli/feishin.git
synced 2026-05-09 20:29:36 +02:00
support original date display and sort (#1488)
This commit is contained in:
@@ -27,6 +27,7 @@ import {
|
|||||||
formatDurationString,
|
formatDurationString,
|
||||||
formatRating,
|
formatRating,
|
||||||
} from '/@/renderer/utils/format';
|
} from '/@/renderer/utils/format';
|
||||||
|
import { SEPARATOR_STRING } from '/@/shared/api/utils';
|
||||||
import { Group } from '/@/shared/components/group/group';
|
import { Group } from '/@/shared/components/group/group';
|
||||||
import { Icon } from '/@/shared/components/icon/icon';
|
import { Icon } from '/@/shared/components/icon/icon';
|
||||||
import { Separator } from '/@/shared/components/separator/separator';
|
import { Separator } from '/@/shared/components/separator/separator';
|
||||||
@@ -1054,7 +1055,17 @@ export const getDataRows = (type?: 'compact' | 'default' | 'poster'): DataRow[]
|
|||||||
{
|
{
|
||||||
format: (data) => {
|
format: (data) => {
|
||||||
if ('releaseYear' in data && data.releaseYear !== null) {
|
if ('releaseYear' in data && data.releaseYear !== null) {
|
||||||
return String(data.releaseYear);
|
const releaseYear = data.releaseYear;
|
||||||
|
const originalYear =
|
||||||
|
'originalYear' in data && data.originalYear !== null
|
||||||
|
? data.originalYear
|
||||||
|
: null;
|
||||||
|
|
||||||
|
if (originalYear !== null && originalYear !== releaseYear) {
|
||||||
|
return `♫ ${originalYear}${SEPARATOR_STRING}${releaseYear}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return String(releaseYear);
|
||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
},
|
},
|
||||||
@@ -1063,7 +1074,15 @@ export const getDataRows = (type?: 'compact' | 'default' | 'poster'): DataRow[]
|
|||||||
{
|
{
|
||||||
format: (data) => {
|
format: (data) => {
|
||||||
if ('releaseDate' in data && data.releaseDate) {
|
if ('releaseDate' in data && data.releaseDate) {
|
||||||
return formatDateAbsoluteUTC(data.releaseDate);
|
if (
|
||||||
|
'originalDate' in data &&
|
||||||
|
data.originalDate &&
|
||||||
|
data.originalDate !== data.releaseDate
|
||||||
|
) {
|
||||||
|
return `♫ ${formatDateAbsoluteUTC(data.originalDate)}${SEPARATOR_STRING}${formatDateAbsoluteUTC(data.releaseDate)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${formatDateAbsoluteUTC(data.releaseDate)}`;
|
||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -10,9 +10,11 @@ import {
|
|||||||
formatDateRelative,
|
formatDateRelative,
|
||||||
formatHrDateTime,
|
formatHrDateTime,
|
||||||
} from '/@/renderer/utils/format';
|
} from '/@/renderer/utils/format';
|
||||||
|
import { SEPARATOR_STRING } from '/@/shared/api/utils';
|
||||||
import { Stack } from '/@/shared/components/stack/stack';
|
import { Stack } from '/@/shared/components/stack/stack';
|
||||||
import { Text } from '/@/shared/components/text/text';
|
import { Text } from '/@/shared/components/text/text';
|
||||||
import { Tooltip } from '/@/shared/components/tooltip/tooltip';
|
import { Tooltip } from '/@/shared/components/tooltip/tooltip';
|
||||||
|
import { TableColumn } from '/@/shared/types/types';
|
||||||
|
|
||||||
const getDateTooltipLabel = (utcString: string) => {
|
const getDateTooltipLabel = (utcString: string) => {
|
||||||
return (
|
return (
|
||||||
@@ -54,6 +56,47 @@ export const AbsoluteDateColumn = (props: ItemTableListInnerColumn) => {
|
|||||||
props.columns[props.columnIndex].id
|
props.columns[props.columnIndex].id
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if (props.type === TableColumn.RELEASE_DATE) {
|
||||||
|
const item = (props.data as (any | undefined)[])[props.rowIndex];
|
||||||
|
if (item && 'releaseDate' in item && item.releaseDate) {
|
||||||
|
const releaseDate = item.releaseDate;
|
||||||
|
const originalDate =
|
||||||
|
'originalDate' in item && item.originalDate && item.originalDate !== releaseDate
|
||||||
|
? item.originalDate
|
||||||
|
: null;
|
||||||
|
|
||||||
|
if (originalDate) {
|
||||||
|
const formattedOriginalDate = formatDateAbsoluteUTC(originalDate);
|
||||||
|
const formattedReleaseDate = formatDateAbsoluteUTC(releaseDate);
|
||||||
|
const displayText = `♫ ${formattedOriginalDate}${SEPARATOR_STRING}${formattedReleaseDate}`;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TableColumnTextContainer {...props}>
|
||||||
|
<Tooltip label={getDateTooltipLabel(releaseDate)} multiline={false}>
|
||||||
|
<span>{displayText}</span>
|
||||||
|
</Tooltip>
|
||||||
|
</TableColumnTextContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof releaseDate === 'string' && releaseDate) {
|
||||||
|
return (
|
||||||
|
<TableColumnTextContainer {...props}>
|
||||||
|
<Tooltip label={getDateTooltipLabel(releaseDate)} multiline={false}>
|
||||||
|
<span>{formatDateAbsoluteUTC(releaseDate)}</span>
|
||||||
|
</Tooltip>
|
||||||
|
</TableColumnTextContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (row === null) {
|
||||||
|
return <ColumnNullFallback {...props} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <ColumnSkeletonFixed {...props} />;
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof row === 'string' && row) {
|
if (typeof row === 'string' && row) {
|
||||||
return (
|
return (
|
||||||
<TableColumnTextContainer {...props}>
|
<TableColumnTextContainer {...props}>
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import {
|
||||||
|
ColumnNullFallback,
|
||||||
|
ColumnSkeletonFixed,
|
||||||
|
ItemTableListInnerColumn,
|
||||||
|
TableColumnTextContainer,
|
||||||
|
} from '/@/renderer/components/item-list/item-table-list/item-table-list-column';
|
||||||
|
import { SEPARATOR_STRING } from '/@/shared/api/utils';
|
||||||
|
|
||||||
|
export const YearColumn = (props: ItemTableListInnerColumn) => {
|
||||||
|
const item = (props.data as (any | undefined)[])[props.rowIndex];
|
||||||
|
|
||||||
|
if (item && 'releaseYear' in item && item.releaseYear !== null) {
|
||||||
|
const releaseYear = item.releaseYear;
|
||||||
|
const originalYear =
|
||||||
|
'originalYear' in item && item.originalYear !== null ? item.originalYear : null;
|
||||||
|
|
||||||
|
if (originalYear !== null && originalYear !== releaseYear) {
|
||||||
|
return (
|
||||||
|
<TableColumnTextContainer {...props}>
|
||||||
|
♫ {originalYear}
|
||||||
|
{SEPARATOR_STRING}
|
||||||
|
{releaseYear}
|
||||||
|
</TableColumnTextContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof releaseYear === 'number') {
|
||||||
|
return <TableColumnTextContainer {...props}>{releaseYear}</TableColumnTextContainer>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const row: number | undefined = (props.data as (any | undefined)[])[props.rowIndex]?.[
|
||||||
|
props.columns[props.columnIndex].id
|
||||||
|
];
|
||||||
|
|
||||||
|
if (row === null) {
|
||||||
|
return <ColumnNullFallback {...props} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <ColumnSkeletonFixed {...props} />;
|
||||||
|
};
|
||||||
@@ -110,7 +110,7 @@ export const SONG_TABLE_COLUMNS: DefaultTableColumn[] = [
|
|||||||
label: i18n.t('table.config.label.year', { postProcess: 'titleCase' }),
|
label: i18n.t('table.config.label.year', { postProcess: 'titleCase' }),
|
||||||
pinned: null,
|
pinned: null,
|
||||||
value: TableColumn.YEAR,
|
value: TableColumn.YEAR,
|
||||||
width: 100,
|
width: 200,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
align: 'center',
|
align: 'center',
|
||||||
@@ -119,7 +119,7 @@ export const SONG_TABLE_COLUMNS: DefaultTableColumn[] = [
|
|||||||
label: i18n.t('table.config.label.releaseDate', { postProcess: 'titleCase' }),
|
label: i18n.t('table.config.label.releaseDate', { postProcess: 'titleCase' }),
|
||||||
pinned: null,
|
pinned: null,
|
||||||
value: TableColumn.RELEASE_DATE,
|
value: TableColumn.RELEASE_DATE,
|
||||||
width: 120,
|
width: 240,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
align: 'center',
|
align: 'center',
|
||||||
@@ -376,7 +376,7 @@ export const ALBUM_TABLE_COLUMNS: DefaultTableColumn[] = [
|
|||||||
label: i18n.t('table.config.label.year', { postProcess: 'titleCase' }),
|
label: i18n.t('table.config.label.year', { postProcess: 'titleCase' }),
|
||||||
pinned: null,
|
pinned: null,
|
||||||
value: TableColumn.YEAR,
|
value: TableColumn.YEAR,
|
||||||
width: 100,
|
width: 200,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
align: 'center',
|
align: 'center',
|
||||||
@@ -385,7 +385,7 @@ export const ALBUM_TABLE_COLUMNS: DefaultTableColumn[] = [
|
|||||||
label: i18n.t('table.config.label.releaseDate', { postProcess: 'titleCase' }),
|
label: i18n.t('table.config.label.releaseDate', { postProcess: 'titleCase' }),
|
||||||
pinned: null,
|
pinned: null,
|
||||||
value: TableColumn.RELEASE_DATE,
|
value: TableColumn.RELEASE_DATE,
|
||||||
width: 120,
|
width: 240,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
align: 'center',
|
align: 'center',
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ import { SizeColumn } from '/@/renderer/components/item-list/item-table-list/col
|
|||||||
import { TextColumn } from '/@/renderer/components/item-list/item-table-list/columns/text-column';
|
import { TextColumn } from '/@/renderer/components/item-list/item-table-list/columns/text-column';
|
||||||
import { TitleColumn } from '/@/renderer/components/item-list/item-table-list/columns/title-column';
|
import { TitleColumn } from '/@/renderer/components/item-list/item-table-list/columns/title-column';
|
||||||
import { TitleCombinedColumn } from '/@/renderer/components/item-list/item-table-list/columns/title-combined-column';
|
import { TitleCombinedColumn } from '/@/renderer/components/item-list/item-table-list/columns/title-combined-column';
|
||||||
|
import { YearColumn } from '/@/renderer/components/item-list/item-table-list/columns/year-column';
|
||||||
import { TableItemProps } from '/@/renderer/components/item-list/item-table-list/item-table-list';
|
import { TableItemProps } from '/@/renderer/components/item-list/item-table-list/item-table-list';
|
||||||
import { ItemControls, ItemListItem } from '/@/renderer/components/item-list/types';
|
import { ItemControls, ItemListItem } from '/@/renderer/components/item-list/types';
|
||||||
import { eventEmitter } from '/@/renderer/events/event-emitter';
|
import { eventEmitter } from '/@/renderer/events/event-emitter';
|
||||||
@@ -487,9 +488,11 @@ export const ItemTableListColumn = (props: ItemTableListColumn) => {
|
|||||||
case TableColumn.DISC_NUMBER:
|
case TableColumn.DISC_NUMBER:
|
||||||
case TableColumn.SAMPLE_RATE:
|
case TableColumn.SAMPLE_RATE:
|
||||||
case TableColumn.TRACK_NUMBER:
|
case TableColumn.TRACK_NUMBER:
|
||||||
case TableColumn.YEAR:
|
|
||||||
return <NumericColumn {...props} {...dragProps} controls={controls} type={type} />;
|
return <NumericColumn {...props} {...dragProps} controls={controls} type={type} />;
|
||||||
|
|
||||||
|
case TableColumn.YEAR:
|
||||||
|
return <YearColumn {...props} {...dragProps} controls={controls} type={type} />;
|
||||||
|
|
||||||
case TableColumn.DATE_ADDED:
|
case TableColumn.DATE_ADDED:
|
||||||
return <DateColumn {...props} {...dragProps} controls={controls} type={type} />;
|
return <DateColumn {...props} {...dragProps} controls={controls} type={type} />;
|
||||||
|
|
||||||
|
|||||||
@@ -238,7 +238,7 @@ const normalizeSong = (
|
|||||||
peak: null,
|
peak: null,
|
||||||
playCount: (item.UserData && item.UserData.PlayCount) || 0,
|
playCount: (item.UserData && item.UserData.PlayCount) || 0,
|
||||||
playlistItemId: item.PlaylistItemId,
|
playlistItemId: item.PlaylistItemId,
|
||||||
releaseDate: item.PremiereDate ? item.PremiereDate : null,
|
releaseDate: item.PremiereDate || null,
|
||||||
releaseYear: item.ProductionYear || null,
|
releaseYear: item.ProductionYear || null,
|
||||||
sampleRate,
|
sampleRate,
|
||||||
size,
|
size,
|
||||||
@@ -302,7 +302,8 @@ const normalizeAlbum = (
|
|||||||
lastPlayedAt: null,
|
lastPlayedAt: null,
|
||||||
mbzId: item.ProviderIds?.MusicBrainzAlbum || null,
|
mbzId: item.ProviderIds?.MusicBrainzAlbum || null,
|
||||||
name: item.Name,
|
name: item.Name,
|
||||||
originalDate: null,
|
originalDate: item.PremiereDate || null,
|
||||||
|
originalYear: item.ProductionYear || null,
|
||||||
participants: getPeople(item),
|
participants: getPeople(item),
|
||||||
playCount: item.UserData?.PlayCount || 0,
|
playCount: item.UserData?.PlayCount || 0,
|
||||||
recordLabels: item.Studios?.map((entry) => entry.Name) || [],
|
recordLabels: item.Studios?.map((entry) => entry.Name) || [],
|
||||||
|
|||||||
@@ -49,6 +49,26 @@ const normalizeReleaseDate = (item: { date?: string; releaseDate?: string }) =>
|
|||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const normalizeOriginalDate = (item: {
|
||||||
|
date?: string;
|
||||||
|
originalDate?: string;
|
||||||
|
releaseDate?: string;
|
||||||
|
}) => {
|
||||||
|
if (item.originalDate && matchesFullDate(item.originalDate)) {
|
||||||
|
return item.originalDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.releaseDate && matchesFullDate(item.releaseDate)) {
|
||||||
|
return item.releaseDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.date && matchesFullDate(item.date)) {
|
||||||
|
return item.date;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
const getArtists = (
|
const getArtists = (
|
||||||
item:
|
item:
|
||||||
| z.infer<typeof ndType._response.album>
|
| z.infer<typeof ndType._response.album>
|
||||||
@@ -282,6 +302,11 @@ const normalizeAlbum = (
|
|||||||
pathReplace?: string,
|
pathReplace?: string,
|
||||||
pathReplaceWith?: string,
|
pathReplaceWith?: string,
|
||||||
): Album => {
|
): Album => {
|
||||||
|
const releaseDate = normalizeReleaseDate(item);
|
||||||
|
const releaseYear = releaseDate ? parseInt(releaseDate.split('-')[0]) : null;
|
||||||
|
const originalDate = normalizeOriginalDate(item);
|
||||||
|
const originalYear = originalDate ? parseInt(originalDate.split('-')[0]) : null;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...parseAlbumTags(item),
|
...parseAlbumTags(item),
|
||||||
...getArtists(item, false),
|
...getArtists(item, false),
|
||||||
@@ -316,11 +341,12 @@ const normalizeAlbum = (
|
|||||||
lastPlayedAt: normalizePlayDate(item),
|
lastPlayedAt: normalizePlayDate(item),
|
||||||
mbzId: item.mbzAlbumId || null,
|
mbzId: item.mbzAlbumId || null,
|
||||||
name: item.name,
|
name: item.name,
|
||||||
originalDate: item.originalDate || null,
|
originalDate,
|
||||||
|
originalYear,
|
||||||
playCount: item.playCount || 0,
|
playCount: item.playCount || 0,
|
||||||
releaseDate: normalizeReleaseDate(item),
|
releaseDate,
|
||||||
releaseType: item.mbzAlbumType || null,
|
releaseType: item.mbzAlbumType || null,
|
||||||
releaseYear: item.maxYear || null,
|
releaseYear,
|
||||||
size: item.size,
|
size: item.size,
|
||||||
songCount: item.songCount,
|
songCount: item.songCount,
|
||||||
songs: item.songs
|
songs: item.songs
|
||||||
|
|||||||
@@ -458,17 +458,18 @@ const album = z.object({
|
|||||||
libraryId: z.number(),
|
libraryId: z.number(),
|
||||||
libraryName: z.string(),
|
libraryName: z.string(),
|
||||||
libraryPath: z.string(),
|
libraryPath: z.string(),
|
||||||
|
maxOriginalYear: z.number().optional(),
|
||||||
maxYear: z.number(),
|
maxYear: z.number(),
|
||||||
mbzAlbumArtistId: z.string().optional(),
|
mbzAlbumArtistId: z.string().optional(),
|
||||||
mbzAlbumId: z.string().optional(),
|
mbzAlbumId: z.string().optional(),
|
||||||
mbzAlbumType: z.string().optional(),
|
mbzAlbumType: z.string().optional(),
|
||||||
mbzReleaseGroupId: z.string().optional(),
|
mbzReleaseGroupId: z.string().optional(),
|
||||||
|
minOriginalYear: z.number().optional(),
|
||||||
minYear: z.number(),
|
minYear: z.number(),
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
orderAlbumArtistName: z.string(),
|
orderAlbumArtistName: z.string(),
|
||||||
orderAlbumName: z.string(),
|
orderAlbumName: z.string(),
|
||||||
originalDate: z.string().optional(),
|
originalDate: z.string().optional(),
|
||||||
originalYear: z.number().optional(),
|
|
||||||
participants: z.optional(participants),
|
participants: z.optional(participants),
|
||||||
playCount: z.number().optional(),
|
playCount: z.number().optional(),
|
||||||
playDate: z.string().optional(),
|
playDate: z.string().optional(),
|
||||||
|
|||||||
@@ -265,6 +265,14 @@ const normalizeAlbum = (
|
|||||||
pathReplace?: string,
|
pathReplace?: string,
|
||||||
pathReplaceWith?: string,
|
pathReplaceWith?: string,
|
||||||
): Album => {
|
): Album => {
|
||||||
|
const releaseDate =
|
||||||
|
item.releaseDate &&
|
||||||
|
typeof item.releaseDate.year === 'number' &&
|
||||||
|
typeof item.releaseDate.month === 'number' &&
|
||||||
|
typeof item.releaseDate.day === 'number'
|
||||||
|
? `${item.releaseDate.year}-${item.releaseDate.month}-${item.releaseDate.day}`
|
||||||
|
: null;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
_itemType: LibraryItem.ALBUM,
|
_itemType: LibraryItem.ALBUM,
|
||||||
_serverId: server?.id || 'unknown',
|
_serverId: server?.id || 'unknown',
|
||||||
@@ -289,17 +297,12 @@ const normalizeAlbum = (
|
|||||||
lastPlayedAt: null,
|
lastPlayedAt: null,
|
||||||
mbzId: null,
|
mbzId: null,
|
||||||
name: item.name,
|
name: item.name,
|
||||||
originalDate: null,
|
originalDate: releaseDate,
|
||||||
|
originalYear: item.year || null,
|
||||||
participants: getParticipants(item),
|
participants: getParticipants(item),
|
||||||
playCount: null,
|
playCount: null,
|
||||||
recordLabels: item.recordLabels?.map((item) => item.name) || [],
|
recordLabels: item.recordLabels?.map((item) => item.name) || [],
|
||||||
releaseDate:
|
releaseDate,
|
||||||
item.releaseDate &&
|
|
||||||
typeof item.releaseDate.year === 'number' &&
|
|
||||||
typeof item.releaseDate.month === 'number' &&
|
|
||||||
typeof item.releaseDate.day === 'number'
|
|
||||||
? `${item.releaseDate.year}-${item.releaseDate.month}-${item.releaseDate.day}`
|
|
||||||
: null,
|
|
||||||
releaseType: getReleaseType(item),
|
releaseType: getReleaseType(item),
|
||||||
releaseTypes: item.releaseTypes || [],
|
releaseTypes: item.releaseTypes || [],
|
||||||
releaseYear: item.year || null,
|
releaseYear: item.year || null,
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ export const getClientType = (): string => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SEPARATOR_STRING = ' · ';
|
export const SEPARATOR_STRING = ' • ';
|
||||||
|
|
||||||
export const sortSongList = (songs: Song[], sortBy: SongListSort, sortOrder: SortOrder) => {
|
export const sortSongList = (songs: Song[], sortBy: SongListSort, sortOrder: SortOrder) => {
|
||||||
let results: Song[] = songs;
|
let results: Song[] = songs;
|
||||||
@@ -419,13 +419,13 @@ export const sortAlbumList = (albums: Album[], sortBy: AlbumListSort, sortOrder:
|
|||||||
results,
|
results,
|
||||||
[
|
[
|
||||||
(v) => {
|
(v) => {
|
||||||
if (v.releaseDate) {
|
if (v.originalDate) {
|
||||||
return new Date(v.releaseDate).getTime();
|
return new Date(v.originalDate).getTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback to the first day of the release year
|
// Fallback to the first day of the release year
|
||||||
if (v.releaseYear) {
|
if (v.originalYear) {
|
||||||
return new Date(v.releaseYear, 0, 1).getTime();
|
return new Date(v.originalYear, 0, 1).getTime();
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -182,6 +182,7 @@ export type Album = {
|
|||||||
mbzId: null | string;
|
mbzId: null | string;
|
||||||
name: string;
|
name: string;
|
||||||
originalDate: null | string;
|
originalDate: null | string;
|
||||||
|
originalYear: null | number;
|
||||||
participants: null | Record<string, RelatedArtist[]>;
|
participants: null | Record<string, RelatedArtist[]>;
|
||||||
playCount: null | number;
|
playCount: null | number;
|
||||||
recordLabels: string[];
|
recordLabels: string[];
|
||||||
|
|||||||
Reference in New Issue
Block a user