Skip to content

Commit

Permalink
Allow program order selection in time slot config. Fixes #251 (#393)
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisbenincasa authored Apr 27, 2024
1 parent e01be5a commit bc8a548
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 12 deletions.
6 changes: 5 additions & 1 deletion shared/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ExternalId } from '@tunarr/types';
import { ExternalId, MultiExternalId } from '@tunarr/types';

export { scheduleRandomSlots } from './services/randomSlotsService.js';
export { scheduleTimeSlots } from './services/timeSlotService.js';
Expand All @@ -13,6 +13,10 @@ export function createExternalId(
return `${sourceType}|${sourceId}|${itemId}`;
}

export function createExternalIdFromMulti(multi: MultiExternalId) {
return createExternalId(multi.source, multi.sourceId, multi.id);
}

// We could type this better if we reuse the other ExternalId
// types in createExternalId
export function containsMultiExternalId(
Expand Down
3 changes: 2 additions & 1 deletion shared/src/services/ProgramIterator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export class ProgramOrderer implements ProgramIterator {
orderer: (program: ContentProgram) => string | number = getProgramOrder,
) {
this.#programs = sortBy(programs, orderer);
console.log(this.#programs);
}

current(): ChannelProgram | null {
Expand All @@ -80,7 +81,7 @@ export function getProgramOrder(program: ContentProgram): string | number {
return program.title;
case 'episode':
// Hacky thing from original code...
return program.seasonNumber! * 100000 + program.episodeNumber!;
return program.seasonNumber! * 1e5 + program.episodeNumber!;
case 'track':
// A-z for now
return program.title;
Expand Down
53 changes: 46 additions & 7 deletions shared/src/services/slotSchedulerUtil.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,27 @@
import {
ChannelProgram,
MultiExternalId,
isContentProgram,
isRedirectProgram,
} from '@tunarr/types';
import { TimeSlot, RandomSlot } from '@tunarr/types/api';
import { reduce, isNull, first } from 'lodash-es';
import { RandomSlot, TimeSlot } from '@tunarr/types/api';
import {
slotIteratorKey,
StaticProgramIterator,
filter,
first,
forEach,
isNull,
isUndefined,
map,
reduce,
some,
} from 'lodash-es';
import { createExternalIdFromMulti } from '../index.js';
import {
ProgramIterator,
ProgramOrderer,
ProgramShuffler,
ProgramIterator,
StaticProgramIterator,
slotIteratorKey,
} from './ProgramIterator.js';

export type SlotLike = {
Expand Down Expand Up @@ -101,10 +112,38 @@ export function createProgramIterators(
}
} else {
const programs = programBySlotType['content'][slotId] ?? [];
// Remove any duplicates.
// We don't need to go through and remove flex since
// they will just be ignored during schedule generation
const seenDBIds = new Set<string>();
const seenIds = new Set<string>();
const uniquePrograms = filter(programs, (p) => {
if (p.persisted && !isUndefined(p.id) && !seenDBIds.has(p.id)) {
seenDBIds.add(p.id);
forEach(p.externalIds, (eid) => {
if (eid.type === 'multi') {
seenIds.add(createExternalIdFromMulti(eid));
}
});
return true;
}

const externalIds = filter(
p.externalIds,
(eid): eid is MultiExternalId => eid.type === 'multi',
);
const eids = map(externalIds, createExternalIdFromMulti);
if (some(eids, (eid) => seenIds.has(eid))) {
return false;
}

forEach(eids, (eid) => seenIds.add(eid));
return true;
});
acc[id] =
slot.order === 'next'
? new ProgramOrderer(programs)
: new ProgramShuffler(programs);
? new ProgramOrderer(uniquePrograms)
: new ProgramShuffler(uniquePrograms);
}
}

Expand Down
17 changes: 15 additions & 2 deletions web/src/components/channel_config/ChannelProgrammingList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,21 @@ const programListItemTitleFormatter = (() => {
switch (p.subtype) {
case 'movie':
return p.title;
case 'episode':
return p.episodeTitle ? `${p.title} - ${p.episodeTitle}` : p.title;
case 'episode': {
// TODO: this makes some assumptions about number of seasons
// and episodes... it may break
const epPart =
p.seasonNumber && p.episodeNumber
? ` S${p.seasonNumber
.toString()
.padStart(2, '0')}E${p.episodeNumber
.toString()
.padStart(2, '0')}`
: '';
return p.episodeTitle
? `${p.title}${epPart} - ${p.episodeTitle}`
: p.title;
}
case 'track': {
return join(
reject(
Expand Down
40 changes: 39 additions & 1 deletion web/src/pages/channels/TimeSlotEditorPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,17 @@ const defaultTimeSlotSchedule: TimeSlotSchedule = {
timeZoneOffset: new Date().getTimezoneOffset(),
};

const showOrderOptions = [
{
value: 'next',
description: 'Next Episode',
},
{
value: 'shuffle',
description: 'Shuffle',
},
];

const lineupItemAppearsInSchedule = (
slots: TimeSlot[],
item: ChannelProgram,
Expand Down Expand Up @@ -314,8 +325,15 @@ const TimeSlotRow = ({
break;
}
}
const showInputSize = currentPeriod === 'week' ? 7 : 9;

const isShowType = slot.programming.type === 'show';
let showInputSize = currentPeriod === 'week' ? 7 : 9;
if (isShowType) {
showInputSize -= 3;
}

const dayOfTheWeek = Math.floor(slot.startTime / OneDayMillis);

return (
<Fragment key={`${slot.startTime}_${index}`}>
{currentPeriod === 'week' ? (
Expand Down Expand Up @@ -358,6 +376,26 @@ const TimeSlotRow = ({
</Select>
</FormControl>
</Grid>
{isShowType && (
<Grid item xs={3}>
<FormControl fullWidth>
<InputLabel>Order</InputLabel>
<Controller
control={control}
name={`slots.${index}.order`}
render={({ field }) => (
<Select label="Order" {...field}>
{map(showOrderOptions, ({ description, value }) => (
<MenuItem key={value} value={value}>
{description}
</MenuItem>
))}
</Select>
)}
/>
</FormControl>
</Grid>
)}
<Grid item xs={1}>
<IconButton onClick={() => removeSlot(index)} color="error">
<Delete />
Expand Down

0 comments on commit bc8a548

Please sign in to comment.