import { useState, useEffect, useCallback, } from 'react' import Hls, { Level } from 'hls.js' import map from 'lodash/map' import find from 'lodash/find' import uniqBy from 'lodash/uniqBy' import isEmpty from 'lodash/isEmpty' import orderBy from 'lodash/orderBy' import isString from 'lodash/isString' import filter from 'lodash/fp/filter' import { useLocalStore } from 'hooks' import { isMobileDevice } from 'config/userAgent' const autoQuality = { label: 'Auto', level: -1, } /** * Убирает из списка качества без height * * Когда в манифесте нет данных о качествах стрима hls.levels содержит * непонятное качество без свойств height, width и тд для определения * какое это качество */ const filterOutUnknownQualities = filter(({ height }: Level) => Boolean(height)) const getVideoQualities = (levels: Array) => { if (isEmpty(levels)) return [] const filteredQualities = filterOutUnknownQualities(levels) const qualities = map(filteredQualities, (level, i) => ({ label: String(level.height), level: i, })) const sorted = orderBy( qualities, Number, 'desc', ) return uniqBy([...sorted, autoQuality], 'label') } export const useVideoQuality = (hls: Hls | null) => { const [videoQualities, setVideoQualities] = useState([autoQuality]) const [selectedQuality, setSelectedQuality] = useLocalStore({ defaultValue: autoQuality.label, key: 'player_quality', validator: isString, }) const onQualitySelect = useCallback((label: string) => { if (!hls) return const quality = find(videoQualities, { label }) if (!quality || quality.level === hls.currentLevel) return // eslint-disable-next-line no-param-reassign hls.currentLevel = quality.level setSelectedQuality(quality.label) }, [ setSelectedQuality, videoQualities, hls, ]) useEffect(() => { if (!hls) return undefined const listener = () => { const qualities = getVideoQualities(hls.levels) const quality = find(qualities, { label: selectedQuality }) || autoQuality // eslint-disable-next-line no-param-reassign hls.currentLevel = quality.level setSelectedQuality(quality.label) setVideoQualities(qualities) if (isMobileDevice && quality.label === 'Auto') { const mob720 = qualities.find((item) => item.label === '720') // eslint-disable-next-line no-param-reassign hls.autoLevelCapping = Number(mob720?.level) } } hls.on(Hls.Events.MANIFEST_PARSED, listener) return () => { hls.off(Hls.Events.MANIFEST_PARSED, listener) } }, [ selectedQuality, setSelectedQuality, hls, ]) return { onQualitySelect, selectedQuality, videoQualities: map(videoQualities, 'label'), } }