diff options
author | 2023-04-10 21:57:33 +0200 | |
---|---|---|
committer | 2023-04-10 21:57:33 +0200 | |
commit | f8463676a40656893c2048655e8807099e3adb39 (patch) | |
tree | 9b38927a293e38ec38da2707434786d92327edd0 /apps | |
parent | f4e00553c4acf44de48af958afc8747128b55562 (diff) | |
download | my_apps-f8463676a40656893c2048655e8807099e3adb39.tar.gz my_apps-f8463676a40656893c2048655e8807099e3adb39.tar.bz2 my_apps-f8463676a40656893c2048655e8807099e3adb39.zip |
add radio app
Diffstat (limited to 'apps')
-rw-r--r-- | apps/Player/components/App.js | 15 | ||||
-rw-r--r-- | apps/Player/components/Video.js | 38 | ||||
-rw-r--r-- | apps/Radio/components/App.js | 23 | ||||
-rw-r--r-- | apps/Radio/components/List.js | 48 | ||||
-rw-r--r-- | apps/Radio/components/Search.js | 31 | ||||
-rw-r--r-- | apps/Radio/components/Submenu.js | 21 | ||||
-rw-r--r-- | apps/Radio/index.js | 3 | ||||
-rw-r--r-- | apps/Radio/styles/Radio.module.scss | 3 | ||||
-rw-r--r-- | apps/Radio/styles/_list.scss | 51 | ||||
-rw-r--r-- | apps/Radio/styles/_radio.scss | 9 | ||||
-rw-r--r-- | apps/Radio/styles/_search.scss | 23 | ||||
-rw-r--r-- | apps/Youtube/styles/_results.scss | 17 | ||||
-rw-r--r-- | apps/index.js | 1 |
13 files changed, 266 insertions, 17 deletions
diff --git a/apps/Player/components/App.js b/apps/Player/components/App.js index 2ace63c..552ae52 100644 --- a/apps/Player/components/App.js +++ b/apps/Player/components/App.js @@ -7,7 +7,7 @@ import Video from './Video' import Buttons from './Buttons' import { Splash } from 'components' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faList, faTrashAlt, faCaretSquareRight, faInfinity, faAlignJustify, faVideo, faMusic } from '@fortawesome/free-solid-svg-icons' +import { faList, faTrashAlt, faCaretSquareRight, faInfinity, faAlignJustify, faVideo, faMusic, faPodcast } from '@fortawesome/free-solid-svg-icons' const App = ({ list }) => { const { t } = useSettings() @@ -78,6 +78,15 @@ const App = ({ list }) => { smallDevice && details && details.show && setShowPlaylist(false) }, [details && details.show]) + const iconType = type => { + switch (type) { + case 'yt_video': return faCaretSquareRight + case 'yt_live': return faInfinity + case 'radio': return faPodcast + default: return faCaretSquareRight + } + } + const remove = (e, i) => { e.stopPropagation() if (current === i) { @@ -144,9 +153,7 @@ const App = ({ list }) => { key={item.id} > <FontAwesomeIcon - icon={item.type.split('_')[1] === 'live' - ? faInfinity - : faCaretSquareRight} + icon={iconType(item.type)} /> <span>{(i + 1) + '.'}</span> <span>{item.title}</span> diff --git a/apps/Player/components/Video.js b/apps/Player/components/Video.js index de00761..2167ee0 100644 --- a/apps/Player/components/Video.js +++ b/apps/Player/components/Video.js @@ -1,9 +1,13 @@ import { useState, useEffect, useRef } from 'react' import Image from 'next/image' import Splash from 'components/Splash' +import usePopup from 'hooks/usePopup' +import useSettings from 'hooks/useSettings' import fetchJson from 'helpers/fetchJson' const Video = ({ playlist, current, setCurrent, audioOnly = false, setDetails }) => { + const { t } = useSettings() + const { setPopup } = usePopup() const [data, setData] = useState(null) const [loading, setLoading] = useState(null) const videoEl = useRef() @@ -40,9 +44,37 @@ const Video = ({ playlist, current, setCurrent, audioOnly = false, setDetails }) description: v.videoDetails.description })) }) - .catch(() => console.log('error fetching video')) + .catch(() => { + setData({ + // thumbnail: v.videoDetails.thumbnails[0].url, + formats: [] + }) + setDetails(d => ({ + ...d, + title: 'Error', + description: 'Item did not load correctly' + })) + setPopup({ + content: t('player_youtube_fetching_error'), + time: 2000, + error: true + }) + }) .finally(() => setLoading(false)) - break + break + case 'radio': + const { url, mimeType, thumbnail, title } = playlist[current]; + setData({ + thumbnail, + formats: [{ url, mimeType }] + }) + setDetails(d => ({ + ...d, + title: 'Gra radio ' + title, + description: '' + })) + setLoading(false) + break default: } }, [playlist && playlist[current].id, audioOnly]) @@ -67,7 +99,7 @@ const Video = ({ playlist, current, setCurrent, audioOnly = false, setDetails }) } Your browser does not support the video tag. </video> - {audioOnly && ( + {(audioOnly || playlist[current].type === 'radio') && ( <> <div /> <span style={{ backgroundImage: `url(${data.thumbnail})` }} /> diff --git a/apps/Radio/components/App.js b/apps/Radio/components/App.js new file mode 100644 index 0000000..bd306b4 --- /dev/null +++ b/apps/Radio/components/App.js @@ -0,0 +1,23 @@ +import { useState } from 'react' +import styles from '../styles/Radio.module.scss' + +import Submenu from './Submenu' +import Search from './Search' +import List from './List' + +const App = () => { + const [enqueue, setEnqueue] = useState(false) + const [results, setResults] = useState() + + return ( + <> + <Submenu enqueue={enqueue} setEnqueue={setEnqueue} /> + <div className={styles.radio}> + <Search setResults={setResults} /> + <List results={results} enqueue={enqueue} /> + </div> + </> + ) +} + +export default App diff --git a/apps/Radio/components/List.js b/apps/Radio/components/List.js new file mode 100644 index 0000000..73e1523 --- /dev/null +++ b/apps/Radio/components/List.js @@ -0,0 +1,48 @@ +import { open } from 'helpers/windowActions' +import appList from 'configs/appList' +import useApps from 'hooks/useApps' +import fetchJson from 'helpers/fetchJson' +import styles from '../styles/Radio.module.scss' + +const play = async (title, station, apps, setApps, enqueue) => { + const streamData = await fetchJson('/api/radio/stream', { + method: 'POST', + body: JSON.stringify({ station }) + }) + + const items = [{ type: 'radio', title, ...streamData }] + + apps && apps.length > 0 && apps.some(a => a && a.name === 'Player') + ? setApps(prev => prev.map(a => a.name === 'Player' ? { ...a, props: { list: { items, enqueue } } } : a)) + : open({ appName: 'Player', ...appList.Player }, setApps, { list: { items, enqueue } }) +} + +const List = ({ results, enqueue }) => { + const { apps, setApps } = useApps() + + return ( + <div className={styles.list}> + <div> + {results && results.length === 0 && <div>No results</div>} + {results && results.length > 0 && ( + results.map(({ title, url, logo, genres, locations }) => ( + <div key={url} onClick={() => play(title, url, apps, setApps, enqueue)}> + <img width={75} height={75} src={logo} loading="lazy" alt="title" /> + <div> + <span>{ title }</span> + <div> + <span>{locations.join(', ')}</span> + <div> + {genres.map(g => <span>{g}</span>)} + </div> + </div> + </div> + </div> + )) + )} + </div> + </div> + ) +} + +export default List diff --git a/apps/Radio/components/Search.js b/apps/Radio/components/Search.js new file mode 100644 index 0000000..363a37c --- /dev/null +++ b/apps/Radio/components/Search.js @@ -0,0 +1,31 @@ +import { useState } from 'react' +import styles from '../styles/Radio.module.scss' +import fetchJson from 'helpers/fetchJson' + +const searchQuery = async (query, setResults) => { + const results = await fetchJson('/api/radio/stations', { + method: 'POST', + body: JSON.stringify({ query }) + }) + + setResults(results) +} + +const Search = ({ setResults }) => { + const [query, setQuery] = useState('') + const changeQuery = e => setQuery(e.target.value) + + return ( + <div className={styles.search}> + <input type="text" onChange={changeQuery} value={query} /> + <span + className="window__button" + onClick={() => searchQuery(query, setResults)} + > + Search + </span> + </div> + ) +} + +export default Search diff --git a/apps/Radio/components/Submenu.js b/apps/Radio/components/Submenu.js new file mode 100644 index 0000000..6b8724b --- /dev/null +++ b/apps/Radio/components/Submenu.js @@ -0,0 +1,21 @@ +import useSettings from 'hooks/useSettings' + +const Submenu = ({ enqueue, setEnqueue }) => { + const { t } = useSettings() + + return ( + <div className='window__submenu'> + <div> + <span /> + <div + className={enqueue ? '' : 'off'} + onClick={() => { setEnqueue(e => !e) }} + > + {t('yt_enqueue')} + </div> + </div> + </div> + ) +} + +export default Submenu diff --git a/apps/Radio/index.js b/apps/Radio/index.js new file mode 100644 index 0000000..521d7e2 --- /dev/null +++ b/apps/Radio/index.js @@ -0,0 +1,3 @@ +import Radio from './components/App' + +export default Radio diff --git a/apps/Radio/styles/Radio.module.scss b/apps/Radio/styles/Radio.module.scss new file mode 100644 index 0000000..ac6dc35 --- /dev/null +++ b/apps/Radio/styles/Radio.module.scss @@ -0,0 +1,3 @@ +@import "radio"; +@import "list"; +@import "search"; diff --git a/apps/Radio/styles/_list.scss b/apps/Radio/styles/_list.scss new file mode 100644 index 0000000..08e37cc --- /dev/null +++ b/apps/Radio/styles/_list.scss @@ -0,0 +1,51 @@ +.list { + padding: 10px; + height: 100%; + overflow-y: auto; + + & > div { + & > div { + padding: 8px 0; + display: flex; + justify-content: left; + align-items: center; + transition: .3s background-color; + + @media(hover: hover) { + &:hover { + background-color: var(--color-button-alt); + } + } + + & > img { + margin-right: 10px; + height: 75px; + width: 75px; + } + + & > div { + padding-top: .5rem; + & > span { + display: inline-block; + font-weight: 600; + color: var(--color-text-alt); + } + + & > div { + & > span { + color: var(--color-text); + } + & > div { + & > span { + display: inline-block; + background-color: var(--color-button); + padding: 0.25rem; + margin: 0.5rem .5rem 0.5rem 0;; + border-radius: .25rem; + } + } + } + } + } + } +} diff --git a/apps/Radio/styles/_radio.scss b/apps/Radio/styles/_radio.scss new file mode 100644 index 0000000..c62ec36 --- /dev/null +++ b/apps/Radio/styles/_radio.scss @@ -0,0 +1,9 @@ +.radio { + width: 100%; + height: 100%; + position: relative; + background-color: #000; + display: flex; + flex-direction: column; + background-color: var(--color-window-content); +} diff --git a/apps/Radio/styles/_search.scss b/apps/Radio/styles/_search.scss new file mode 100644 index 0000000..0194618 --- /dev/null +++ b/apps/Radio/styles/_search.scss @@ -0,0 +1,23 @@ +.search { + display: flex; + justify-content: left; + align-items: center; + padding: .5rem .5rem 15px 1rem; + background: var(--color-window-menu); + + input { + background-color: var(--color-window-content); + color: var(--color-text-alt); + border: none; + border-radius: .5rem; + padding: 0.5rem; + font-size: 1rem; + border: 1px dashed var(--color-window-buttons); + width: 100%; + } + + span { + margin-top: 0; + margin-right: .5em; + } +} diff --git a/apps/Youtube/styles/_results.scss b/apps/Youtube/styles/_results.scss index 48437a2..e112e10 100644 --- a/apps/Youtube/styles/_results.scss +++ b/apps/Youtube/styles/_results.scss @@ -7,25 +7,22 @@ } form { - padding: .5em; - justify-content: center; - align-items: center; display: flex; - height: 4em; + justify-content: left; + align-items: center; + padding: .5em 0 .75rem .5rem; + background: var(--color-window-menu); input[type=text] { background-color: var(--color-window-content); color: var(--color-text-alt); - margin: .5em .5em 0; - height: 2.5rem; border: none; - border-radius: .5em; + border-radius: .5rem; padding: 0.5rem; font-size: 1rem; border: 1px dashed var(--color-window-buttons); - flex-shrink: 1; - flex-grow: 1; - width: 1em; + width: 100%; + margin-left: .5rem; &:placeholder { font: inherit; diff --git a/apps/index.js b/apps/index.js index 05ad5e4..064980c 100644 --- a/apps/index.js +++ b/apps/index.js @@ -3,4 +3,5 @@ export { default as Notes } from './Notes' export { default as Player } from './Player' export { default as Settings } from './Settings' export { default as ChangePassword } from './ChangePassword' +export { default as Radio } from './Radio' export { default as Youtube } from './Youtube' |