Skip to content

Commit

Permalink
Implementing some programming controls (#130)
Browse files Browse the repository at this point in the history
* Checkpoint

* Restrict hours control

* Implement FF and rewind schedule
  • Loading branch information
chrisbenincasa authored Feb 29, 2024
1 parent fe3e307 commit e8998c8
Show file tree
Hide file tree
Showing 7 changed files with 164 additions and 6 deletions.
49 changes: 49 additions & 0 deletions web2/src/components/channel_config/ChannelProgrammingConfig.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ import ChannelProgrammingList from './ChannelProgrammingList.tsx';
import ProgrammingSelectorDialog from './ProgrammingSelectorDialog.tsx';
import Delete from '@mui/icons-material/Delete';
import { useRemoveDuplicates } from '../../hooks/programming_controls/useRemoveDuplicates.ts';
import { range } from 'lodash-es';
import { useRestrictHours } from '../../hooks/programming_controls/useRestrictHours.ts';
import { FastForward, FastRewind } from '@mui/icons-material';
import {
useFastForwardSchedule,
useRewindSchedule,
} from '../../hooks/programming_controls/useSlideSchedule.ts';

// dayjs.extend(duration);

Expand All @@ -67,6 +74,10 @@ export function ChannelProgrammingConfig() {
};

const removeDuplicatePrograms = useRemoveDuplicates();
const restrictHours = useRestrictHours();

const fastForward = useFastForwardSchedule();
const rewind = useRewindSchedule();

const startTime = channel ? dayjs(channel.startTime) : dayjs();
const endTime = startTime.add(channel?.duration ?? 0, 'milliseconds');
Expand Down Expand Up @@ -206,6 +217,44 @@ export function ChannelProgrammingConfig() {
Duplicates
</Button>
</Grid2>
<Grid2 xs={3}>
{/* This should be in its own component */}
<Select>
{range(0, 24).map((hour) => (
<MenuItem key={hour}>{`${hour}:00`}</MenuItem>
))}
</Select>
<Select>
{range(0, 24).map((hour) => (
<MenuItem key={hour}>{`${hour}:00`}</MenuItem>
))}
</Select>
<Button
variant="contained"
startIcon={<Delete />}
onClick={() => restrictHours(5, 8)}
>
Restrict Hours
</Button>
</Grid2>
<Grid2 xs={3}>
<Button
variant="contained"
onClick={() => fastForward(60 * 1000)}
startIcon={<FastForward />}
>
Fast Forward
</Button>
</Grid2>
<Grid2 xs={3}>
<Button
variant="contained"
onClick={() => rewind(60 * 1000)}
startIcon={<FastRewind />}
>
Rewind
</Button>
</Grid2>
</Grid2>
</AccordionDetails>
</Accordion>
Expand Down
9 changes: 6 additions & 3 deletions web2/src/components/channel_config/ChannelProgrammingList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -257,9 +257,12 @@ export default function ChannelProgrammingList({

// Return a value that uniquely identifies this item.
// Typically this will be a UID of some sort.
return item.type != 'flex'
? channelProgramUniqueId(item)
: `flex-${item.originalIndex}`;
const key =
item.type != 'flex'
? channelProgramUniqueId(item)
: `flex-${item.originalIndex}`;

return `${key}_${item.startTimeOffset}`;
}

if (virtualListProps) {
Expand Down
1 change: 1 addition & 0 deletions web2/src/helpers/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const OneDayMillis = 1000 * 60 * 60 * 24;
14 changes: 11 additions & 3 deletions web2/src/helpers/util.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { ChannelProgram, Resolution } from '@tunarr/types';
import dayjs from 'dayjs';
import duration from 'dayjs/plugin/duration';
import { ChannelProgram, Resolution } from '@tunarr/types';
import { zipWith, range, isNumber } from 'lodash-es';
import { ChangeEvent } from 'react';
import { isNumber, range, zipWith } from 'lodash-es';

dayjs.extend(duration);

Expand Down Expand Up @@ -107,6 +106,15 @@ export const zipWithIndex = <T extends object>(
}));
};

export const createFlexProgram = (
duration: number,
persisted: boolean = false,
): FlexProgram => ({
duration,
persisted,
type: 'flex',
});

// Useful for toggling state
export const toggle = (b: boolean) => !b;

Expand Down
67 changes: 67 additions & 0 deletions web2/src/hooks/programming_controls/useRestrictHours.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { CondensedChannelProgram, isFlexProgram } from '@tunarr/types';
import dayjs from 'dayjs';
import duration from 'dayjs/plugin/duration';
import { forEach, inRange, reject } from 'lodash-es';
import { OneDayMillis } from '../../helpers/constants.ts';
import { createFlexProgram } from '../../helpers/util.ts';
import useStore from '../../store/index.ts';
import {
updateCurrentChannel,
setCurrentLineup,
} from '../../store/channelEditor/actions.ts';

dayjs.extend(duration);

export const restrictHours = (
programs: CondensedChannelProgram[],
from: number,
to: number,
) => {
if (!inRange(from, 0, 24) || !inRange(to, 0, 24) || to <= from) {
return { newStartTime: null, newPrograms: programs };
}

const newStartTime = dayjs().hour(from).minute(0).second(0).millisecond(0);
const startTimeOffset = dayjs.duration(from, 'hours').asMilliseconds();
const endTimeOffset = dayjs.duration(to, 'hours').asMilliseconds();
const maxDuration = endTimeOffset - startTimeOffset;
// Remove all flex and programs that will never fit in the restricted hours slot
const workingPrograms = reject(
programs,
(p) => isFlexProgram(p) || p.duration > maxDuration,
);

let currOffset = 0; // Offset from the channel start time
const newPrograms: CondensedChannelProgram[] = [];

forEach(workingPrograms, (program) => {
const timeLeft = maxDuration - currOffset;
if (program.duration > timeLeft) {
// Put the program back and try tomorrow.
// workingPrograms.unshift(program);
// Flex until the following day's start time
newPrograms.push(
createFlexProgram(timeLeft + OneDayMillis - maxDuration),
);
currOffset = 0;
}

newPrograms.push(program);
currOffset += program.duration;
});

return { newStartTime, newPrograms };
};

export const useRestrictHours = () => {
const programs = useStore((s) => s.channelEditor.programList);

return (from: number, to: number) => {
const { newStartTime, newPrograms } = restrictHours(programs, from, to);

if (newStartTime) {
updateCurrentChannel({ startTime: newStartTime.unix() * 1000 });
}
setCurrentLineup(newPrograms, true);
};
};
23 changes: 23 additions & 0 deletions web2/src/hooks/programming_controls/useSlideSchedule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { setChannelStartTime } from '../../store/channelEditor/actions.ts';
import useStore from '../../store/index.ts';
import { materializedProgramListSelector } from '../../store/selectors.ts';

const useSlide = () => {
const channel = useStore(({ channelEditor }) => channelEditor.currentEntity);
const programs = useStore(materializedProgramListSelector);
return (amount: number) => {
if (channel && amount !== 0 && programs.length > 0) {
setChannelStartTime(channel.startTime + amount);
}
};
};

export const useFastForwardSchedule = () => {
const slide = useSlide();
return (amount: number) => slide(amount < 0 ? amount : -amount);
};

export const useRewindSchedule = () => {
const slide = useSlide();
return (amount: number) => slide(Math.abs(amount));
};
7 changes: 7 additions & 0 deletions web2/src/store/channelEditor/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,13 @@ export const setCurrentChannel = (
}
});

export const changeChannelStartTime = (newStartTime: number) =>
useStore.setState(({ channelEditor }) => {
if (channelEditor.currentEntity) {
channelEditor.currentEntity.startTime = newStartTime;
}
});

export const setCurrentChannelProgramming = (
programming: CondensedChannelProgramming,
) =>
Expand Down

0 comments on commit e8998c8

Please sign in to comment.