add player component
This commit is contained in:
parent
0c077f3f12
commit
355073fc91
2
Dockerfile
Normal file
2
Dockerfile
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
ARG NEXTCLOUD_VERSION=20.0.0
|
||||||
|
FROM rootlogin/nextcloud
|
BIN
img/play.png
Normal file
BIN
img/play.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
58
package-lock.json
generated
58
package-lock.json
generated
@ -5842,6 +5842,64 @@
|
|||||||
"flat-cache": "^2.0.1"
|
"flat-cache": "^2.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"file-loader": {
|
||||||
|
"version": "6.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.1.1.tgz",
|
||||||
|
"integrity": "sha512-Klt8C4BjWSXYQAfhpYYkG4qHNTna4toMHEbWrI5IuVoxbU6uiDKeKAP99R8mmbJi3lvewn/jQBOgU4+NS3tDQw==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"loader-utils": "^2.0.0",
|
||||||
|
"schema-utils": "^3.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@types/json-schema": {
|
||||||
|
"version": "7.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz",
|
||||||
|
"integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"ajv": {
|
||||||
|
"version": "6.12.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||||
|
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"fast-deep-equal": "^3.1.1",
|
||||||
|
"fast-json-stable-stringify": "^2.0.0",
|
||||||
|
"json-schema-traverse": "^0.4.1",
|
||||||
|
"uri-js": "^4.2.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ajv-keywords": {
|
||||||
|
"version": "3.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
|
||||||
|
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"loader-utils": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"big.js": "^5.2.2",
|
||||||
|
"emojis-list": "^3.0.0",
|
||||||
|
"json5": "^2.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"schema-utils": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/json-schema": "^7.0.6",
|
||||||
|
"ajv": "^6.12.5",
|
||||||
|
"ajv-keywords": "^3.5.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"file-uri-to-path": {
|
"file-uri-to-path": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
|
||||||
|
@ -66,6 +66,7 @@
|
|||||||
"eslint-plugin-promise": "^4.2.1",
|
"eslint-plugin-promise": "^4.2.1",
|
||||||
"eslint-plugin-standard": "^4.0.1",
|
"eslint-plugin-standard": "^4.0.1",
|
||||||
"eslint-plugin-vue": "^6.2.2",
|
"eslint-plugin-vue": "^6.2.2",
|
||||||
|
"file-loader": "^6.1.1",
|
||||||
"node-sass": "^4.14.1",
|
"node-sass": "^4.14.1",
|
||||||
"sass-loader": "^8.0.2",
|
"sass-loader": "^8.0.2",
|
||||||
"stylelint": "^13.7.2",
|
"stylelint": "^13.7.2",
|
||||||
|
86
src/components/Main.vue
Normal file
86
src/components/Main.vue
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
<template>
|
||||||
|
<Content app-name="radio">
|
||||||
|
<Navigation />
|
||||||
|
<AppContent>
|
||||||
|
<Breadcrumbs class="breadcrumbs">
|
||||||
|
<Breadcrumb title="Home" href="/" />
|
||||||
|
<Breadcrumb title="Top" href="/Top" />
|
||||||
|
</Breadcrumbs>
|
||||||
|
<Table :station-data="tableData" />
|
||||||
|
</AppContent>
|
||||||
|
</Content>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Content from '@nextcloud/vue/dist/Components/Content'
|
||||||
|
import AppContent from '@nextcloud/vue/dist/Components/AppContent'
|
||||||
|
import Breadcrumbs from '@nextcloud/vue/dist/Components/Breadcrumbs'
|
||||||
|
import Breadcrumb from '@nextcloud/vue/dist/Components/Breadcrumb'
|
||||||
|
import Navigation from './Navigation'
|
||||||
|
import Table from './Table'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Main',
|
||||||
|
components: {
|
||||||
|
Navigation,
|
||||||
|
Content,
|
||||||
|
AppContent,
|
||||||
|
Breadcrumbs,
|
||||||
|
Breadcrumb,
|
||||||
|
Table,
|
||||||
|
},
|
||||||
|
data: () => ({
|
||||||
|
tableData: [],
|
||||||
|
offset: 0,
|
||||||
|
}),
|
||||||
|
mounted() {
|
||||||
|
this.loadStations()
|
||||||
|
this.scroll()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
loadStations() {
|
||||||
|
const vm = this
|
||||||
|
this.$jquery.ajax({
|
||||||
|
type: 'POST',
|
||||||
|
url: 'https://de1.api.radio-browser.info/json/stations/topclick',
|
||||||
|
data: '{ "limit": 20, "offset": 20 }',
|
||||||
|
contentType: 'application/json',
|
||||||
|
dataType: 'json',
|
||||||
|
})
|
||||||
|
.done(function(data) {
|
||||||
|
console.log('new data')
|
||||||
|
vm.tableData = vm.tableData.concat(data)
|
||||||
|
vm.offset += 20
|
||||||
|
})
|
||||||
|
},
|
||||||
|
scroll() {
|
||||||
|
window.onscroll = () => {
|
||||||
|
if ((window.innerHeight + window.scrollY) >= document.body.scrollHeight) {
|
||||||
|
this.loadStations()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
|
||||||
|
.app-navigation-toggle {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-content-vue {
|
||||||
|
padding-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumbs {
|
||||||
|
background-color: var(--color-main-background-translucent);
|
||||||
|
z-index: 60;
|
||||||
|
position: sticky;
|
||||||
|
position: -webkit-sticky;
|
||||||
|
top: 50px;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
41
src/components/Navigation.vue
Normal file
41
src/components/Navigation.vue
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<template>
|
||||||
|
<AppNavigation>
|
||||||
|
<template id="app-radio-navigation" #list>
|
||||||
|
<AppNavigationItem
|
||||||
|
:to="{ name: 'TOP' }"
|
||||||
|
icon="icon-category-dashboard"
|
||||||
|
title="Top" />
|
||||||
|
<AppNavigationItem
|
||||||
|
:to="{ name: 'RECENT' }"
|
||||||
|
icon="icon-category-monitoring"
|
||||||
|
title="Recent" />
|
||||||
|
<AppNavigationItem
|
||||||
|
:to="{ name: 'FAVORITES' }"
|
||||||
|
icon="icon-favorite"
|
||||||
|
title="Favorites" />
|
||||||
|
<AppNavigationItem
|
||||||
|
:to="{ name: 'CATEGORIES' }"
|
||||||
|
icon="icon-files-dark"
|
||||||
|
title="Categories" />
|
||||||
|
</template>
|
||||||
|
<template #footer>
|
||||||
|
<Player :pinned="true" />
|
||||||
|
</template>
|
||||||
|
</AppNavigation>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import AppNavigation from '@nextcloud/vue/dist/Components/AppNavigation'
|
||||||
|
import AppNavigationItem from '@nextcloud/vue/dist/Components/AppNavigationItem'
|
||||||
|
import Player from './Player'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Navigation',
|
||||||
|
components: {
|
||||||
|
AppNavigation,
|
||||||
|
AppNavigationItem,
|
||||||
|
Player,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
88
src/components/Player.vue
Normal file
88
src/components/Player.vue
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
<template>
|
||||||
|
<div id="app-settings">
|
||||||
|
<button id="playbutton" class="play" :style="playerIcon" />
|
||||||
|
<div id="volumeicon" class="full" />
|
||||||
|
<div id="volumeslider" />
|
||||||
|
<span id="station_metadata" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
playerIcon: {
|
||||||
|
backgroundImage: `url( ${require('../../img/play.png')} )`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
|
||||||
|
#playbutton{
|
||||||
|
height:50px;
|
||||||
|
width: 50px;
|
||||||
|
margin: 10px;
|
||||||
|
border: none;
|
||||||
|
background-size: 100%;
|
||||||
|
background-position: center;
|
||||||
|
float: left;
|
||||||
|
transition: background-image 0.4s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buffering {
|
||||||
|
animation:spin 4s linear infinite;
|
||||||
|
/* background: url('../img/wheel.png') no-repeat; */
|
||||||
|
transition: background-image 0.4s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin { 100% { transform:rotate(360deg); } }
|
||||||
|
|
||||||
|
#station_metadata{
|
||||||
|
margin: 2px 20px 5px 20px;
|
||||||
|
padding-left: 5px;
|
||||||
|
white-space:nowrap;
|
||||||
|
overflow:hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.play{
|
||||||
|
/* background: url('../img/play.png') no-repeat; */
|
||||||
|
}
|
||||||
|
|
||||||
|
.pause{
|
||||||
|
/* background: url('../img/pause.png') no-repeat; */
|
||||||
|
}
|
||||||
|
|
||||||
|
#volumeicon {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
background-size: contain;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
position: relative;
|
||||||
|
left: 75px;
|
||||||
|
top: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#volumeicon.full {
|
||||||
|
/* background-image: url('../img/sound_full.png'); */
|
||||||
|
}
|
||||||
|
|
||||||
|
#volumeicon.mid {
|
||||||
|
/* background-image: url('../img/sound_mid.png'); */
|
||||||
|
}
|
||||||
|
|
||||||
|
#volumeicon.silent {
|
||||||
|
/* background-image: url('../img/sound_silent.png'); */
|
||||||
|
}
|
||||||
|
|
||||||
|
#volumeslider{
|
||||||
|
width: 170px;
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
left: 40px;
|
||||||
|
top: -7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
136
src/components/Table.vue
Normal file
136
src/components/Table.vue
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
<template>
|
||||||
|
<table id="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th />
|
||||||
|
<th class="nameColumn">
|
||||||
|
Name
|
||||||
|
</th>
|
||||||
|
<th />
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody v-if="stationData">
|
||||||
|
<tr v-for="(station, idx) in stationData" :key="idx">
|
||||||
|
<td>
|
||||||
|
<div class="stationIcon"
|
||||||
|
:style="{ backgroundImage: `url('${ station.favicon }')` }" />
|
||||||
|
</td>
|
||||||
|
<td class="filenameColumn">
|
||||||
|
<span class="innernametext">
|
||||||
|
{{ station.name }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td class="actionColumn">
|
||||||
|
<Actions>
|
||||||
|
<ActionButton icon="icon-star" :close-after-click="true">
|
||||||
|
Add to favorites
|
||||||
|
</ActionButton>
|
||||||
|
<ActionButton icon="icon-info" :close-after-click="true">
|
||||||
|
Details
|
||||||
|
</ActionButton>
|
||||||
|
</Actions>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Actions from '@nextcloud/vue/dist/Components/Actions'
|
||||||
|
import ActionButton from '@nextcloud/vue/dist/Components/ActionButton'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Table',
|
||||||
|
components: {
|
||||||
|
Actions,
|
||||||
|
ActionButton,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
stationData: {
|
||||||
|
type: Array,
|
||||||
|
default() { return [] },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
min-width: 250px;
|
||||||
|
|
||||||
|
thead {
|
||||||
|
background-color: var(--color-main-background-translucent);
|
||||||
|
z-index: 60;
|
||||||
|
position: sticky;
|
||||||
|
position: -webkit-sticky;
|
||||||
|
top: 99px;
|
||||||
|
|
||||||
|
th {
|
||||||
|
border-bottom: 1px solid var(--color-border);
|
||||||
|
padding: 15px;
|
||||||
|
height: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
th, th a {
|
||||||
|
color: var(--color-text-maxcontrast);
|
||||||
|
}
|
||||||
|
|
||||||
|
th.nameColumn {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody {
|
||||||
|
|
||||||
|
td {
|
||||||
|
padding: 0 15px;
|
||||||
|
font-style: normal;
|
||||||
|
background-position: 8px center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
border-bottom: 1px solid var(--color-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
tr {
|
||||||
|
height: 51px;
|
||||||
|
background-color: var(--color-background-light);
|
||||||
|
|
||||||
|
tr:hover, tr:focus, tr.mouseOver td {
|
||||||
|
background-color: var(--color-background-hover);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tr td:first-child {
|
||||||
|
padding-left: 40px;
|
||||||
|
width: 32px;
|
||||||
|
padding-right: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
td.filenameColumn .innernametext {
|
||||||
|
color: var(--color-main-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stationIcon {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: contain;
|
||||||
|
background-position: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actionColumn {
|
||||||
|
width: auto;
|
||||||
|
padding-right: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filenameColumn {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
40
src/router.js
Normal file
40
src/router.js
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import Vue from 'vue'
|
||||||
|
import Router from 'vue-router'
|
||||||
|
import { generateUrl } from '@nextcloud/router'
|
||||||
|
|
||||||
|
import Main from './components/Main'
|
||||||
|
|
||||||
|
Vue.use(Router)
|
||||||
|
|
||||||
|
export default new Router({
|
||||||
|
mode: 'history',
|
||||||
|
base: generateUrl('/apps/radio'),
|
||||||
|
linkActiveClass: 'active',
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
component: Main,
|
||||||
|
name: 'TOP',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/top',
|
||||||
|
component: Main,
|
||||||
|
name: 'TOP',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/recent',
|
||||||
|
component: Main,
|
||||||
|
name: 'RECENT',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/favorites',
|
||||||
|
component: Main,
|
||||||
|
name: 'FAVORITES',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/categories',
|
||||||
|
component: Main,
|
||||||
|
name: 'CATEGORIES',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
18
webpack.js
18
webpack.js
@ -1,3 +1,19 @@
|
|||||||
|
const { merge } = require('webpack-merge')
|
||||||
const webpackConfig = require('@nextcloud/webpack-vue-config')
|
const webpackConfig = require('@nextcloud/webpack-vue-config')
|
||||||
|
|
||||||
module.exports = webpackConfig
|
const config = {
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.(png|jpg|gif)$/, loader: 'file-loader?name=./images/[name].[ext]',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const mergedConfigs = merge(config, webpackConfig)
|
||||||
|
|
||||||
|
// Remove duplicate rules by the `test` key
|
||||||
|
mergedConfigs.module.rules = mergedConfigs.module.rules.filter((v, i, a) => a.findIndex(t => (t.test.toString() === v.test.toString())) === i)
|
||||||
|
|
||||||
|
module.exports = mergedConfigs
|
||||||
|
Loading…
Reference in New Issue
Block a user