diff --git a/web/components/video/OwncastPlayer.tsx b/web/components/video/OwncastPlayer.tsx index 85f0ed380..357eb306e 100644 --- a/web/components/video/OwncastPlayer.tsx +++ b/web/components/video/OwncastPlayer.tsx @@ -7,17 +7,30 @@ import VideoPoster from './VideoPoster'; import { getLocalStorage, setLocalStorage } from '../../utils/localStorage'; import { isVideoPlayingAtom, clockSkewAtom } from '../stores/ClientConfigStore'; import PlaybackMetrics from './metrics/playback'; +import createVideoSettingsMenuButton from './settings-menu'; +const VIDEO_CONFIG_URL = '/api/video/variants'; const PLAYER_VOLUME = 'owncast_volume'; const ping = new ViewerPing(); let playbackMetrics = null; - interface Props { source: string; online: boolean; } +async function getVideoSettings() { + let qualities = []; + + try { + const response = await fetch(VIDEO_CONFIG_URL); + qualities = await response.json(); + } catch (e) { + console.error(e); + } + return qualities; +} + export default function OwncastPlayer(props: Props) { const playerRef = React.useRef(null); const { source, online } = props; @@ -118,7 +131,6 @@ export default function OwncastPlayer(props: Props) { const handlePlayerReady = (player, videojs) => { playerRef.current = player; - setSavedVolume(); // You can handle player events here, for example: @@ -149,10 +161,26 @@ export default function OwncastPlayer(props: Props) { setVideoPlaying(false); }); + videojs.hookOnce(); + player.on('volumechange', handleVolume); playbackMetrics = new PlaybackMetrics(player, videojs); playbackMetrics.setClockSkew(clockSkew); + + const createSettings = async () => { + const videoQualities = await getVideoSettings(); + const menuButton = createVideoSettingsMenuButton(player, videojs, videoQualities); + player.controlBar.addChild( + menuButton, + {}, + // eslint-disable-next-line no-underscore-dangle + player.controlBar.children_.length - 2, + ); + // this.latencyCompensatorToggleButton = lowLatencyItem; + }; + + createSettings(); }; useEffect(() => { diff --git a/web/components/video/player.scss b/web/components/video/player.scss index ad3e93eb8..2796ea4c9 100644 --- a/web/components/video/player.scss +++ b/web/components/video/player.scss @@ -12,3 +12,16 @@ .vjs-owncast .vjs-control-bar { background-color: var(--theme-background) !important; } + +// .vjs-airplay .vjs-icon-placeholder::before { +// content: url("../img/airplay.png"); +// } + +.vjs-quality-selector .vjs-icon-placeholder { + font-family: VideoJS; + font-weight: 400; + font-style: normal; +} +.vjs-quality-selector .vjs-icon-placeholder::before { + content: "\f110"; +} \ No newline at end of file diff --git a/web/components/video/settings-menu.ts b/web/components/video/settings-menu.ts new file mode 100644 index 000000000..90ec80b9f --- /dev/null +++ b/web/components/video/settings-menu.ts @@ -0,0 +1,108 @@ +export default function createVideoSettingsMenuButton(player, videojs, qualities): any { + // const VjsMenuItem = videojs.getComponent('MenuItem'); + const MenuItem = videojs.getComponent('MenuItem'); + const MenuButtonClass = videojs.getComponent('MenuButton'); + + // class MenuSeparator extends VjsMenuItem { + // // eslint-disable-next-line no-useless-constructor + // constructor(p: any, options: { selectable: boolean }) { + // super(p, options); + // } + + // createEl(tag = 'button', props = {}, attributes = {}) { + // const el = super.createEl(tag, props, attributes); + // el.innerHTML = '