(null);
+ const {options, onReady} = props;
+
+ React.useEffect(() => {
+ // Make sure Video.js player is only initialized once
+ if (!playerRef.current) {
+ const videoElement = videoRef.current;
+
+ if (!videoElement) return;
+
+ const player = playerRef.current = videojs(videoElement, options, () => {
+ videojs.log('player is ready');
+ onReady && onReady(player);
+ });
+
+ // You could update an existing player in the `else` block here
+ // on prop change, for example:
+ } else {
+ // const player = playerRef.current;
+
+ // player.autoplay(options.autoplay);
+ // player.src(options.sources);
+ }
+ }, [options, videoRef]);
+
+ // Dispose the Video.js player when the functional component unmounts
+ React.useEffect(() => {
+ const player = playerRef.current;
+
+ return () => {
+ if (player) {
+ player.dispose();
+ playerRef.current = null;
+ }
+ };
+ }, [playerRef]);
+
+ return (
+
+
+
+ );
+};
+
+export default VideoJS;
diff --git a/src/main.tsx b/src/main.tsx
index 6cadac6..735aef0 100644
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -7,8 +7,8 @@ import {ThemeProvider} from '@mui/material/styles';
import {StrictMode} from 'react';
import {render} from 'react-dom';
import App from './App';
-import { AuthProvider } from './components/AuthProvider';
-import { TivoContextComponent } from './components/TivoContext';
+import {AuthProvider} from './components/AuthProvider';
+import {TivoContextComponent} from './components/TivoContext';
const theme = createTheme();
diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx
index 8aa2992..f802da5 100644
--- a/src/pages/Home.tsx
+++ b/src/pages/Home.tsx
@@ -1,145 +1,145 @@
-import Typography from '@mui/material/Typography';
+import ChevronRightIcon from '@mui/icons-material/ChevronRight';
+import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
+import {Box, Tabs, Tab, Chip} from '@mui/material';
+import Accordion from '@mui/material/Accordion';
+import AccordionDetails from '@mui/material/AccordionDetails';
+import AccordionSummary from '@mui/material/AccordionSummary';
import List from '@mui/material/List';
import ListItem from '@mui/material/ListItem';
import ListItemButton from '@mui/material/ListItemButton';
import ListItemText from '@mui/material/ListItemText';
-import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
-import ChevronRightIcon from "@mui/icons-material/ChevronRight";
-import Accordion from '@mui/material/Accordion';
-import AccordionSummary from '@mui/material/AccordionSummary';
-import AccordionDetails from '@mui/material/AccordionDetails';
-import type { Channel, Recording } from '@/types/Tivo';
-import { useFetch } from '@/util/api';
-import { lazy, useEffect, useState, Suspense} from 'react';
-import { Box, Tabs, Tab, Chip} from '@mui/material';
+import Typography from '@mui/material/Typography';
import Container from '@mui/system/Container';
+import {lazy, useEffect, useState, Suspense} from 'react';
+import type {Channel, Recording} from '@/types/Tivo';
+import {useFetch} from '@/util/api';
-const Playback = lazy(() => import('@/components/Playback'));
-const ChannelComponent = lazy(() => import('@/components/ChannelComponent'));
+const Playback = lazy(async () => import('@/components/Playback'));
+const ChannelComponent = lazy(async () => import('@/components/ChannelComponent'));
-interface TabPanelProps {
- children?: React.ReactNode;
- index: number;
- value: number;
- }
+type TabPanelProps = {
+ children ?: React.ReactNode;
+ index : number;
+ value : number;
+};
+
+function TabPanel(props : TabPanelProps) {
+ const {children, value, index, ...other} = props;
-function TabPanel(props: TabPanelProps) {
- const { children, value, index, ...other } = props;
-
return (
-
- {value === index && (
-
- {children}
-
- )}
-
+
+ {value === index && (
+
+ {children}
+
+ )}
+
);
- }
+}
const Home = () : JSX.Element => {
const [recordings, setRecordings] = useState([]);
const [uniqueCollections, setUniqueCollections] = useState([]);
const [channels, setChannels] = useState([]);
- const [selectedRecording, setSelectedRecording] = useState(null);
- const [selectedChannel, setSelectedChannel] = useState(null);
+ const [selectedRecording, setSelectedRecording] = useState(null);
+ const [selectedChannel, setSelectedChannel] = useState(null);
const [tab, setTab] = useState(0);
const fetch = useFetch();
- const changeTab = (event: React.SyntheticEvent, newValue: number) => {
+ const changeTab = (event : React.SyntheticEvent, newValue : number) => {
setSelectedChannel(null);
setSelectedRecording(null);
setTab(newValue);
};
useEffect(() => {
- fetch('/getMyShows').then(async (rec) => {
+ fetch('/getMyShows').then(async rec => {
const rawRecordings = await rec.json() as Recording[];
setUniqueCollections(rawRecordings.filter((r, i) => rawRecordings.findIndex(or => or.collectionId === r.collectionId) === i));
setRecordings(rawRecordings);
});
- fetch('/getMyLineup').then(async (rec) => {
+ fetch('/getMyLineup').then(async rec => {
const allChannels = await rec.json();
const channelList = allChannels.channel as Channel[];
- setChannels(channelList.filter((c) => c.isReceived));
- console.log('channels', channelList.filter((c) => c.isReceived));
+ setChannels(channelList.filter(c => c.isReceived));
+ console.log('channels', channelList.filter(c => c.isReceived));
});
- },[]);
+ }, []);
console.log('uniqueCollections', uniqueCollections);
return (
<>
Home
-
-
-
-
-
+
+
+
+
+
-
- {uniqueCollections.map((c, i) => {
- const collectionRecordings = recordings.filter(r => r.collectionId === c.collectionId).reverse();
- const latestRecording = collectionRecordings.length ?
- new Date(collectionRecordings[0].actualStartTime).toLocaleDateString() :
- '';
- return
- }
- aria-controls="panel1a-content"
- id="panel1a-header"
- >
-
- {c.collectionTitle} {latestRecording}
-
-
-
- {collectionRecordings.map(recording => {
- const recordingStart = new Date(recording.scheduledStartTime).toLocaleString();
- const episode = recording.episodeNum?.length > 0 ? `S${recording.seasonNumber} E${recording.episodeNum.join(',')} ` : ``;
- const secondary = `${episode}${recording.subtitle}`;
- return
- {
- console.log('recording', recording);
- setSelectedRecording(recording);
- }}
- >
-
-
-
- })}
-
-
-
- })}
+
+ {uniqueCollections.map((c, i) => {
+ const collectionRecordings = recordings.filter(r => r.collectionId === c.collectionId).reverse();
+ const latestRecording = collectionRecordings.length
+ ? new Date(collectionRecordings[0].actualStartTime).toLocaleDateString()
+ : '';
+ return
+ }
+ aria-controls="panel1a-content"
+ id="panel1a-header"
+ >
+
+ {c.collectionTitle} {latestRecording}
+
+
+
+ {collectionRecordings.map(recording => {
+ const recordingStart = new Date(recording.scheduledStartTime).toLocaleString();
+ const episode = recording.episodeNum.length > 0 ? `S${recording.seasonNumber} E${recording.episodeNum.join(',')} ` : '';
+ const secondary = `${episode}${recording.subtitle}`;
+ return
+ {
+ console.log('recording', recording);
+ setSelectedRecording(recording);
+ }}
+ >
+
+
+ ;
+ })}
+
+
+ ;
+ })}
- {channels.map((channel) => {
+ {channels.map(channel => {
return
- {
- console.log('channel', channel);
- setSelectedChannel(channel);
- }}
- >
-
-
-
+ {
+ console.log('channel', channel);
+ setSelectedChannel(channel);
+ }}
+ >
+
+
+ ;
})}
@@ -147,18 +147,22 @@ const Home = () : JSX.Element => {
}>
{setSelectedRecording(null)}}
+ close={() => {
+ setSelectedRecording(null);
+ }}
recording={selectedRecording}
/>
}>
{setSelectedChannel(null)}}
+ close={() => {
+ setSelectedChannel(null);
+ }}
channel={selectedChannel}
/>
-
+
>
);
};
diff --git a/src/types/Tivo.ts b/src/types/Tivo.ts
index 05e8419..01b6eec 100644
--- a/src/types/Tivo.ts
+++ b/src/types/Tivo.ts
@@ -3,7 +3,7 @@ export type Recording = {
isNew : boolean;
shortTitle : string;
subtitle : string;
- description: string;
+ description : string;
seasonNumber : number;
collectionTitle : string;
@@ -16,7 +16,7 @@ export type Recording = {
isEpisode : boolean;
duration : number;
- hdtv: boolean;
+ hdtv : boolean;
size : number;
actualStartTime : string;
@@ -33,7 +33,7 @@ export type Recording = {
recordingPlaybackPolicy : string;
tivoToGo : boolean;
type : string;
- }
+ };
};
export type MyShows = {
@@ -42,25 +42,25 @@ export type MyShows = {
type : string;
IsFinal : boolean;
isBottom : boolean;
-}
+};
export type Channel = {
- affiliate: string;
- callSign: string;
- channelId: string;
- channelNumber: string;
- isKidZone: boolean;
- isReceived: boolean;
- name: string;
- sourceType: string;
- stationId: string;
+ affiliate : string;
+ callSign : string;
+ channelId : string;
+ channelNumber : string;
+ isKidZone : boolean;
+ isReceived : boolean;
+ name : string;
+ sourceType : string;
+ stationId : string;
isDigital ?: boolean;
logoIndex ?: number;
- isBlocked: boolean;
- objectIdAndType: string;
- isHdtv: boolean;
- isEntitled: boolean;
- videoResolution: string;
- type: string;
- stbChannelId: string;
-}
\ No newline at end of file
+ isBlocked : boolean;
+ objectIdAndType : string;
+ isHdtv : boolean;
+ isEntitled : boolean;
+ videoResolution : string;
+ type : string;
+ stbChannelId : string;
+};
diff --git a/src/util/api.ts b/src/util/api.ts
index d7ddf6f..951078b 100644
--- a/src/util/api.ts
+++ b/src/util/api.ts
@@ -1,18 +1,20 @@
-import { tivoContext } from '@/components/TivoContext';
-import { useAuth0 } from '@auth0/auth0-react';
-import { useContext } from 'react';
+import {useAuth0} from '@auth0/auth0-react';
+import {useContext} from 'react';
+import {tivoContext} from '@/components/TivoContext';
export const useFetch = () => {
- const { getAccessTokenSilently } = useAuth0();
+ const {getAccessTokenSilently} = useAuth0();
const context = useContext(tivoContext);
-
+
return async (url : string | URL, options : RequestInit | undefined = {}) => {
const token = await getAccessTokenSilently();
+
if (!options) {
options = {};
}
+
options.headers = new Headers(options.headers);
options.headers.append('Authorization', 'Bearer ' + token);
return await fetch((context?.apiBaseUrl ?? '') + url.toString(), options);
- }
-}
\ No newline at end of file
+ };
+};
diff --git a/vite.config.ts b/vite.config.ts
index f925726..289c478 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -1,4 +1,4 @@
-import { Output } from '@mui/icons-material';
+import {Output} from '@mui/icons-material';
import react from '@vitejs/plugin-react';
import {visualizer} from 'rollup-plugin-visualizer';
import {defineConfig} from 'vite';
@@ -26,6 +26,6 @@ export default defineConfig({
'^/session-streaming/.*': {
target: 'http://localhost:8000',
},
- }
- }
+ },
+ },
});