mirror of
https://github.com/chrisbenincasa/tunarr.git
synced 2026-04-18 09:03:35 -04:00
feat: add ability to exclude seasons in slot schedulers
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -155,6 +155,7 @@ describe('TimeSlotService', () => {
|
||||
order: 'next',
|
||||
direction: 'asc',
|
||||
seasonFilter: [],
|
||||
seasonExcludeFilter: [],
|
||||
},
|
||||
{
|
||||
startTime: 12 * 60 * 60 * 1000, // Noon
|
||||
@@ -212,6 +213,7 @@ describe('TimeSlotService', () => {
|
||||
order: 'next',
|
||||
direction: 'asc',
|
||||
seasonFilter: [],
|
||||
seasonExcludeFilter: [],
|
||||
},
|
||||
],
|
||||
period: 'day',
|
||||
@@ -269,6 +271,7 @@ describe('TimeSlotService', () => {
|
||||
order: 'next',
|
||||
direction: 'asc',
|
||||
seasonFilter: [],
|
||||
seasonExcludeFilter: [],
|
||||
},
|
||||
],
|
||||
period: 'day',
|
||||
@@ -318,6 +321,7 @@ describe('TimeSlotService', () => {
|
||||
order: 'next',
|
||||
direction: 'asc',
|
||||
seasonFilter: [],
|
||||
seasonExcludeFilter: [],
|
||||
},
|
||||
],
|
||||
period: 'day',
|
||||
@@ -395,6 +399,7 @@ describe('TimeSlotService', () => {
|
||||
order: 'next',
|
||||
direction: 'desc',
|
||||
seasonFilter: [],
|
||||
seasonExcludeFilter: [],
|
||||
},
|
||||
],
|
||||
period: 'day',
|
||||
@@ -507,6 +512,7 @@ describe('TimeSlotService', () => {
|
||||
order: 'next',
|
||||
direction: 'asc',
|
||||
seasonFilter: [],
|
||||
seasonExcludeFilter: [],
|
||||
},
|
||||
],
|
||||
period: 'day',
|
||||
@@ -564,6 +570,7 @@ describe('TimeSlotService', () => {
|
||||
order: 'next',
|
||||
direction: 'asc',
|
||||
seasonFilter: [],
|
||||
seasonExcludeFilter: [],
|
||||
},
|
||||
],
|
||||
period: 'day',
|
||||
@@ -713,6 +720,7 @@ describe('TimeSlotService', () => {
|
||||
order: 'next',
|
||||
direction: 'asc',
|
||||
seasonFilter: [],
|
||||
seasonExcludeFilter: [],
|
||||
},
|
||||
],
|
||||
period: 'week',
|
||||
@@ -755,6 +763,7 @@ describe('TimeSlotService', () => {
|
||||
order: 'next',
|
||||
direction: 'asc',
|
||||
seasonFilter: [],
|
||||
seasonExcludeFilter: [],
|
||||
},
|
||||
],
|
||||
period: 'day',
|
||||
@@ -1051,6 +1060,7 @@ describe('TimeSlotService', () => {
|
||||
order: 'next',
|
||||
direction: 'asc',
|
||||
seasonFilter: [],
|
||||
seasonExcludeFilter: [],
|
||||
},
|
||||
],
|
||||
period: 'day',
|
||||
|
||||
@@ -448,11 +448,19 @@ function getContentProgramIterator(
|
||||
(p) => p.uuid,
|
||||
);
|
||||
|
||||
if (slot.type === 'show' && slot.seasonFilter.length > 0) {
|
||||
programs = programs.filter((program) => {
|
||||
const season = program.season?.index ?? program.seasonNumber;
|
||||
return season && slot.seasonFilter.includes(season);
|
||||
});
|
||||
if (slot.type === 'show') {
|
||||
if (slot.seasonFilter.length > 0) {
|
||||
programs = programs.filter((program) => {
|
||||
const season = program.season?.index ?? program.seasonNumber;
|
||||
return season && slot.seasonFilter.includes(season);
|
||||
});
|
||||
}
|
||||
if (slot.seasonExcludeFilter?.length > 0) {
|
||||
programs = programs.filter((program) => {
|
||||
const season = program.season?.index ?? program.seasonNumber;
|
||||
return !season || !slot.seasonExcludeFilter.includes(season);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
switch (slot.order) {
|
||||
|
||||
@@ -61,6 +61,7 @@ export const ShowProgrammingSlotSchema = z.object({
|
||||
type: z.literal('show'),
|
||||
showId: z.string(),
|
||||
seasonFilter: z.number().array().default([]).catch([]),
|
||||
seasonExcludeFilter: z.number().array().default([]).catch([]),
|
||||
...BaseSlotOrdering.shape,
|
||||
...Slot.shape,
|
||||
});
|
||||
|
||||
@@ -103,6 +103,7 @@ export const AddRandomSlotButton = ({ onAdd }: AddRandomSlotButtonProps) => {
|
||||
order: 'next',
|
||||
show: null,
|
||||
seasonFilter: [],
|
||||
seasonExcludeFilter: [],
|
||||
}))
|
||||
.with({ type: 'flex' }, () => ({
|
||||
...baseSlot,
|
||||
|
||||
@@ -72,6 +72,7 @@ export const AddTimeSlotButton = ({
|
||||
showId: sortBy(opts, (opt) => opt.value)?.[0].showId,
|
||||
show: null,
|
||||
seasonFilter: [],
|
||||
seasonExcludeFilter: [],
|
||||
} satisfies ShowTimeSlotViewModel;
|
||||
} else if (
|
||||
optionsByType['custom-show'] &&
|
||||
|
||||
@@ -192,6 +192,7 @@ export const EditRandomSlotDialogContent = ({
|
||||
order: 'next',
|
||||
direction: 'asc',
|
||||
seasonFilter: [],
|
||||
seasonExcludeFilter: [],
|
||||
}))
|
||||
.with('smart-collection', () => {
|
||||
const opt = programOptions.find(
|
||||
|
||||
@@ -166,6 +166,7 @@ export const EditTimeSlotDialogContent = ({
|
||||
title: opt?.description ?? '',
|
||||
show: null,
|
||||
seasonFilter: [],
|
||||
seasonExcludeFilter: [],
|
||||
};
|
||||
})
|
||||
.with('smart-collection', () => {
|
||||
|
||||
@@ -14,7 +14,8 @@ export const ShowSearchSlotProgrammingForm = () => {
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const enabled = useMemo(() => searchQuery.length >= 1, [searchQuery]);
|
||||
const show = watch('show');
|
||||
console.log(watch());
|
||||
const seasonFilter = watch('seasonFilter');
|
||||
const seasonExcludeFilter = watch('seasonExcludeFilter');
|
||||
|
||||
const search = useMemo(
|
||||
() => ({
|
||||
@@ -32,7 +33,7 @@ export const ShowSearchSlotProgrammingForm = () => {
|
||||
enabled: !!show,
|
||||
});
|
||||
|
||||
const seasonAutocompleteOpts = useMemo(
|
||||
const allSeasons = useMemo(
|
||||
() =>
|
||||
showChildrenQuery.data?.result.programs.filter(
|
||||
(x) => x.type === 'season',
|
||||
@@ -40,6 +41,16 @@ export const ShowSearchSlotProgrammingForm = () => {
|
||||
[showChildrenQuery.data],
|
||||
);
|
||||
|
||||
const includeOptions = useMemo(
|
||||
() => allSeasons.filter((s) => !seasonExcludeFilter.includes(s.index)),
|
||||
[allSeasons, seasonExcludeFilter],
|
||||
);
|
||||
|
||||
const excludeOptions = useMemo(
|
||||
() => allSeasons.filter((s) => !seasonFilter.includes(s.index)),
|
||||
[allSeasons, seasonFilter],
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Controller
|
||||
@@ -56,6 +67,7 @@ export const ShowSearchSlotProgrammingForm = () => {
|
||||
field.onChange(show.uuid);
|
||||
setValue('show', show);
|
||||
setValue('seasonFilter', []);
|
||||
setValue('seasonExcludeFilter', []);
|
||||
}}
|
||||
onQueryChange={setSearchQuery}
|
||||
label="Show"
|
||||
@@ -67,20 +79,18 @@ export const ShowSearchSlotProgrammingForm = () => {
|
||||
name="seasonFilter"
|
||||
render={({ field }) => (
|
||||
<Autocomplete
|
||||
options={seasonAutocompleteOpts}
|
||||
options={includeOptions}
|
||||
value={
|
||||
field.value.length === 0
|
||||
? []
|
||||
: seasonAutocompleteOpts.filter((opt) =>
|
||||
field.value.includes(opt.index),
|
||||
)
|
||||
: allSeasons.filter((opt) => field.value.includes(opt.index))
|
||||
}
|
||||
disabled={!show || showChildrenQuery.isLoading}
|
||||
multiple
|
||||
getOptionKey={(season) => season.index}
|
||||
getOptionLabel={(season) => season.title}
|
||||
renderInput={(params) => (
|
||||
<TextField {...params} label={'Seasons'} />
|
||||
<TextField {...params} label={'Include Seasons'} />
|
||||
)}
|
||||
onChange={(_, seasons) =>
|
||||
setValue(
|
||||
@@ -92,6 +102,34 @@ export const ShowSearchSlotProgrammingForm = () => {
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
control={control}
|
||||
name="seasonExcludeFilter"
|
||||
render={({ field }) => (
|
||||
<Autocomplete
|
||||
options={excludeOptions}
|
||||
value={
|
||||
field.value.length === 0
|
||||
? []
|
||||
: allSeasons.filter((opt) => field.value.includes(opt.index))
|
||||
}
|
||||
disabled={!show || showChildrenQuery.isLoading}
|
||||
multiple
|
||||
getOptionKey={(season) => season.index}
|
||||
getOptionLabel={(season) => season.title}
|
||||
renderInput={(params) => (
|
||||
<TextField {...params} label={'Exclude Seasons'} />
|
||||
)}
|
||||
onChange={(_, seasons) =>
|
||||
setValue(
|
||||
'seasonExcludeFilter',
|
||||
seasons.map((s) => s.index),
|
||||
)
|
||||
}
|
||||
filterSelectedOptions
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<SlotOrderFormControl />
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -109,6 +109,7 @@ const SlotSchedulerCyclicShuffleDialogContent = ({ onClose }: Props) => {
|
||||
show,
|
||||
weight: 100,
|
||||
seasonFilter: [],
|
||||
seasonExcludeFilter: [],
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -3704,6 +3704,7 @@ export type GetApiChannelsByIdProgrammingResponses = {
|
||||
type: 'show';
|
||||
showId: string;
|
||||
seasonFilter: Array<number>;
|
||||
seasonExcludeFilter: Array<number>;
|
||||
order: 'next' | 'shuffle' | 'ordered_shuffle' | 'alphanumeric' | 'chronological';
|
||||
direction: 'asc' | 'desc';
|
||||
filler?: Array<{
|
||||
@@ -3805,6 +3806,7 @@ export type GetApiChannelsByIdProgrammingResponses = {
|
||||
type: 'show';
|
||||
showId: string;
|
||||
seasonFilter: Array<number>;
|
||||
seasonExcludeFilter: Array<number>;
|
||||
} | {
|
||||
cooldownMs: number;
|
||||
periodMs?: number;
|
||||
@@ -4026,6 +4028,7 @@ export type PostApiChannelsByIdProgrammingData = {
|
||||
type: 'show';
|
||||
showId: string;
|
||||
seasonFilter?: Array<number>;
|
||||
seasonExcludeFilter?: Array<number>;
|
||||
order: 'next' | 'shuffle' | 'ordered_shuffle' | 'alphanumeric' | 'chronological';
|
||||
direction?: 'asc' | 'desc';
|
||||
filler?: Array<{
|
||||
@@ -4133,6 +4136,7 @@ export type PostApiChannelsByIdProgrammingData = {
|
||||
type: 'show';
|
||||
showId: string;
|
||||
seasonFilter?: Array<number>;
|
||||
seasonExcludeFilter?: Array<number>;
|
||||
} | {
|
||||
cooldownMs: number;
|
||||
periodMs?: number;
|
||||
@@ -4359,6 +4363,7 @@ export type PostApiChannelsByIdProgrammingResponses = {
|
||||
type: 'show';
|
||||
showId: string;
|
||||
seasonFilter: Array<number>;
|
||||
seasonExcludeFilter: Array<number>;
|
||||
order: 'next' | 'shuffle' | 'ordered_shuffle' | 'alphanumeric' | 'chronological';
|
||||
direction: 'asc' | 'desc';
|
||||
filler?: Array<{
|
||||
@@ -4460,6 +4465,7 @@ export type PostApiChannelsByIdProgrammingResponses = {
|
||||
type: 'show';
|
||||
showId: string;
|
||||
seasonFilter: Array<number>;
|
||||
seasonExcludeFilter: Array<number>;
|
||||
} | {
|
||||
cooldownMs: number;
|
||||
periodMs?: number;
|
||||
@@ -5013,6 +5019,7 @@ export type PostApiChannelsByChannelIdScheduleTimeSlotsData = {
|
||||
type: 'show';
|
||||
showId: string;
|
||||
seasonFilter?: Array<number>;
|
||||
seasonExcludeFilter?: Array<number>;
|
||||
order: 'next' | 'shuffle' | 'ordered_shuffle' | 'alphanumeric' | 'chronological';
|
||||
direction?: 'asc' | 'desc';
|
||||
filler?: Array<{
|
||||
@@ -5215,6 +5222,7 @@ export type PostApiChannelsByChannelIdScheduleSlotsData = {
|
||||
type: 'show';
|
||||
showId: string;
|
||||
seasonFilter?: Array<number>;
|
||||
seasonExcludeFilter?: Array<number>;
|
||||
} | {
|
||||
cooldownMs: number;
|
||||
periodMs?: number;
|
||||
@@ -5445,6 +5453,7 @@ export type GetApiChannelsByIdScheduleResponses = {
|
||||
type: 'show';
|
||||
showId: string;
|
||||
seasonFilter: Array<number>;
|
||||
seasonExcludeFilter: Array<number>;
|
||||
order: 'next' | 'shuffle' | 'ordered_shuffle' | 'alphanumeric' | 'chronological';
|
||||
direction: 'asc' | 'desc';
|
||||
filler?: Array<{
|
||||
@@ -5686,6 +5695,7 @@ export type GetApiChannelsByIdScheduleResponses = {
|
||||
type: 'show';
|
||||
showId: string;
|
||||
seasonFilter: Array<number>;
|
||||
seasonExcludeFilter: Array<number>;
|
||||
show: Show | null;
|
||||
/**
|
||||
* A show that existed in the DB at schedule time, but no longer exists.
|
||||
|
||||
@@ -63,6 +63,7 @@ export const CommonShowSlotViewModel = z.object({
|
||||
})
|
||||
.optional(),
|
||||
seasonFilter: z.number().array().default([]),
|
||||
seasonExcludeFilter: z.number().array().default([]),
|
||||
});
|
||||
|
||||
export type CommonShowSlotViewModel = z.infer<typeof CommonShowSlotViewModel>;
|
||||
|
||||
Reference in New Issue
Block a user