Advertisement
shapoval

VideoPlayer.ts

May 25th, 2022
1,396
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. import React from "react";
  2.  
  3. import * as Sentry from "@sentry/nextjs";
  4. import classnames from "classnames";
  5. import classnamesBind from "classnames/bind";
  6. import isEqual from "lodash/isEqual";
  7. import videojs from "video.js";
  8.  
  9. import "videojs-contrib-quality-levels";
  10. import "videojs-hls-quality-selector";
  11. import "videojs-seek-buttons";
  12.  
  13. require("videojs-hotkeys");
  14.  
  15. // todo use import after rewriting to ts
  16. require("../../../helpers/videojs-thumbnails");
  17.  
  18. import { getCookiesLocalStorageValue, setCookiesLocalStorageValue } from "../../../helpers/CookiesLocalStorage";
  19.  
  20. import Loader from "../Loader";
  21.  
  22. import styles from "./VideoPlayer.module.scss";
  23.  
  24. const cx = classnamesBind.bind(styles);
  25.  
  26. const initialOptions: videojs.PlayerOptions = {
  27.     controls: true,
  28.     fluid: true,
  29.     preload: "auto",
  30.     controlBar: {
  31.         volumePanel: {
  32.             inline: false,
  33.         },
  34.     },
  35. };
  36.  
  37. interface VideoPlayerProps extends React.VideoHTMLAttributes<HTMLVideoElement> {
  38.     options: videojs.PlayerOptions;
  39.     thumbnailTemplate?: string;
  40.     onReady?: (player: videojs.Player) => void;
  41.     seekSeconds?: number;
  42.     isLoading?: boolean;
  43. }
  44.  
  45. function VideoPlayer(props: VideoPlayerProps): React.ReactElement {
  46.     const { options, thumbnailTemplate, onReady, seekSeconds, isLoading = false, ...rest } = props;
  47.  
  48.     const videoRef = React.useRef<HTMLVideoElement>(null);
  49.     const playerRef = React.useRef<videojs.Player | null>(null);
  50.  
  51.     React.useEffect(() => {
  52.         // Make sure Video.js player is only initialized once
  53.         if (!playerRef.current) {
  54.             const videoElement = videoRef.current;
  55.  
  56.             if (!videoElement) return;
  57.  
  58.             const allOptions = {
  59.                 ...initialOptions,
  60.                 ...options,
  61.             };
  62.  
  63.             if (allOptions.sources && allOptions.sources.length > 0) {
  64.                 try {
  65.                     const player = playerRef.current = videojs(videoElement, allOptions, () => {
  66.                         onReady && onReady(player);
  67.                     });
  68.  
  69.                     const playerSavedVolume = getCookiesLocalStorageValue("videojs:volume");
  70.  
  71.                     if (playerSavedVolume !== null) {
  72.                         if (playerSavedVolume === "muted") {
  73.                             player.muted(true);
  74.                         } else if (playerSavedVolume) {
  75.                             player.volume(parseFloat(playerSavedVolume));
  76.                         }
  77.                     }
  78.                 }  catch (e) {
  79.                     Sentry.captureException(e);
  80.                 }
  81.  
  82.                 try {
  83.                     playerRef.current?.on("error", (err) => {
  84.                         Sentry.captureException(err);
  85.                     });
  86.                 } catch (e) {
  87.                     console.error(e);
  88.                 }
  89.  
  90.                 try {
  91.                     playerRef.current?.on("volumechange", function() {
  92.                         if (playerRef.current) {
  93.                             setCookiesLocalStorageValue(
  94.                                 "videojs:volume",
  95.                                 playerRef.current.muted() ? "muted" : playerRef.current.volume().toString()
  96.                             );
  97.                         }
  98.                     });
  99.                 } catch (e) {
  100.                     Sentry.captureException(e);
  101.                 }
  102.  
  103.                 try {
  104.                     playerRef.current?.hlsQualitySelector({
  105.                         vjsIconClass: "vjs-icon-cog",
  106.                     });
  107.                 } catch (e) {
  108.                     Sentry.captureException(e);
  109.                 }
  110.  
  111.                 if (thumbnailTemplate) {
  112.                     try {
  113.                         playerRef.current?.thumbnails({
  114.                             width: 142,
  115.                             height: 80,
  116.                             preload: true,
  117.                             interval: 5,
  118.                             template: thumbnailTemplate,
  119.                         });
  120.                     } catch (e) {
  121.                         Sentry.captureException(e);
  122.                     }
  123.                 }
  124.  
  125.                 try {
  126.                     playerRef.current?.seekButtons({
  127.                         back: seekSeconds ?? 15,
  128.                         forward: seekSeconds ?? 30,
  129.                     });
  130.                 } catch (e) {
  131.                     Sentry.captureException(e);
  132.                 }
  133.  
  134.                 try {
  135.                     playerRef.current?.on("ended", () => {
  136.                         if (playerRef.current?.isFullscreen()) {
  137.                             playerRef.current?.exitFullscreen();
  138.                         }
  139.                         playerRef.current?.currentTime(0);
  140.                         playerRef.current?.hasStarted(false);
  141.                         playerRef.current?.trigger("ready");
  142.                     });
  143.                 } catch (e) {
  144.                     Sentry.captureException(e);
  145.                 }
  146.  
  147.                 try {
  148.                     playerRef.current?.on("touchstart", (e) => {
  149.                         if (e.target.nodeName === "VIDEO") {
  150.                             if (playerRef.current?.paused()) {
  151.                                 playerRef.current?.play();
  152.                             } else {
  153.                                 playerRef.current?.pause();
  154.                             }
  155.                         }
  156.                     });
  157.                 } catch (e) {
  158.                     Sentry.captureException(e);
  159.                 }
  160.  
  161.                 try {
  162.                     playerRef.current?.hotkeys({
  163.                         volumeStep: 0.1,
  164.                         seekStep: seekSeconds ?? 5,
  165.                         enableModifiersForNumbers: false,
  166.                         enableHoverScroll: true,
  167.                     });
  168.                 } catch (e) {
  169.                     Sentry.captureException(e);
  170.                 }
  171.             }
  172.         } else {
  173.             // update already initialized player
  174.  
  175.             if (options.autoplay) {
  176.                 playerRef.current.autoplay(options.autoplay);
  177.             }
  178.  
  179.             if (options.sources) {
  180.                 if (options.sources.length === 0) {
  181.                     playerRef.current.pause();
  182.                 } else {
  183.                     playerRef.current.src(options.sources);
  184.                 }
  185.             }
  186.  
  187.             if (options.poster) {
  188.                 playerRef.current.poster(options.poster);
  189.             }
  190.         }
  191.     }, [onReady, options, seekSeconds, thumbnailTemplate]);
  192.  
  193.     // Dispose the Video.js player when the functional component unmounts
  194.     React.useEffect(() => {
  195.         return () => {
  196.             if (playerRef.current) {
  197.                 playerRef.current.dispose();
  198.                 playerRef.current = null;
  199.             }
  200.         };
  201.     }, []);
  202.  
  203.     const containerClassName = cx({
  204.         container: true,
  205.         isLoading: isLoading,
  206.     });
  207.  
  208.     const videoClassName = classnames({
  209.         [styles.videoPlayer]: true,
  210.         "video-js": true,
  211.         "vjs-big-play-centered": true,
  212.     });
  213.  
  214.     return (
  215.         <div className={ containerClassName }>
  216.             <div className={ styles.loaderWrapper }>
  217.                 <Loader isLoading={ isLoading } />
  218.             </div>
  219.  
  220.             <div data-vjs-player="">
  221.                 { /* eslint-disable-next-line jsx-a11y/media-has-caption */ }
  222.                 <video
  223.                     ref={ videoRef }
  224.                     className={ videoClassName }
  225.                     { ...rest }
  226.                 />
  227.             </div>
  228.         </div>
  229.     );
  230. }
  231.  
  232. export default React.memo(VideoPlayer, (prevProps, nextProps): boolean => {
  233.     const { options: prevPropsOptions, seekSeconds: prevPropsSeekSeconds, ...prevPropsRest } = prevProps;
  234.     const { options: nextPropsOptions, seekSeconds: nextPropsSeekSeconds, ...nextPropsRest } = nextProps;
  235.  
  236.     return isEqual(prevPropsOptions, nextPropsOptions)
  237.         && prevPropsSeekSeconds === nextPropsSeekSeconds
  238.         && isEqual(prevPropsRest, nextPropsRest);
  239. });
  240.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement