mirror of
https://github.com/antebudimir/feishin.git
synced 2025-12-31 18:13:31 +00:00
Merge branch 'development' into react-image-lazy-loaded
This commit is contained in:
commit
1aac1a6361
193 changed files with 2003 additions and 2154 deletions
|
|
@ -170,6 +170,47 @@ const normalizeSong = (
|
|||
deviceId: string,
|
||||
imageSize?: number,
|
||||
): Song => {
|
||||
let bitRate = 0;
|
||||
let channels: null | number = null;
|
||||
let container: null | string = null;
|
||||
let path: null | string = null;
|
||||
let sampleRate: null | number = null;
|
||||
let size = 0;
|
||||
let streamUrl = '';
|
||||
|
||||
if (item.MediaSources?.length) {
|
||||
const source = item.MediaSources[0];
|
||||
|
||||
container = source.Container;
|
||||
path = source.Path;
|
||||
size = source.Size;
|
||||
|
||||
streamUrl = getStreamUrl({
|
||||
container: container,
|
||||
deviceId,
|
||||
eTag: source.ETag,
|
||||
id: item.Id,
|
||||
mediaSourceId: source.Id,
|
||||
server,
|
||||
});
|
||||
|
||||
if ((source.MediaStreams?.length || 0) > 0) {
|
||||
for (const stream of source.MediaStreams) {
|
||||
if (stream.Type === 'Audio') {
|
||||
bitRate =
|
||||
stream.BitRate !== undefined
|
||||
? Number(Math.trunc(stream.BitRate / 1000))
|
||||
: 0;
|
||||
channels = stream.Channels || null;
|
||||
sampleRate = stream.SampleRate || null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.warn('Jellyfin song retrieved with no media sources', item);
|
||||
}
|
||||
|
||||
return {
|
||||
album: item.Album,
|
||||
albumArtists: item.AlbumArtists?.map((entry) => ({
|
||||
|
|
@ -184,14 +225,13 @@ const normalizeSong = (
|
|||
imageUrl: null,
|
||||
name: entry.Name,
|
||||
})),
|
||||
bitRate:
|
||||
item.MediaSources?.[0].Bitrate &&
|
||||
Number(Math.trunc(item.MediaSources[0].Bitrate / 1000)),
|
||||
bitDepth: null,
|
||||
bitRate,
|
||||
bpm: null,
|
||||
channels: null,
|
||||
channels,
|
||||
comment: null,
|
||||
compilation: null,
|
||||
container: (item.MediaSources && item.MediaSources[0]?.Container) || null,
|
||||
container,
|
||||
createdAt: item.DateCreated,
|
||||
discNumber: (item.ParentIndexNumber && item.ParentIndexNumber) || 1,
|
||||
discSubtitle: null,
|
||||
|
|
@ -220,7 +260,7 @@ const normalizeSong = (
|
|||
lyrics: null,
|
||||
name: item.Name,
|
||||
participants: getPeople(item),
|
||||
path: (item.MediaSources && item.MediaSources[0]?.Path) || null,
|
||||
path,
|
||||
peak: null,
|
||||
playCount: (item.UserData && item.UserData.PlayCount) || 0,
|
||||
playlistItemId: item.PlaylistItemId,
|
||||
|
|
@ -230,17 +270,11 @@ const normalizeSong = (
|
|||
? new Date(item.ProductionYear, 0, 1).toISOString()
|
||||
: null,
|
||||
releaseYear: item.ProductionYear ? String(item.ProductionYear) : null,
|
||||
sampleRate,
|
||||
serverId: server?.id || '',
|
||||
serverType: ServerType.JELLYFIN,
|
||||
size: item.MediaSources && item.MediaSources[0]?.Size,
|
||||
streamUrl: getStreamUrl({
|
||||
container: item.MediaSources?.[0]?.Container,
|
||||
deviceId,
|
||||
eTag: item.MediaSources?.[0]?.ETag,
|
||||
id: item.Id,
|
||||
mediaSourceId: item.MediaSources?.[0]?.Id,
|
||||
server,
|
||||
}),
|
||||
size,
|
||||
streamUrl,
|
||||
tags: getTags(item),
|
||||
trackNumber: item.IndexNumber,
|
||||
uniqueId: nanoid(),
|
||||
|
|
|
|||
|
|
@ -148,6 +148,7 @@ const normalizeSong = (
|
|||
albumId: item.albumId,
|
||||
...getArtists(item),
|
||||
artistName: item.artist,
|
||||
bitDepth: item.bitDepth || null,
|
||||
bitRate: item.bitRate,
|
||||
bpm: item.bpm ? item.bpm : null,
|
||||
channels: item.channels ? item.channels : null,
|
||||
|
|
@ -189,6 +190,7 @@ const normalizeSong = (
|
|||
: new Date(Date.UTC(item.year, 0, 1))
|
||||
).toISOString(),
|
||||
releaseYear: String(item.year),
|
||||
sampleRate: item.sampleRate || null,
|
||||
serverId: server?.id || 'unknown',
|
||||
serverType: ServerType.NAVIDROME,
|
||||
size: item.size,
|
||||
|
|
|
|||
|
|
@ -184,6 +184,7 @@ const song = z.object({
|
|||
albumId: z.string(),
|
||||
artist: z.string(),
|
||||
artistId: z.string(),
|
||||
bitDepth: z.number().optional(),
|
||||
bitRate: z.number(),
|
||||
bookmarkPosition: z.number(),
|
||||
bpm: z.number().optional(),
|
||||
|
|
@ -226,6 +227,7 @@ const song = z.object({
|
|||
rgAlbumPeak: z.number().optional(),
|
||||
rgTrackGain: z.number().optional(),
|
||||
rgTrackPeak: z.number().optional(),
|
||||
sampleRate: z.number(),
|
||||
size: z.number(),
|
||||
smallImageUrl: z.string().optional(),
|
||||
sortAlbumArtistName: z.string(),
|
||||
|
|
|
|||
|
|
@ -135,9 +135,10 @@ const normalizeSong = (
|
|||
albumId: item.albumId?.toString() || '',
|
||||
artistName: item.artist || '',
|
||||
artists: getArtistList(item.artists, item.artistId, item.artist),
|
||||
bitDepth: item.bitDepth || null,
|
||||
bitRate: item.bitRate || 0,
|
||||
bpm: item.bpm || null,
|
||||
channels: null,
|
||||
channels: item.channelCount || null,
|
||||
comment: null,
|
||||
compilation: null,
|
||||
container: item.contentType,
|
||||
|
|
@ -172,6 +173,7 @@ const normalizeSong = (
|
|||
playCount: item?.playCount || 0,
|
||||
releaseDate: null,
|
||||
releaseYear: item.year ? String(item.year) : null,
|
||||
sampleRate: item.samplingRate || null,
|
||||
serverId: server?.id || 'unknown',
|
||||
serverType: ServerType.SUBSONIC,
|
||||
size: item.size,
|
||||
|
|
|
|||
|
|
@ -85,8 +85,10 @@ const song = z.object({
|
|||
artistId: id.optional(),
|
||||
artists: z.array(simpleArtist),
|
||||
averageRating: z.number().optional(),
|
||||
bitDepth: z.number().optional(),
|
||||
bitRate: z.number().optional(),
|
||||
bpm: z.number().optional(),
|
||||
channelCount: z.number().optional(),
|
||||
contentType: z.string(),
|
||||
contributors: z.array(contributor).optional(),
|
||||
coverArt: z.string().optional(),
|
||||
|
|
@ -103,6 +105,7 @@ const song = z.object({
|
|||
path: z.string(),
|
||||
playCount: z.number().optional(),
|
||||
replayGain: songGain.optional(),
|
||||
samplingRate: z.number().optional(),
|
||||
size: z.number(),
|
||||
starred: z.boolean().optional(),
|
||||
suffix: z.string(),
|
||||
|
|
|
|||
|
|
@ -17,12 +17,7 @@ export interface AccordionProps
|
|||
export const Accordion = ({ children, classNames, ...props }: AccordionProps) => {
|
||||
return (
|
||||
<MantineAccordion
|
||||
chevron={
|
||||
<Icon
|
||||
icon="arrowUpS"
|
||||
size="lg"
|
||||
/>
|
||||
}
|
||||
chevron={<Icon icon="arrowUpS" size="lg" />}
|
||||
classNames={{
|
||||
chevron: styles.chevron,
|
||||
control: styles.control,
|
||||
|
|
|
|||
|
|
@ -45,19 +45,9 @@ const _ActionIcon = forwardRef<HTMLButtonElement, ActionIconProps>(
|
|||
|
||||
if (tooltip && icon) {
|
||||
return (
|
||||
<Tooltip
|
||||
withinPortal
|
||||
{...tooltip}
|
||||
>
|
||||
<MantineActionIcon
|
||||
ref={ref}
|
||||
{...actionIconProps}
|
||||
>
|
||||
<Icon
|
||||
icon={icon}
|
||||
size={actionIconProps.size}
|
||||
{...iconProps}
|
||||
/>
|
||||
<Tooltip withinPortal {...tooltip}>
|
||||
<MantineActionIcon ref={ref} {...actionIconProps}>
|
||||
<Icon icon={icon} size={actionIconProps.size} {...iconProps} />
|
||||
</MantineActionIcon>
|
||||
</Tooltip>
|
||||
);
|
||||
|
|
@ -65,29 +55,16 @@ const _ActionIcon = forwardRef<HTMLButtonElement, ActionIconProps>(
|
|||
|
||||
if (icon) {
|
||||
return (
|
||||
<MantineActionIcon
|
||||
ref={ref}
|
||||
{...actionIconProps}
|
||||
>
|
||||
<Icon
|
||||
icon={icon}
|
||||
size={actionIconProps.size}
|
||||
{...iconProps}
|
||||
/>
|
||||
<MantineActionIcon ref={ref} {...actionIconProps}>
|
||||
<Icon icon={icon} size={actionIconProps.size} {...iconProps} />
|
||||
</MantineActionIcon>
|
||||
);
|
||||
}
|
||||
|
||||
if (tooltip) {
|
||||
return (
|
||||
<Tooltip
|
||||
withinPortal
|
||||
{...tooltip}
|
||||
>
|
||||
<MantineActionIcon
|
||||
ref={ref}
|
||||
{...actionIconProps}
|
||||
>
|
||||
<Tooltip withinPortal {...tooltip}>
|
||||
<MantineActionIcon ref={ref} {...actionIconProps}>
|
||||
{children}
|
||||
</MantineActionIcon>
|
||||
</Tooltip>
|
||||
|
|
@ -95,10 +72,7 @@ const _ActionIcon = forwardRef<HTMLButtonElement, ActionIconProps>(
|
|||
}
|
||||
|
||||
return (
|
||||
<MantineActionIcon
|
||||
ref={ref}
|
||||
{...actionIconProps}
|
||||
>
|
||||
<MantineActionIcon ref={ref} {...actionIconProps}>
|
||||
{children}
|
||||
</MantineActionIcon>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -44,10 +44,7 @@ export const _Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
|||
) => {
|
||||
if (tooltip) {
|
||||
return (
|
||||
<Tooltip
|
||||
withinPortal
|
||||
{...tooltip}
|
||||
>
|
||||
<Tooltip withinPortal {...tooltip}>
|
||||
<MantineButton
|
||||
autoContrast
|
||||
classNames={{
|
||||
|
|
@ -145,10 +142,7 @@ export const TimeoutButton = ({ timeoutProps, ...props }: TimeoutButtonProps) =>
|
|||
}, [clear, isRunning, start]);
|
||||
|
||||
return (
|
||||
<Button
|
||||
onClick={startTimeout}
|
||||
{...props}
|
||||
>
|
||||
<Button onClick={startTimeout} {...props}>
|
||||
{isRunning ? 'Cancel' : props.children}
|
||||
</Button>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -44,10 +44,7 @@ export const DropdownMenu = ({ children, ...props }: MenuProps) => {
|
|||
|
||||
const MenuLabel = ({ children, ...props }: MenuLabelProps) => {
|
||||
return (
|
||||
<MantineMenu.Label
|
||||
className={styles['menu-label']}
|
||||
{...props}
|
||||
>
|
||||
<MantineMenu.Label className={styles['menu-label']} {...props}>
|
||||
{children}
|
||||
</MantineMenu.Label>
|
||||
);
|
||||
|
|
@ -75,10 +72,7 @@ const pMenuItem = ({ children, isDanger, isSelected, ...props }: MenuItemProps)
|
|||
|
||||
const MenuDropdown = ({ children, ...props }: MenuDropdownProps) => {
|
||||
return (
|
||||
<MantineMenu.Dropdown
|
||||
className={styles['menu-dropdown']}
|
||||
{...props}
|
||||
>
|
||||
<MantineMenu.Dropdown className={styles['menu-dropdown']} {...props}>
|
||||
{children}
|
||||
</MantineMenu.Dropdown>
|
||||
);
|
||||
|
|
@ -87,12 +81,7 @@ const MenuDropdown = ({ children, ...props }: MenuDropdownProps) => {
|
|||
const MenuItem = createPolymorphicComponent<'button', MenuItemProps>(pMenuItem);
|
||||
|
||||
const MenuDivider = ({ ...props }: MenuDividerProps) => {
|
||||
return (
|
||||
<MantineMenu.Divider
|
||||
className={styles['menu-divider']}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
return <MantineMenu.Divider className={styles['menu-divider']} {...props} />;
|
||||
};
|
||||
|
||||
DropdownMenu.Label = MenuLabel;
|
||||
|
|
|
|||
|
|
@ -3,13 +3,7 @@ import { Grid as MantineGrid, GridProps as MantineGridProps } from '@mantine/cor
|
|||
export interface GridProps extends MantineGridProps {}
|
||||
|
||||
export const Grid = ({ classNames, style, ...props }: GridProps) => {
|
||||
return (
|
||||
<MantineGrid
|
||||
classNames={{ ...classNames }}
|
||||
style={{ ...style }}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
return <MantineGrid classNames={{ ...classNames }} style={{ ...style }} {...props} />;
|
||||
};
|
||||
|
||||
Grid.Col = MantineGrid.Col;
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ import {
|
|||
LuListPlus,
|
||||
LuLoader,
|
||||
LuLock,
|
||||
LuLockOpen,
|
||||
LuLogIn,
|
||||
LuLogOut,
|
||||
LuMenu,
|
||||
|
|
@ -163,6 +164,7 @@ export const AppIcon = {
|
|||
listInfinite: LuInfinity,
|
||||
listPaginated: LuArrowRightToLine,
|
||||
lock: LuLock,
|
||||
lockOpen: LuLockOpen,
|
||||
mediaNext: LuSkipForward,
|
||||
mediaPause: LuPause,
|
||||
mediaPlay: LuPlay,
|
||||
|
|
|
|||
|
|
@ -89,10 +89,7 @@ export function Image({
|
|||
function ImageContainer({ children, className, enableAnimation, ...props }: ImageContainerProps) {
|
||||
if (!enableAnimation) {
|
||||
return (
|
||||
<div
|
||||
className={clsx(styles.imageContainer, className)}
|
||||
{...props}
|
||||
>
|
||||
<div className={clsx(styles.imageContainer, className)} {...props}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
|
@ -112,10 +109,7 @@ function ImageContainer({ children, className, enableAnimation, ...props }: Imag
|
|||
function ImageLoader({ className }: ImageLoaderProps) {
|
||||
return (
|
||||
<div className={clsx(styles.loader, className)}>
|
||||
<Skeleton
|
||||
className={clsx(styles.skeleton, className)}
|
||||
enableAnimation={true}
|
||||
/>
|
||||
<Skeleton className={clsx(styles.skeleton, className)} enableAnimation={true} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -123,10 +117,7 @@ function ImageLoader({ className }: ImageLoaderProps) {
|
|||
function ImageUnloader({ className }: ImageUnloaderProps) {
|
||||
return (
|
||||
<div className={clsx(styles.unloader, className)}>
|
||||
<Icon
|
||||
icon="emptyImage"
|
||||
size="xl"
|
||||
/>
|
||||
<Icon icon="emptyImage" size="xl" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -93,19 +93,10 @@ export const ConfirmModal = ({
|
|||
<Stack>
|
||||
<Flex>{children}</Flex>
|
||||
<Group justify="flex-end">
|
||||
<Button
|
||||
data-focus
|
||||
onClick={handleCancel}
|
||||
variant="default"
|
||||
>
|
||||
<Button data-focus onClick={handleCancel} variant="default">
|
||||
{labels?.cancel ? labels.cancel : 'Cancel'}
|
||||
</Button>
|
||||
<Button
|
||||
disabled={disabled}
|
||||
loading={loading}
|
||||
onClick={onConfirm}
|
||||
variant="filled"
|
||||
>
|
||||
<Button disabled={disabled} loading={loading} onClick={onConfirm} variant="filled">
|
||||
{labels?.confirm ? labels.confirm : 'Confirm'}
|
||||
</Button>
|
||||
</Group>
|
||||
|
|
|
|||
|
|
@ -12,11 +12,7 @@ interface OptionProps extends GroupProps {
|
|||
|
||||
export const Option = ({ children, ...props }: OptionProps) => {
|
||||
return (
|
||||
<Group
|
||||
classNames={{ root: styles.root }}
|
||||
grow
|
||||
{...props}
|
||||
>
|
||||
<Group classNames={{ root: styles.root }} grow {...props}>
|
||||
{children}
|
||||
</Group>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -16,20 +16,10 @@ export const Spinner = ({ ...props }: SpinnerProps) => {
|
|||
if (props.container) {
|
||||
return (
|
||||
<Center className={styles.container}>
|
||||
<SpinnerIcon
|
||||
className={styles.icon}
|
||||
color={props.color}
|
||||
size={props.size}
|
||||
/>
|
||||
<SpinnerIcon className={styles.icon} color={props.color} size={props.size} />
|
||||
</Center>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<SpinnerIcon
|
||||
className={styles.icon}
|
||||
color={props.color}
|
||||
size={props.size}
|
||||
/>
|
||||
);
|
||||
return <SpinnerIcon className={styles.icon} color={props.color} size={props.size} />;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -320,6 +320,7 @@ export type Song = {
|
|||
albumId: string;
|
||||
artistName: string;
|
||||
artists: RelatedArtist[];
|
||||
bitDepth: null | number;
|
||||
bitRate: number;
|
||||
bpm: null | number;
|
||||
channels: null | number;
|
||||
|
|
@ -346,6 +347,7 @@ export type Song = {
|
|||
playlistItemId?: string;
|
||||
releaseDate: null | string;
|
||||
releaseYear: null | string;
|
||||
sampleRate: null | number;
|
||||
serverId: string;
|
||||
serverType: ServerType;
|
||||
size: number;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue