aboutsummaryrefslogtreecommitdiffstats
path: root/apps
diff options
context:
space:
mode:
authorGravatar piotrruss <mail@pruss.it> 2023-04-10 21:57:33 +0200
committerGravatar piotrruss <mail@pruss.it> 2023-04-10 21:57:33 +0200
commitf8463676a40656893c2048655e8807099e3adb39 (patch)
tree9b38927a293e38ec38da2707434786d92327edd0 /apps
parentf4e00553c4acf44de48af958afc8747128b55562 (diff)
downloadmy_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.js15
-rw-r--r--apps/Player/components/Video.js38
-rw-r--r--apps/Radio/components/App.js23
-rw-r--r--apps/Radio/components/List.js48
-rw-r--r--apps/Radio/components/Search.js31
-rw-r--r--apps/Radio/components/Submenu.js21
-rw-r--r--apps/Radio/index.js3
-rw-r--r--apps/Radio/styles/Radio.module.scss3
-rw-r--r--apps/Radio/styles/_list.scss51
-rw-r--r--apps/Radio/styles/_radio.scss9
-rw-r--r--apps/Radio/styles/_search.scss23
-rw-r--r--apps/Youtube/styles/_results.scss17
-rw-r--r--apps/index.js1
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'