Merge pull request #4785 from grafixeyehero/Add-Action-Button-mui
Add Play, Queue, Shuffle and NewCollection Buttons
This commit is contained in:
commit
817f4adb90
7 changed files with 339 additions and 2 deletions
|
@ -0,0 +1,34 @@
|
||||||
|
import React, { FC, useCallback } from 'react';
|
||||||
|
import { IconButton } from '@mui/material';
|
||||||
|
import AddIcon from '@mui/icons-material/Add';
|
||||||
|
import globalize from 'scripts/globalize';
|
||||||
|
|
||||||
|
const NewCollectionButton: FC = () => {
|
||||||
|
const showCollectionEditor = useCallback(() => {
|
||||||
|
import('components/collectionEditor/collectionEditor').then(
|
||||||
|
({ default: CollectionEditor }) => {
|
||||||
|
const serverId = window.ApiClient.serverId();
|
||||||
|
const collectionEditor = new CollectionEditor();
|
||||||
|
collectionEditor.show({
|
||||||
|
items: [],
|
||||||
|
serverId: serverId
|
||||||
|
}).catch(() => {
|
||||||
|
// closed collection editor
|
||||||
|
});
|
||||||
|
}).catch(err => {
|
||||||
|
console.error('[NewCollection] failed to load collection editor', err);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IconButton
|
||||||
|
title={globalize.translate('Add')}
|
||||||
|
className='paper-icon-button-light btnNewCollection autoSize'
|
||||||
|
onClick={showCollectionEditor}
|
||||||
|
>
|
||||||
|
<AddIcon />
|
||||||
|
</IconButton>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default NewCollectionButton;
|
57
src/apps/experimental/components/library/PlayAllButton.tsx
Normal file
57
src/apps/experimental/components/library/PlayAllButton.tsx
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
import type { BaseItemDto } from '@jellyfin/sdk/lib/generated-client';
|
||||||
|
import React, { FC, useCallback } from 'react';
|
||||||
|
import { IconButton } from '@mui/material';
|
||||||
|
import PlayArrowIcon from '@mui/icons-material/PlayArrow';
|
||||||
|
|
||||||
|
import { playbackManager } from 'components/playback/playbackmanager';
|
||||||
|
import globalize from 'scripts/globalize';
|
||||||
|
import { getFiltersQuery } from 'utils/items';
|
||||||
|
import { LibraryViewSettings } from 'types/library';
|
||||||
|
import { LibraryTab } from 'types/libraryTab';
|
||||||
|
|
||||||
|
interface PlayAllButtonProps {
|
||||||
|
item: BaseItemDto | undefined;
|
||||||
|
items: BaseItemDto[];
|
||||||
|
viewType: LibraryTab;
|
||||||
|
hasFilters: boolean;
|
||||||
|
libraryViewSettings: LibraryViewSettings
|
||||||
|
}
|
||||||
|
|
||||||
|
const PlayAllButton: FC<PlayAllButtonProps> = ({ item, items, viewType, hasFilters, libraryViewSettings }) => {
|
||||||
|
const play = useCallback(() => {
|
||||||
|
if (item && !hasFilters) {
|
||||||
|
playbackManager.play({
|
||||||
|
items: [item],
|
||||||
|
autoplay: true,
|
||||||
|
queryOptions: {
|
||||||
|
SortBy: [libraryViewSettings.SortBy],
|
||||||
|
SortOrder: [libraryViewSettings.SortOrder]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
playbackManager.play({
|
||||||
|
items: items,
|
||||||
|
autoplay: true,
|
||||||
|
queryOptions: {
|
||||||
|
ParentId: item?.Id ?? undefined,
|
||||||
|
...getFiltersQuery(viewType, libraryViewSettings),
|
||||||
|
SortBy: [libraryViewSettings.SortBy],
|
||||||
|
SortOrder: [libraryViewSettings.SortOrder]
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [hasFilters, item, items, libraryViewSettings, viewType]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IconButton
|
||||||
|
title={globalize.translate('HeaderPlayAll')}
|
||||||
|
className='paper-icon-button-light btnPlay autoSize'
|
||||||
|
onClick={play}
|
||||||
|
>
|
||||||
|
<PlayArrowIcon />
|
||||||
|
</IconButton>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default PlayAllButton;
|
39
src/apps/experimental/components/library/QueueButton.tsx
Normal file
39
src/apps/experimental/components/library/QueueButton.tsx
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
import type { BaseItemDto } from '@jellyfin/sdk/lib/generated-client';
|
||||||
|
import React, { FC, useCallback } from 'react';
|
||||||
|
import { IconButton } from '@mui/material';
|
||||||
|
import QueueIcon from '@mui/icons-material/Queue';
|
||||||
|
|
||||||
|
import { playbackManager } from 'components/playback/playbackmanager';
|
||||||
|
import globalize from 'scripts/globalize';
|
||||||
|
|
||||||
|
interface QueueButtonProps {
|
||||||
|
item: BaseItemDto | undefined
|
||||||
|
items: BaseItemDto[];
|
||||||
|
hasFilters: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QueueButton: FC<QueueButtonProps> = ({ item, items, hasFilters }) => {
|
||||||
|
const queue = useCallback(() => {
|
||||||
|
if (item && !hasFilters) {
|
||||||
|
playbackManager.queue({
|
||||||
|
items: [item]
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
playbackManager.queue({
|
||||||
|
items: items
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [hasFilters, item, items]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IconButton
|
||||||
|
title={globalize.translate('AddToPlayQueue')}
|
||||||
|
className='paper-icon-button-light btnQueue autoSize'
|
||||||
|
onClick={queue}
|
||||||
|
>
|
||||||
|
<QueueIcon />
|
||||||
|
</IconButton>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default QueueButton;
|
49
src/apps/experimental/components/library/ShuffleButton.tsx
Normal file
49
src/apps/experimental/components/library/ShuffleButton.tsx
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
import type { BaseItemDto } from '@jellyfin/sdk/lib/generated-client';
|
||||||
|
import { ItemSortBy } from '@jellyfin/sdk/lib/models/api/item-sort-by';
|
||||||
|
import React, { FC, useCallback } from 'react';
|
||||||
|
import { IconButton } from '@mui/material';
|
||||||
|
import ShuffleIcon from '@mui/icons-material/Shuffle';
|
||||||
|
|
||||||
|
import { playbackManager } from 'components/playback/playbackmanager';
|
||||||
|
import globalize from 'scripts/globalize';
|
||||||
|
import { getFiltersQuery } from 'utils/items';
|
||||||
|
import { LibraryViewSettings } from 'types/library';
|
||||||
|
import { LibraryTab } from 'types/libraryTab';
|
||||||
|
|
||||||
|
interface ShuffleButtonProps {
|
||||||
|
item: BaseItemDto | undefined;
|
||||||
|
items: BaseItemDto[];
|
||||||
|
viewType: LibraryTab
|
||||||
|
hasFilters: boolean;
|
||||||
|
libraryViewSettings: LibraryViewSettings
|
||||||
|
}
|
||||||
|
|
||||||
|
const ShuffleButton: FC<ShuffleButtonProps> = ({ item, items, viewType, hasFilters, libraryViewSettings }) => {
|
||||||
|
const shuffle = useCallback(() => {
|
||||||
|
if (item && !hasFilters) {
|
||||||
|
playbackManager.shuffle(item);
|
||||||
|
} else {
|
||||||
|
playbackManager.play({
|
||||||
|
items: items,
|
||||||
|
autoplay: true,
|
||||||
|
queryOptions: {
|
||||||
|
ParentId: item?.Id ?? undefined,
|
||||||
|
...getFiltersQuery(viewType, libraryViewSettings),
|
||||||
|
SortBy: [ItemSortBy.Random]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [hasFilters, item, items, libraryViewSettings, viewType]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<IconButton
|
||||||
|
title={globalize.translate('Shuffle')}
|
||||||
|
className='paper-icon-button-light btnShuffle autoSize'
|
||||||
|
onClick={shuffle}
|
||||||
|
>
|
||||||
|
<ShuffleIcon />
|
||||||
|
</IconButton>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ShuffleButton;
|
|
@ -98,7 +98,7 @@ const SortButton: FC<SortButtonProps> = ({
|
||||||
title={globalize.translate('Sort')}
|
title={globalize.translate('Sort')}
|
||||||
sx={{ ml: 2 }}
|
sx={{ ml: 2 }}
|
||||||
aria-describedby={id}
|
aria-describedby={id}
|
||||||
className='paper-icon-button-light btnShuffle autoSize'
|
className='paper-icon-button-light btnSort autoSize'
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
>
|
>
|
||||||
<SortByAlphaIcon />
|
<SortByAlphaIcon />
|
||||||
|
|
|
@ -100,7 +100,7 @@ const ViewSettingsButton: FC<ViewSettingsButtonProps> = ({
|
||||||
title={globalize.translate('ButtonSelectView')}
|
title={globalize.translate('ButtonSelectView')}
|
||||||
sx={{ ml: 2 }}
|
sx={{ ml: 2 }}
|
||||||
aria-describedby={id}
|
aria-describedby={id}
|
||||||
className='paper-icon-button-light btnShuffle autoSize'
|
className='paper-icon-button-light btnSelectView autoSize'
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
>
|
>
|
||||||
<ViewComfyIcon />
|
<ViewComfyIcon />
|
||||||
|
|
158
src/utils/items.ts
Normal file
158
src/utils/items.ts
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
import { ItemFields } from '@jellyfin/sdk/lib/generated-client/models/item-fields';
|
||||||
|
import { ImageType } from '@jellyfin/sdk/lib/generated-client/models/image-type';
|
||||||
|
import { ItemSortBy } from '@jellyfin/sdk/lib/models/api/item-sort-by';
|
||||||
|
import { SortOrder } from '@jellyfin/sdk/lib/generated-client/models/sort-order';
|
||||||
|
import * as userSettings from 'scripts/settings/userSettings';
|
||||||
|
import { EpisodeFilter, FeatureFilters, LibraryViewSettings, ParentId, VideoBasicFilter, ViewMode } from '../types/library';
|
||||||
|
import { LibraryTab } from 'types/libraryTab';
|
||||||
|
|
||||||
|
export const getVideoBasicFilter = (libraryViewSettings: LibraryViewSettings) => {
|
||||||
|
let isHd;
|
||||||
|
|
||||||
|
if (libraryViewSettings.Filters?.VideoBasicFilter?.includes(VideoBasicFilter.IsHD)) {
|
||||||
|
isHd = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (libraryViewSettings.Filters?.VideoBasicFilter?.includes(VideoBasicFilter.IsSD)) {
|
||||||
|
isHd = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
isHd,
|
||||||
|
is4K: libraryViewSettings.Filters?.VideoBasicFilter?.includes(VideoBasicFilter.Is4K) ?
|
||||||
|
true :
|
||||||
|
undefined,
|
||||||
|
is3D: libraryViewSettings.Filters?.VideoBasicFilter?.includes(VideoBasicFilter.Is3D) ?
|
||||||
|
true :
|
||||||
|
undefined
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getFeatureFilters = (libraryViewSettings: LibraryViewSettings) => {
|
||||||
|
return {
|
||||||
|
hasSubtitles: libraryViewSettings.Filters?.Features?.includes(FeatureFilters.HasSubtitles) ?
|
||||||
|
true :
|
||||||
|
undefined,
|
||||||
|
hasTrailer: libraryViewSettings.Filters?.Features?.includes(FeatureFilters.HasTrailer) ?
|
||||||
|
true :
|
||||||
|
undefined,
|
||||||
|
hasSpecialFeature: libraryViewSettings.Filters?.Features?.includes(
|
||||||
|
FeatureFilters.HasSpecialFeature
|
||||||
|
) ?
|
||||||
|
true :
|
||||||
|
undefined,
|
||||||
|
hasThemeSong: libraryViewSettings.Filters?.Features?.includes(FeatureFilters.HasThemeSong) ?
|
||||||
|
true :
|
||||||
|
undefined,
|
||||||
|
hasThemeVideo: libraryViewSettings.Filters?.Features?.includes(
|
||||||
|
FeatureFilters.HasThemeVideo
|
||||||
|
) ?
|
||||||
|
true :
|
||||||
|
undefined
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getEpisodeFilter = (
|
||||||
|
viewType: LibraryTab,
|
||||||
|
libraryViewSettings: LibraryViewSettings
|
||||||
|
) => {
|
||||||
|
return {
|
||||||
|
parentIndexNumber: libraryViewSettings.Filters?.EpisodeFilter?.includes(
|
||||||
|
EpisodeFilter.ParentIndexNumber
|
||||||
|
) ?
|
||||||
|
0 :
|
||||||
|
undefined,
|
||||||
|
isMissing:
|
||||||
|
viewType === LibraryTab.Episodes ?
|
||||||
|
!!libraryViewSettings.Filters?.EpisodeFilter?.includes(EpisodeFilter.IsMissing) :
|
||||||
|
undefined,
|
||||||
|
isUnaired: libraryViewSettings.Filters?.EpisodeFilter?.includes(EpisodeFilter.IsUnaired) ?
|
||||||
|
true :
|
||||||
|
undefined
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const getItemFieldsEnum = (
|
||||||
|
viewType: LibraryTab,
|
||||||
|
libraryViewSettings: LibraryViewSettings
|
||||||
|
) => {
|
||||||
|
const itemFields: ItemFields[] = [];
|
||||||
|
|
||||||
|
if (viewType !== LibraryTab.Networks) {
|
||||||
|
itemFields.push(ItemFields.BasicSyncInfo, ItemFields.MediaSourceCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (libraryViewSettings.ImageType === ImageType.Primary) {
|
||||||
|
itemFields.push(ItemFields.PrimaryImageAspectRatio);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (viewType === LibraryTab.Networks) {
|
||||||
|
itemFields.push(
|
||||||
|
ItemFields.DateCreated,
|
||||||
|
ItemFields.PrimaryImageAspectRatio
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return itemFields;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getFieldsQuery = (
|
||||||
|
viewType: LibraryTab,
|
||||||
|
libraryViewSettings: LibraryViewSettings
|
||||||
|
) => {
|
||||||
|
return {
|
||||||
|
fields: getItemFieldsEnum(viewType, libraryViewSettings)
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getLimitQuery = () => {
|
||||||
|
return {
|
||||||
|
limit: userSettings.libraryPageSize(undefined) || undefined
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getAlphaPickerQuery = (libraryViewSettings: LibraryViewSettings) => {
|
||||||
|
const alphabetValue = libraryViewSettings.Alphabet !== null ?
|
||||||
|
libraryViewSettings.Alphabet : undefined;
|
||||||
|
|
||||||
|
return {
|
||||||
|
nameLessThan: alphabetValue === '#' ? 'A' : undefined,
|
||||||
|
nameStartsWith: alphabetValue === '#' ? undefined : alphabetValue
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getFiltersQuery = (
|
||||||
|
viewType: LibraryTab,
|
||||||
|
libraryViewSettings: LibraryViewSettings
|
||||||
|
) => {
|
||||||
|
return {
|
||||||
|
...getFeatureFilters(libraryViewSettings),
|
||||||
|
...getEpisodeFilter(viewType, libraryViewSettings),
|
||||||
|
...getVideoBasicFilter(libraryViewSettings),
|
||||||
|
seriesStatus: libraryViewSettings?.Filters?.SeriesStatus,
|
||||||
|
videoTypes: libraryViewSettings?.Filters?.VideoTypes,
|
||||||
|
filters: libraryViewSettings?.Filters?.Status,
|
||||||
|
genres: libraryViewSettings?.Filters?.Genres,
|
||||||
|
officialRatings: libraryViewSettings?.Filters?.OfficialRatings,
|
||||||
|
tags: libraryViewSettings?.Filters?.Tags,
|
||||||
|
years: libraryViewSettings?.Filters?.Years,
|
||||||
|
studioIds: libraryViewSettings?.Filters?.StudioIds
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getSettingsKey = (viewType: LibraryTab, parentId: ParentId) => {
|
||||||
|
return `${viewType} - ${parentId}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getDefaultLibraryViewSettings = (): LibraryViewSettings => {
|
||||||
|
return {
|
||||||
|
ShowTitle: true,
|
||||||
|
ShowYear: false,
|
||||||
|
ViewMode: ViewMode.GridView,
|
||||||
|
ImageType: ImageType.Primary,
|
||||||
|
CardLayout: false,
|
||||||
|
SortBy: ItemSortBy.SortName,
|
||||||
|
SortOrder: SortOrder.Ascending,
|
||||||
|
StartIndex: 0
|
||||||
|
};
|
||||||
|
};
|
Loading…
Add table
Add a link
Reference in a new issue