Skip to content

Commit

Permalink
Implement Plex Search in program selector (#210)
Browse files Browse the repository at this point in the history
* Plex Search - outlining filter metadata types + querying for them

* Checkpoint

* Another checkpoint - form almost working

* Another checkpoint for Plex search

* Hooked up form to local state and have it triggering search updates
* Need to still confirm that the different operators are working
  correctly
* Lots of updates left for the different form types, like the date
  picker
* Autocomplete working sorta...loading tag values from Plex

* Some more fixes to the builder:

1. Tooltips for the different buttons
2. Ability to remove groups
3. Hookup and/or operator nodes to the overall form
4. Some stylistic fixes

* Ton more changes

1. Hooked up the filtering feature to the actual program selector UI
2. Implement sorting
3. Differentiation between basic/advanced search types (Basic is a
   single value field while advanced supports boolean operators)
4. Some hideous UI for all of this

Still TODO:
* Caching? Unclear whether this is worth it
* Auto-search when values change based on a timeout -- current flow
  requires a button press which is sort of annoying
* Clear search value button (button on each input to clear the value)
* Validation of search parameters / fields

* Rename some files; fix the build
  • Loading branch information
chrisbenincasa authored Mar 28, 2024
1 parent 6555e8c commit 0069184
Show file tree
Hide file tree
Showing 17 changed files with 1,143 additions and 168 deletions.
Empty file added types/src/plex/filters.ts
Empty file.
91 changes: 89 additions & 2 deletions types/src/plex/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ export const PlexJoinItemSchema = z.object({

export type PlexJoinItem = z.infer<typeof PlexJoinItemSchema>;

export const PlexMediaTypeSchema = z.union([
z.literal('movie'),
z.literal('show'),
z.literal('artist'),
]);

export const PlexLibrarySectionSchema = z.object({
allowSync: z.boolean(),
art: z.string(),
Expand All @@ -23,7 +29,7 @@ export const PlexLibrarySectionSchema = z.object({
refreshing: z.boolean(),
thumb: z.string(),
key: z.string(),
type: z.union([z.literal('movie'), z.literal('show'), z.literal('artist')]),
type: PlexMediaTypeSchema,
title: z.string(),
agent: z.string(),
scanner: z.string(),
Expand Down Expand Up @@ -102,7 +108,7 @@ const basePlexCollectionSchema = z.object({
});

const basePlexLibrarySchema = basePlexCollectionSchema.extend({
type: z.union([z.literal('movie'), z.literal('show'), z.literal('artist')]),
type: PlexMediaTypeSchema,
});

const basePlexChildCollectionSchema = basePlexCollectionSchema.extend({
Expand Down Expand Up @@ -703,3 +709,84 @@ export const PlexResourcesResponseSchema = z.array(PlexResourceSchema);
export type PlexResourcesResponse = Alias<
z.infer<typeof PlexResourcesResponseSchema>
>;

export const PlexFilterFieldTypeOperatorSchema = z.object({
key: z.string(),
title: z.string(),
});

export const PlexFilterFieldTypeSchema = z.object({
type: z.string(),
Operator: z.array(PlexFilterFieldTypeOperatorSchema),
});

export const PlexLibraryFilterSchema = z.object({
filter: z.string(),
filterType: z.string(),
key: z.string(),
title: z.string(),
type: z.string(),
advanced: z.boolean().optional(),
});

export const PlexLibrarySortSchema = z.object({
active: z.boolean().optional(),
activeDirection: z.string().optional(),
default: z.string().optional(),
defaultDirection: z.string(),
descKey: z.string(),
firstCharacterKey: z.string().optional(),
key: z.string(),
title: z.string(),
});

export type PlexLibrarySort = z.infer<typeof PlexLibrarySortSchema>;

export const PlexLibraryFieldSchema = z.object({
key: z.string(),
title: z.string(),
type: z.string(),
});

export const PlexFilterTypeSchema = z.object({
key: z.string(),
type: PlexMediaTypeSchema,
title: z.string(),
active: z.boolean(),
Filter: z.array(PlexLibraryFilterSchema),
Sort: z.array(PlexLibrarySortSchema),
Field: z.array(PlexLibraryFieldSchema),
});

export type PlexFilterType = z.infer<typeof PlexFilterTypeSchema>;

const PlexFilterResponseMetaSchema = z.object({
Type: z.array(PlexFilterTypeSchema),
FieldType: z.array(PlexFilterFieldTypeSchema),
});

export type PlexFilterResponseMeta = z.infer<
typeof PlexFilterResponseMetaSchema
>;

export const PlexFiltersResponseSchema = z.object({
// There are some standard fields here...
Meta: PlexFilterResponseMetaSchema,
});

export type PlexFiltersResponse = z.infer<typeof PlexFiltersResponseSchema>;

export const PlexTagSchema = z.object({
fastKey: z.string().optional(),
thumb: z.string().optional(),
key: z.string(),
title: z.string(),
});

export const PlexTagResultSchema = z.object({
size: z.number(),
// Some other stuff here that we don't need yet...
Directory: z.array(PlexTagSchema),
});

export type PlexTagResult = z.infer<typeof PlexTagResultSchema>;
2 changes: 1 addition & 1 deletion web/src/components/TabPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Grid } from '@mui/material';
import { Unstable_Grid2 as Grid } from '@mui/material';
import { ForwardedRef, forwardRef } from 'react';
import useStore from '../store';

Expand Down
33 changes: 33 additions & 0 deletions web/src/components/base/StandaloneToggleButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import ToggleButton, { ToggleButtonProps } from '@mui/material/ToggleButton';
import React from 'react';

type Props = {
children: React.ReactNode;
selected: boolean;
onToggle(): void;
toggleButtonProps?: Partial<ToggleButtonProps>;
};

const defaultProps: Partial<Props> = {
toggleButtonProps: {},
};

export default function StandaloneToggleButton({
children,
selected,
onToggle,
toggleButtonProps,
}: Props) {
return (
<ToggleButton
{...(toggleButtonProps ?? defaultProps.toggleButtonProps)}
value="check"
selected={selected}
onChange={() => {
onToggle();
}}
>
{children}
</ToggleButton>
);
}
Loading

0 comments on commit 0069184

Please sign in to comment.