Skip to content

Commit

Permalink
Merge pull request #4785 from grafixeyehero/Add-Action-Button-mui
Browse files Browse the repository at this point in the history
Add Play, Queue, Shuffle and NewCollection Buttons
  • Loading branch information
thornbill authored Oct 4, 2023
2 parents 5968e76 + ab0ffb8 commit 817f4ad
Show file tree
Hide file tree
Showing 7 changed files with 339 additions and 2 deletions.
34 changes: 34 additions & 0 deletions src/apps/experimental/components/library/NewCollectionButton.tsx
Original file line number Diff line number Diff line change
@@ -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 changes: 57 additions & 0 deletions src/apps/experimental/components/library/PlayAllButton.tsx
Original file line number Diff line number Diff line change
@@ -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 changes: 39 additions & 0 deletions src/apps/experimental/components/library/QueueButton.tsx
Original file line number Diff line number Diff line change
@@ -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 changes: 49 additions & 0 deletions src/apps/experimental/components/library/ShuffleButton.tsx
Original file line number Diff line number Diff line change
@@ -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;
2 changes: 1 addition & 1 deletion src/apps/experimental/components/library/SortButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ const SortButton: FC<SortButtonProps> = ({
title={globalize.translate('Sort')}
sx={{ ml: 2 }}
aria-describedby={id}
className='paper-icon-button-light btnShuffle autoSize'
className='paper-icon-button-light btnSort autoSize'
onClick={handleClick}
>
<SortByAlphaIcon />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ const ViewSettingsButton: FC<ViewSettingsButtonProps> = ({
title={globalize.translate('ButtonSelectView')}
sx={{ ml: 2 }}
aria-describedby={id}
className='paper-icon-button-light btnShuffle autoSize'
className='paper-icon-button-light btnSelectView autoSize'
onClick={handleClick}
>
<ViewComfyIcon />
Expand Down
158 changes: 158 additions & 0 deletions src/utils/items.ts
Original file line number Diff line number Diff line change
@@ -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
};
};

0 comments on commit 817f4ad

Please sign in to comment.