diff options
Diffstat (limited to 'apps/Player')
-rw-r--r-- | apps/Player/components/App.js | 107 | ||||
-rw-r--r-- | apps/Player/components/Buttons.js | 34 | ||||
-rw-r--r-- | apps/Player/components/Video.js | 32 | ||||
-rw-r--r-- | apps/Player/index.js | 3 | ||||
-rw-r--r-- | apps/Player/styles/Player.module.scss | 1 | ||||
-rw-r--r-- | apps/Player/styles/_player.scss | 70 |
6 files changed, 247 insertions, 0 deletions
diff --git a/apps/Player/components/App.js b/apps/Player/components/App.js new file mode 100644 index 0000000..5879111 --- /dev/null +++ b/apps/Player/components/App.js @@ -0,0 +1,107 @@ +import styles from '../styles/Player.module.scss' +import { useState, useEffect } from 'react' +import useSettings from 'hooks/useSettings' +import Video from './Video' +import Buttons from './Buttons' +import { Splash } from 'components' + +const App = ({ list }) => { + const { t } = useSettings() + const [current, setCurrent] = useState(null) + const [playlist, setPlaylist] = useState(null) + const [showPlaylist, setShowPlaylist] = useState(false) + + useEffect(() => { + if (list) { + const { items, enqueue } = list + + setPlaylist( + p => enqueue && p + ? p.some(x => items.some(y => x.id === y.id)) + ? p + : [...p, ...items] + : items + ) + + if (enqueue) { + setShowPlaylist(true) + } + } else { + if (typeof window !== 'undefined') { + setPlaylist(JSON.parse(window.localStorage.getItem('playlist'))) + } + } + }, [list]) + + useEffect(() => { + if (playlist) { + const { items, enqueue } = playlist + + if (typeof window !== 'undefined' && playlist && playlist.length > 0) { + window.localStorage.setItem('playlist', JSON.stringify(enqueue ? [...playlist, ...items] : items)) + setShowPlaylist(true) + } + + if (current === null && list) { + setCurrent( + enqueue + ? playlist && playlist.length > 1 + ? playlist.length - 1 + : 0 + : 0 + ) + } + } + }, [playlist]) + + if (!playlist) return <Splash /> + + return ( + <> + <div className='window__submenu'> + <div> + <div + onClick={() => { setShowPlaylist(p => !p) }} + className={current ? 'active' : null} + > + {t('player_playlist_default')} + </div> + <div onClick={() => {}}>+</div> + <Buttons current={current} setCurrent={setCurrent} playlist={playlist} /> + </div> + </div> + <div className={styles.player}> + <div> + {current !== null && ( + <Video playlist={playlist} current={current} setCurrent={setCurrent} /> + )} + </div> + <div style={showPlaylist ? {} : { transform: 'translateX(-110%)' }}> + <ul> + { + playlist && playlist.length > 0 + ? ( + playlist.map((item, i) => ( + <li + onClick={() => { setCurrent(i) }} + className={current === i ? styles.activeItem : ''} + key={item.id} + > + <span>{(i + 1) + '.'}</span> + {item.title} + </li> + )) + ) + : ( + <li>{t('player_playlist_empty')}</li> + ) + } + </ul> + <div onClick={() => setShowPlaylist(false)}><</div> + </div> + </div> + </> + ) +} + +export default App diff --git a/apps/Player/components/Buttons.js b/apps/Player/components/Buttons.js new file mode 100644 index 0000000..14452a0 --- /dev/null +++ b/apps/Player/components/Buttons.js @@ -0,0 +1,34 @@ +import { faStepForward, faStepBackward, faPlay, faStop } from '@fortawesome/free-solid-svg-icons' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' + +const Buttons = ({ current, setCurrent, playlist }) => ( + <> + <span /> + <div + className={current !== null && current > 0 ? '' : 'iconOff'} + onClick={() => { current !== null && current > 0 && setCurrent(c => c - 1) }} + > + <FontAwesomeIcon icon={faStepBackward} /> + </div> + <div + className={current === null ? 'iconOff' : ''} + onClick={() => { current !== null && setCurrent(null) }} + > + <FontAwesomeIcon icon={faStop} /> + </div> + <div + className={current === null ? '' : 'iconOff'} + onClick={() => { current === null && setCurrent(0) }} + > + <FontAwesomeIcon icon={faPlay} /> + </div> + <div + className={current !== null && current < playlist.length - 1 ? '' : 'iconOff'} + onClick={() => { current !== null && current < playlist.length - 1 && setCurrent(c => c + 1) }} + > + <FontAwesomeIcon icon={faStepForward} /> + </div> + </> +) + +export default Buttons diff --git a/apps/Player/components/Video.js b/apps/Player/components/Video.js new file mode 100644 index 0000000..7152967 --- /dev/null +++ b/apps/Player/components/Video.js @@ -0,0 +1,32 @@ +import { useRef } from 'react' +import Splash from 'components/Splash' + +const Video = ({ playlist, current, setCurrent }) => { + if (!playlist) return null + + const videoEl = useRef() + const sources = playlist[current]?.sources + + const handleEnd = () => { + setCurrent(current === playlist.length - 1 ? null : current + 1) + } + + return ( + sources + ? ( + <video onEnded={handleEnd} ref={videoEl} key={playlist[current]?.id} controls autoPlay> + { + sources.map(s => ( + <source src={s.url} type={s.mimeType} key={s.url} /> + )) + } + Your browser does not support the video tag. + </video> + ) + : ( + <Splash /> + ) + ) +} + +export default Video diff --git a/apps/Player/index.js b/apps/Player/index.js new file mode 100644 index 0000000..7971cc2 --- /dev/null +++ b/apps/Player/index.js @@ -0,0 +1,3 @@ +import Player from './components/App' + +export default Player diff --git a/apps/Player/styles/Player.module.scss b/apps/Player/styles/Player.module.scss new file mode 100644 index 0000000..b882295 --- /dev/null +++ b/apps/Player/styles/Player.module.scss @@ -0,0 +1 @@ +@import "player"; diff --git a/apps/Player/styles/_player.scss b/apps/Player/styles/_player.scss new file mode 100644 index 0000000..8cf37cd --- /dev/null +++ b/apps/Player/styles/_player.scss @@ -0,0 +1,70 @@ +.player { + height: 100%; + width: 100%; + position: relative; + background-color: #000; + + & > div:nth-of-type(1) { + height: calc(100% - 2em); + width: 100%; + padding-bottom: .5em; + + video { + height: 100%; + width: 100%; + object-fit: contain; + } + } + + & > div:nth-of-type(2) { + width: auto; + min-width: 25em; + position: absolute; + top: 0; + left: 0; + bottom: 2em; + background-color: var(--color-glass); + transition: .3s transform; + border-top-right-radius: 1em; + border-bottom-right-radius: 1em; + border-right: 1px solid var(--color-window-border-bottom); + + li { + padding: .5em 2em .5em .5em; + white-space: nowrap; + + span { + padding-right: 1em; + } + + @media(hover: hover) { + &:hover { + background-color: var(--color-glass-alt); + } + } + } + + & > div { + position: absolute; + top: 50%; + right: -0.5em; + padding: 2em .5em; + border-radius: .5em; + transform: translateY(-50%); + background-color: var(--color-window-menu-alt); + color: var(--color-text-alt); + border: 1px solid var(--color-window-border-bottom); + transition: .3s background; + + &:hover { + background-color: var(--color-window-menu); + } + } + } + +} + +.activeItem { + color: var(--color-text-alt); + font-weight: 600; +} |