diff --git a/.changeset/strange-coats-exercise.md b/.changeset/strange-coats-exercise.md new file mode 100644 index 0000000000..091e6d53a1 --- /dev/null +++ b/.changeset/strange-coats-exercise.md @@ -0,0 +1,6 @@ +--- +'@alfalab/core-components-gallery': patch +--- + +- Импорт HLS заменён на light версию, только энкодер без дополнительных обвязок. +- Подгрузка модуля перенесена на время после загрузки основного js, для лучшей клиентской доступности основного приложения. diff --git a/packages/gallery/src/components/image-viewer/video/index.tsx b/packages/gallery/src/components/image-viewer/video/index.tsx index 4b806fb9c3..43b88ee4b1 100644 --- a/packages/gallery/src/components/image-viewer/video/index.tsx +++ b/packages/gallery/src/components/image-viewer/video/index.tsx @@ -5,9 +5,10 @@ import React, { useContext, useEffect, useRef, + useState, } from 'react'; import cn from 'classnames'; -import Hls from 'hls.js'; +import { type default as HlsType, type ErrorData, type Events } from 'hls.js/dist/hls.light.mjs'; import { Circle } from '@alfalab/core-components-icon-view/circle'; import PlayCompactMIcon from '@alfalab/icons-glyph/PlayCompactMIcon'; @@ -28,6 +29,7 @@ type Props = { export const Video = ({ url, index, className, isActive }: Props) => { const playerRef = useRef(null); const timer = useRef>(); + const [HLSSupported, setHLSSupported] = useState(true); const { setImageMeta, @@ -50,10 +52,22 @@ export const Video = ({ url, index, className, isActive }: Props) => { }, [index]); useEffect(() => { - const hls = new Hls(); + let hls: HlsType; - if (Hls.isSupported()) { - hls.on(Hls.Events.ERROR, (_, data) => { + async function initHls() { + const { default: Hls } = await import( + /* webpackChunkName: "hls-js-video" */ 'hls.js/dist/hls.light.mjs' + ); + + hls = new Hls(); + + if (!Hls.isSupported()) { + setHLSSupported(false); + + return; + } + + hls.on(Hls.Events.ERROR, (_: Events.ERROR, data: ErrorData) => { if (data.fatal) { switch (data.type) { case Hls.ErrorTypes.MEDIA_ERROR: @@ -70,12 +84,15 @@ export const Video = ({ url, index, className, isActive }: Props) => { }); hls.loadSource(url); + if (playerRef.current) { hls.attachMedia(playerRef.current); hls.subtitleDisplay = false; } } + initHls().catch(); + return () => { if (hls) { hls.destroy(); @@ -84,6 +101,7 @@ export const Video = ({ url, index, className, isActive }: Props) => { clearTimeout(timer.current); } }; + /* eslint-disable-next-line react-hooks/exhaustive-deps */ }, [url, index]); @@ -183,7 +201,7 @@ export const Video = ({ url, index, className, isActive }: Props) => { playsInline={true} muted={mutedVideo} loop={true} - src={Hls.isSupported() ? undefined : url} + src={HLSSupported ? undefined : url} className={cn(styles.video, { [styles.mobile]: view === 'mobile' }, className)} > diff --git a/packages/gallery/src/hls.d.ts b/packages/gallery/src/hls.d.ts new file mode 100644 index 0000000000..f18c299be9 --- /dev/null +++ b/packages/gallery/src/hls.d.ts @@ -0,0 +1,6 @@ +declare module 'hls.js/dist/hls.light.mjs' { + import Hls, { ErrorData, Events } from 'hls'; + + export default Hls; + export { ErrorData, Events }; +}