Skip to content
This repository has been archived by the owner on Feb 11, 2024. It is now read-only.

Commit

Permalink
Merge pull request #4 from toplap/fix/non-uniform-streams
Browse files Browse the repository at this point in the history
fix: Show streams with non-uniform lengths
  • Loading branch information
munshkr authored Feb 9, 2024
2 parents e14ba30 + b22e9ca commit d55a310
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 74 deletions.
76 changes: 41 additions & 35 deletions src/app/components/EventPage.tsx
Original file line number Diff line number Diff line change
@@ -1,48 +1,54 @@
import React, { ReactElement, useEffect, useState } from 'react';
import React, { ReactElement, useEffect, useState } from "react";
import PerformanceList from "../components/PerformanceList";
import { MuxyEvents } from "../types";
import EventHeader from "./EventHeader";

function EventPage(): ReactElement {
const [muxyEvents, setMuxyEvents] = useState<MuxyEvents | null>(null);
const [reservedStreamCount, setReservedStreamCount] = useState<number | null>(null);
const [totalStreamCount, setTotalStreamCount] = useState<number | null>(null);
const [muxyEvents, setMuxyEvents] = useState<MuxyEvents | null>(null);
const [reservedStreamCount, setReservedStreamCount] = useState<number | null>(
null
);
const [totalStreamCount, setTotalStreamCount] = useState<number | null>(null);

const muxyApiKey: string = (process.env.REACT_APP_MUXY_API_KEY as string);
const muxyUrl: string = (process.env.REACT_APP_MUXY_URL as string);
const eventSlug: string = (process.env.REACT_APP_EVENT_SLUG as string);
const muxyApiKey: string = process.env.REACT_APP_MUXY_API_KEY as string;
const muxyUrl: string = process.env.REACT_APP_MUXY_URL as string;
const eventSlug: string = process.env.REACT_APP_EVENT_SLUG as string;

useEffect(() => {
fetch(`${muxyUrl}/events/?slug=${eventSlug}`, {
method: "get",
headers: new Headers({
Authorization: `Api-Key ${muxyApiKey}`,
}),
useEffect(() => {
fetch(`${muxyUrl}/events/?slug=${eventSlug}`, {
method: "get",
headers: new Headers({
Authorization: `Api-Key ${muxyApiKey}`,
}),
})
.then((res) => res.json())
.then((data) => {
setMuxyEvents(data);
})
.then((res) => res.json())
.then((data) => {
setMuxyEvents(data);
})
.catch(console.error);
}, [eventSlug]);
.catch(console.error);
}, [eventSlug]);

const event = muxyEvents?.results[0];
const event = muxyEvents?.results[0];

return (
<main className="App">
<EventHeader event={event} reservedStreamCount={reservedStreamCount} totalStreamCount={totalStreamCount}/>
{event && (
<PerformanceList
slug={event.slug}
eventUrl={event.url}
startsAt={event.starts_at}
endsAt={event.ends_at}
setReservedStreamCount={setReservedStreamCount}
setTotalStreamCount={setTotalStreamCount}
/>
)}
</main>
);
return (
<main className="App">
<EventHeader
event={event}
reservedStreamCount={reservedStreamCount}
totalStreamCount={totalStreamCount}
/>
{event && (
<PerformanceList
slug={event.slug}
eventUrl={event.url}
startsAt={event.starts_at}
endsAt={event.ends_at}
setReservedStreamCount={setReservedStreamCount}
setTotalStreamCount={setTotalStreamCount}
/>
)}
</main>
);
}

export default EventPage;
96 changes: 57 additions & 39 deletions src/app/components/PerformanceList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,62 +15,80 @@ interface Props {

const SLOT_DURATION_MIN = 15;

const PerformanceList = ({slug, eventUrl, startsAt, endsAt, setReservedStreamCount, setTotalStreamCount}: Props): ReactElement => {
const muxyApiKey: string = (process.env.REACT_APP_MUXY_API_KEY as string);
const muxyUrl: string = (process.env.REACT_APP_MUXY_URL as string);
const [muxyStreams, setMuxyStreams] = useState<MuxyStreams | null>(null);
const PerformanceList = ({
slug,
eventUrl,
startsAt,
endsAt,
setReservedStreamCount,
setTotalStreamCount,
}: Props): ReactElement => {
const muxyApiKey: string = process.env.REACT_APP_MUXY_API_KEY as string;
const muxyUrl: string = process.env.REACT_APP_MUXY_URL as string;
const [muxyStreams, setMuxyStreams] = useState<MuxyStreams | null>(null);

useEffect(() => {
fetch(`${muxyUrl}/streams/?event__slug=${slug}`, {
method: "get",
headers: new Headers({
Authorization: `Api-Key ${muxyApiKey}`,
}),
useEffect(() => {
fetch(`${muxyUrl}/streams/?event__slug=${slug}`, {
method: "get",
headers: new Headers({
Authorization: `Api-Key ${muxyApiKey}`,
}),
})
.then((res) => res.json())
.then((data) => {
setMuxyStreams(data);
})
.then((res) => res.json())
.then((data) => {
setMuxyStreams(data);
})
.catch(console.error);
}, [slug]);
.catch(console.error);
}, [slug]);

const allStreams: (MuxyStream | EmptyMuxyStream)[] = useMemo(() => {
if (!startsAt || !endsAt) return [];
if (!muxyStreams) return [];

// eslint-disable-next-line no-debugger
const results = muxyStreams?.results || [];

// Sort streams by start time (just in case)
const sortedStreams = results.sort((a, b) => {
return a.starts_at.localeCompare(b.starts_at);
});

const startsAtDt = DateTime.fromISO(startsAt);
const endsAtDt = DateTime.fromISO(endsAt);

const diff = endsAtDt.diff(startsAtDt, ["minute"]).toObject();
const numSlots = diff?.minutes ? diff.minutes / SLOT_DURATION_MIN : 0;
const allSlots = [];

const results = muxyStreams?.results || [];
// Try to create empty slots for every SLOT_DURATION_MIN minutes If there is
// already a stream that fits in the slot (or overlaps with it), use it and
// continue from the end of it. Otherwise create an empty slot.
let slotAt = startsAtDt;
while (slotAt < endsAtDt) {
const nextSlotAt = slotAt.plus({ minutes: SLOT_DURATION_MIN });

// Build slots array
const slots = Array.from(Array(numSlots)).map((_, i) => {
const streamStartsAtDt = startsAtDt.plus({
minutes: i * SLOT_DURATION_MIN,
});
const streamEndsAtDt = streamStartsAtDt.plus({
minutes: SLOT_DURATION_MIN,
// Find the first stream that fits in the slot or overlaps with it
const stream = sortedStreams.find((stream) => {
const streamStartsAtDt = DateTime.fromISO(stream.starts_at);
const streamEndsAtDt = DateTime.fromISO(stream.ends_at);
return slotAt >= streamStartsAtDt && nextSlotAt <= streamEndsAtDt;
});

const streamStartsAt = streamStartsAtDt.toUTC().toFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
const streamEndsAt = streamEndsAtDt.toUTC().toFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")
const stream = results.find(
(stream) =>
stream.starts_at == streamStartsAt && stream.ends_at == streamEndsAt
);

return stream || { starts_at: streamStartsAt, ends_at: streamEndsAt };
});
// If no stream fits in the slot, create an empty slot
if (!stream) {
allSlots.push({
starts_at: slotAt.toUTC().toFormat("yyyy-MM-dd'T'HH:mm:ss'Z"),
ends_at: nextSlotAt.toUTC().toFormat("yyyy-MM-dd'T'HH:mm:ss'Z"),
});
slotAt = nextSlotAt;
} else {
allSlots.push(stream);
slotAt = DateTime.fromISO(stream.ends_at);
}
}

return slots
return allSlots;
}, [muxyStreams]);

setReservedStreamCount(muxyStreams ? muxyStreams.results.length: 0);
setTotalStreamCount(allStreams ? allStreams.length: 0);
setReservedStreamCount(muxyStreams ? muxyStreams.results.length : 0);
setTotalStreamCount(allStreams ? allStreams.length : 0);

return (
<div className="performance-list">
Expand Down

0 comments on commit d55a310

Please sign in to comment.