From 7bee8b4f8ceedc301769037ba1a29389b59bc4aa Mon Sep 17 00:00:00 2001 From: Brian Engert Date: Sun, 6 Nov 2022 17:26:55 -0600 Subject: [PATCH] feat: fluid player and skip forward/back buttons --- package-lock.json | 64 ++++++++++++++++++++++++++++++++++--- package.json | 3 ++ src/app.css | 18 +++++++++++ src/components/Playback.tsx | 2 +- src/components/VideoJS.tsx | 24 ++++++++------ src/main.tsx | 1 + src/pages/Home.tsx | 4 +-- src/types/Tivo.ts | 2 +- 8 files changed, 100 insertions(+), 18 deletions(-) create mode 100644 src/app.css diff --git a/package-lock.json b/package-lock.json index 9736038..6f51729 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,8 @@ "react-dom": "^17.0.2", "react-router-dom": "^6.2.2", "video.js": "^7.20.3", + "videojs-font": "^4.0.0", + "videojs-seek-buttons": "^3.0.1", "zod": "^3.19.1" }, "devDependencies": { @@ -28,6 +30,7 @@ "@types/react": "^17.0.43", "@types/react-dom": "^17.0.14", "@types/video.js": "^7.3.49", + "@types/videojs-seek-buttons": "^2.1.0", "@vitejs/plugin-react": "^1.3.0", "eslint": "^8.10.0", "eslint-config-dasprid": "^0.1.8", @@ -1548,6 +1551,15 @@ "integrity": "sha512-GtBMH+rm7yyw5DAK7ycQeEd35x/EYoLK/49op+CqDDoNUm9XJEVOfb+EARKKe4TwP5jkaikjWqf5RFjmw8yHoQ==", "dev": true }, + "node_modules/@types/videojs-seek-buttons": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/videojs-seek-buttons/-/videojs-seek-buttons-2.1.0.tgz", + "integrity": "sha512-vaTCELmPea/cgkf82P8RaeFBBZnd4nnuOiMaciSeMKiWzbflSvcvjnHfvKcBmJEi3hnSQ12bVXlmUvvOrgmpjQ==", + "dev": true, + "dependencies": { + "@types/video.js": "*" + } + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "5.40.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.40.0.tgz", @@ -6617,11 +6629,31 @@ "videojs-vtt.js": "^0.15.4" } }, - "node_modules/videojs-font": { + "node_modules/video.js/node_modules/videojs-font": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/videojs-font/-/videojs-font-3.2.0.tgz", "integrity": "sha512-g8vHMKK2/JGorSfqAZQUmYYNnXmfec4MLhwtEFS+mMs2IDY398GLysy6BH6K+aS1KMNu/xWZ8Sue/X/mdQPliA==" }, + "node_modules/videojs-font": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/videojs-font/-/videojs-font-4.0.0.tgz", + "integrity": "sha512-sRXrizXF0zBMatXjg2vGpn63G26uH3XqwyZ9PjU2H9xqGm7fRSVYuxOJCUME6us/1rFl9yxkRKk31WTQ7XZkww==" + }, + "node_modules/videojs-seek-buttons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/videojs-seek-buttons/-/videojs-seek-buttons-3.0.1.tgz", + "integrity": "sha512-scVWOqCMqHajlbwYZIzJ5nBYkDXTAhEpWjfcdCu8ykksA1barrKnEKdQvS84TtDWOx6UXDD/e/x0acYEZCDMEQ==", + "dependencies": { + "global": "^4.4.0" + }, + "engines": { + "node": ">=14", + "npm": ">=6" + }, + "peerDependencies": { + "video.js": "^6 || ^7" + } + }, "node_modules/videojs-vtt.js": { "version": "0.15.4", "resolved": "https://registry.npmjs.org/videojs-vtt.js/-/videojs-vtt.js-0.15.4.tgz", @@ -7997,6 +8029,15 @@ "integrity": "sha512-GtBMH+rm7yyw5DAK7ycQeEd35x/EYoLK/49op+CqDDoNUm9XJEVOfb+EARKKe4TwP5jkaikjWqf5RFjmw8yHoQ==", "dev": true }, + "@types/videojs-seek-buttons": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/videojs-seek-buttons/-/videojs-seek-buttons-2.1.0.tgz", + "integrity": "sha512-vaTCELmPea/cgkf82P8RaeFBBZnd4nnuOiMaciSeMKiWzbflSvcvjnHfvKcBmJEi3hnSQ12bVXlmUvvOrgmpjQ==", + "dev": true, + "requires": { + "@types/video.js": "*" + } + }, "@typescript-eslint/eslint-plugin": { "version": "5.40.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.40.0.tgz", @@ -11553,12 +11594,27 @@ "safe-json-parse": "4.0.0", "videojs-font": "3.2.0", "videojs-vtt.js": "^0.15.4" + }, + "dependencies": { + "videojs-font": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/videojs-font/-/videojs-font-3.2.0.tgz", + "integrity": "sha512-g8vHMKK2/JGorSfqAZQUmYYNnXmfec4MLhwtEFS+mMs2IDY398GLysy6BH6K+aS1KMNu/xWZ8Sue/X/mdQPliA==" + } } }, "videojs-font": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/videojs-font/-/videojs-font-3.2.0.tgz", - "integrity": "sha512-g8vHMKK2/JGorSfqAZQUmYYNnXmfec4MLhwtEFS+mMs2IDY398GLysy6BH6K+aS1KMNu/xWZ8Sue/X/mdQPliA==" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/videojs-font/-/videojs-font-4.0.0.tgz", + "integrity": "sha512-sRXrizXF0zBMatXjg2vGpn63G26uH3XqwyZ9PjU2H9xqGm7fRSVYuxOJCUME6us/1rFl9yxkRKk31WTQ7XZkww==" + }, + "videojs-seek-buttons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/videojs-seek-buttons/-/videojs-seek-buttons-3.0.1.tgz", + "integrity": "sha512-scVWOqCMqHajlbwYZIzJ5nBYkDXTAhEpWjfcdCu8ykksA1barrKnEKdQvS84TtDWOx6UXDD/e/x0acYEZCDMEQ==", + "requires": { + "global": "^4.4.0" + } }, "videojs-vtt.js": { "version": "0.15.4", diff --git a/package.json b/package.json index 991379f..20a3a0c 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "@types/react": "^17.0.43", "@types/react-dom": "^17.0.14", "@types/video.js": "^7.3.49", + "@types/videojs-seek-buttons": "^2.1.0", "@vitejs/plugin-react": "^1.3.0", "eslint": "^8.10.0", "eslint-config-dasprid": "^0.1.8", @@ -44,6 +45,8 @@ "react-dom": "^17.0.2", "react-router-dom": "^6.2.2", "video.js": "^7.20.3", + "videojs-font": "^4.0.0", + "videojs-seek-buttons": "^3.0.1", "zod": "^3.19.1" } } diff --git a/src/app.css b/src/app.css new file mode 100644 index 0000000..92243c7 --- /dev/null +++ b/src/app.css @@ -0,0 +1,18 @@ +button.vjs-seek-button.skip-back { + font-family: VideoJS; + font-weight: normal; + font-style: normal; + cursor: pointer; +} +button.vjs-seek-button.skip-back :before { + content: "\f11b"; } + +button.vjs-seek-button.skip-forward { + font-family: VideoJS; + font-weight: normal; + font-style: normal; + cursor: pointer; +} +button.vjs-seek-button.skip-forward :before { + content: "\f121"; +} \ No newline at end of file diff --git a/src/components/Playback.tsx b/src/components/Playback.tsx index e9501da..c4ed6b6 100644 --- a/src/components/Playback.tsx +++ b/src/components/Playback.tsx @@ -94,7 +94,7 @@ const Playback = ({openState, close, recording} : Props) : JSX.Element => { return <>; } - const episode = recording.episodeNum.length > 0 ? `S${recording.seasonNumber} E${recording.episodeNum.join(',')} ` : ''; + const episode = recording.episodeNum !== undefined ? `S${recording.seasonNumber} E${recording.episodeNum.join(',')} ` : ''; const secondary = `${episode}${recording.subtitle}`; return ( void; }; -export const VideoJS = (props : Props) => { - const videoRef = React.useRef(null); - const playerRef = React.useRef(null); +const VideoJS = (props : Props) => { + const videoRef = useRef(null); + const playerRef = useRef(null); const {options, onReady} = props; - React.useEffect(() => { + useEffect(() => { // Make sure Video.js player is only initialized once if (!playerRef.current) { const videoElement = videoRef.current; @@ -24,8 +26,12 @@ export const VideoJS = (props : Props) => { onReady && onReady(player); }); - // You could update an existing player in the `else` block here - // on prop change, for example: + if (options.liveui) { + player.seekButtons({ + forward: 30, + back: 10 + }) + } } else { // const player = playerRef.current; @@ -35,7 +41,7 @@ export const VideoJS = (props : Props) => { }, [options, videoRef]); // Dispose the Video.js player when the functional component unmounts - React.useEffect(() => { + useEffect(() => { const player = playerRef.current; return () => { @@ -53,7 +59,7 @@ export const VideoJS = (props : Props) => { style={{ width: '100%', }} - className='video-js vjs-big-play-centered' + className='video-js vjs-big-play-centered vjs-fluid' /> ); diff --git a/src/main.tsx b/src/main.tsx index 735aef0..a7e1e43 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -2,6 +2,7 @@ import '@fontsource/roboto/300.css'; import '@fontsource/roboto/400.css'; import '@fontsource/roboto/500.css'; import '@fontsource/roboto/700.css'; +import './app.css'; import {createTheme, CssBaseline} from '@mui/material'; import {ThemeProvider} from '@mui/material/styles'; import {StrictMode} from 'react'; diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index f802da5..29a86e4 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -68,11 +68,9 @@ const Home = () : JSX.Element => { 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)); }); }, []); - console.log('uniqueCollections', uniqueCollections); return ( <> @@ -103,7 +101,7 @@ const Home = () : JSX.Element => { {collectionRecordings.map(recording => { const recordingStart = new Date(recording.scheduledStartTime).toLocaleString(); - const episode = recording.episodeNum.length > 0 ? `S${recording.seasonNumber} E${recording.episodeNum.join(',')} ` : ''; + const episode = recording.episodeNum?.length !== undefined ? `S${recording.seasonNumber} E${recording.episodeNum.join(',')} ` : ''; const secondary = `${episode}${recording.subtitle}`; return