mirror of
https://github.com/owntone/owntone-server.git
synced 2024-12-28 08:05:56 -05:00
Merge pull request #1008 from chme/web_next
Player web interface v0.7.2
This commit is contained in:
commit
3fd812ef2f
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -2060,8 +2060,6 @@ queue_item_to_json(struct db_queue_item *queue_item, char shuffle)
|
|||||||
json_object *item;
|
json_object *item;
|
||||||
char uri[100];
|
char uri[100];
|
||||||
char artwork_url[100];
|
char artwork_url[100];
|
||||||
char chbuf[6];
|
|
||||||
const char *ch;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
item = json_object_new_object();
|
item = json_object_new_object();
|
||||||
@ -2141,15 +2139,7 @@ queue_item_to_json(struct db_queue_item *queue_item, char shuffle)
|
|||||||
safe_json_add_string(item, "type", queue_item->type);
|
safe_json_add_string(item, "type", queue_item->type);
|
||||||
json_object_object_add(item, "bitrate", json_object_new_int(queue_item->bitrate));
|
json_object_object_add(item, "bitrate", json_object_new_int(queue_item->bitrate));
|
||||||
json_object_object_add(item, "samplerate", json_object_new_int(queue_item->samplerate));
|
json_object_object_add(item, "samplerate", json_object_new_int(queue_item->samplerate));
|
||||||
switch (queue_item->channels)
|
json_object_object_add(item, "channels", json_object_new_int(queue_item->channels));
|
||||||
{
|
|
||||||
case 1: ch = "mono"; break;
|
|
||||||
case 2: ch = "stereo"; break;
|
|
||||||
default:
|
|
||||||
snprintf(chbuf, sizeof(chbuf), "%d ch", queue_item->channels);
|
|
||||||
ch = chbuf;
|
|
||||||
}
|
|
||||||
safe_json_add_string(item, "channels", ch);
|
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
7153
web-src/package-lock.json
generated
7153
web-src/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "forked-daapd-web",
|
"name": "forked-daapd-web",
|
||||||
"version": "0.7.1",
|
"version": "0.7.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "forked-daapd web interface",
|
"description": "forked-daapd web interface",
|
||||||
"author": "chme <christian.meffert@googlemail.com>",
|
"author": "chme <christian.meffert@googlemail.com>",
|
||||||
@ -12,36 +12,39 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^0.19.2",
|
"axios": "^0.19.2",
|
||||||
"bulma": "^0.8.2",
|
"bulma": "^0.9.0",
|
||||||
"core-js": "^3.6.5",
|
"core-js": "^3.6.5",
|
||||||
"mdi": "^2.2.43",
|
"mdi": "^2.2.43",
|
||||||
"moment": "^2.24.0",
|
"moment": "^2.27.0",
|
||||||
"moment-duration-format": "^2.3.2",
|
"moment-duration-format": "^2.3.2",
|
||||||
"npm": "^6.14.4",
|
"npm": "^6.14.5",
|
||||||
"reconnectingwebsocket": "^1.0.0",
|
"reconnectingwebsocket": "^1.0.0",
|
||||||
"spotify-web-api-js": "^1.2.0",
|
"spotify-web-api-js": "^1.4.0",
|
||||||
"string-to-color": "^2.1.3",
|
"string-to-color": "^2.1.4",
|
||||||
"v-click-outside": "^3.0.1",
|
"v-click-outside": "^3.0.1",
|
||||||
"vue": "^2.6.11",
|
"vue": "^2.6.11",
|
||||||
"vue-infinite-loading": "^2.4.5",
|
"vue-infinite-loading": "^2.4.5",
|
||||||
"vue-progressbar": "^0.7.5",
|
"vue-progressbar": "^0.7.5",
|
||||||
"vue-range-slider": "^0.6.0",
|
"vue-range-slider": "^0.6.0",
|
||||||
"vue-router": "^3.1.6",
|
"vue-router": "^3.3.4",
|
||||||
|
"vue-tiny-lazyload-img": "^0.1.0",
|
||||||
"vuedraggable": "^2.23.2",
|
"vuedraggable": "^2.23.2",
|
||||||
"vuex": "^3.1.3"
|
"vuex": "^3.5.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vue/cli-plugin-babel": "^4.3.1",
|
"@vue/cli-plugin-babel": "^4.4.6",
|
||||||
"@vue/cli-plugin-eslint": "^4.3.1",
|
"@vue/cli-plugin-eslint": "^4.4.6",
|
||||||
"@vue/cli-service": "^4.3.1",
|
"@vue/cli-service": "^4.4.6",
|
||||||
"@vue/eslint-config-standard": "^5.1.2",
|
"@vue/eslint-config-standard": "^5.1.2",
|
||||||
"babel-eslint": "^10.1.0",
|
"babel-eslint": "^10.1.0",
|
||||||
"eslint": "^6.8.0",
|
"eslint": "^7.3.1",
|
||||||
"eslint-plugin-import": "^2.20.2",
|
"eslint-plugin-import": "^2.22.0",
|
||||||
"eslint-plugin-node": "^11.1.0",
|
"eslint-plugin-node": "^11.1.0",
|
||||||
"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",
|
||||||
|
"sass": "^1.26.9",
|
||||||
|
"sass-loader": "^8.0.2",
|
||||||
"vue-template-compiler": "^2.6.11"
|
"vue-template-compiler": "^2.6.11"
|
||||||
},
|
},
|
||||||
"license": "GPL-2.0"
|
"license": "GPL-2.0"
|
||||||
|
@ -9,8 +9,7 @@
|
|||||||
<modal-dialog-remote-pairing :show="pairing_active" @close="pairing_active = false" />
|
<modal-dialog-remote-pairing :show="pairing_active" @close="pairing_active = false" />
|
||||||
<notifications v-show="!show_burger_menu" />
|
<notifications v-show="!show_burger_menu" />
|
||||||
<navbar-bottom />
|
<navbar-bottom />
|
||||||
<div class="is-overlay" v-show="show_burger_menu || show_player_menu"
|
<div class="fd-overlay-fullscreen" v-show="show_burger_menu || show_player_menu"
|
||||||
style="z-index:25; width: 100vw; height:100vh;background-color: rgba(10, 10, 10, 0.2);"
|
|
||||||
@click="show_burger_menu = show_player_menu = false"></div>
|
@click="show_burger_menu = show_player_menu = false"></div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -1,15 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<figure>
|
<figure>
|
||||||
<img
|
<img v-lazyload
|
||||||
v-show="artwork_visible"
|
|
||||||
:src="artwork_url_with_size"
|
|
||||||
@load="artwork_loaded"
|
|
||||||
@error="artwork_error"
|
|
||||||
@click="$emit('click')">
|
|
||||||
<img
|
|
||||||
v-show="!artwork_visible"
|
|
||||||
:src="dataURI"
|
:src="dataURI"
|
||||||
:alt="alt_text"
|
:data-src="artwork_url_with_size"
|
||||||
|
:data-err="dataURI"
|
||||||
@click="$emit('click')">
|
@click="$emit('click')">
|
||||||
</figure>
|
</figure>
|
||||||
</template>
|
</template>
|
||||||
@ -30,9 +24,7 @@ export default {
|
|||||||
height: 600,
|
height: 600,
|
||||||
font_family: 'sans-serif',
|
font_family: 'sans-serif',
|
||||||
font_size: 200,
|
font_size: 200,
|
||||||
font_weight: 600,
|
font_weight: 600
|
||||||
|
|
||||||
artwork_visible: false
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -95,16 +87,6 @@ export default {
|
|||||||
dataURI () {
|
dataURI () {
|
||||||
return this.svg.render(this.rendererParams)
|
return this.svg.render(this.rendererParams)
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
artwork_loaded: function () {
|
|
||||||
this.artwork_visible = true
|
|
||||||
},
|
|
||||||
|
|
||||||
artwork_error: function () {
|
|
||||||
this.artwork_visible = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
<template functional>
|
<template functional>
|
||||||
<div class="media" :id="'index_' + props.album.name_sort.charAt(0).toUpperCase()">
|
<div class="media" :id="'index_' + props.album.name_sort.charAt(0).toUpperCase()">
|
||||||
|
<slot name="artwork"></slot>
|
||||||
<div class="media-content fd-has-action is-clipped" @click="listeners.click">
|
<div class="media-content fd-has-action is-clipped" @click="listeners.click">
|
||||||
<h1 class="title is-6">{{ props.album.name }}</h1>
|
<div style="margin-top:0.7rem;">
|
||||||
<h2 class="subtitle is-7 has-text-grey"><b>{{ props.album.artist }}</b></h2>
|
<h1 class="title is-6">{{ props.album.name }}</h1>
|
||||||
|
<h2 class="subtitle is-7 has-text-grey"><b>{{ props.album.artist }}</b></h2>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="media-right">
|
<div class="media-right" style="padding-top:0.7rem;">
|
||||||
<slot name="actions"></slot>
|
<slot name="actions"></slot>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -53,7 +53,12 @@
|
|||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<span class="heading">Quality</span>
|
<span class="heading">Quality</span>
|
||||||
<span class="title is-6">{{ item.type}} | {{ item.samplerate }} Hz | {{ item.channels }} | {{ item.bitrate }} Kb/s</span>
|
<span class="title is-6">
|
||||||
|
{{ item.type }}
|
||||||
|
<span v-if="item.samplerate"> | {{ item.samplerate }} Hz</span>
|
||||||
|
<span v-if="item.channels"> | {{ item.channels | channels }}</span>
|
||||||
|
<span v-if="item.bitrate"> | {{ item.bitrate }} Kb/s</span>
|
||||||
|
</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
</p>
|
</p>
|
||||||
<p v-if="track.date_released">
|
<p v-if="track.date_released">
|
||||||
<span class="heading">Release date</span>
|
<span class="heading">Release date</span>
|
||||||
<span class="title is-6">{{ track.date_released | time('L')}}</span>
|
<span class="title is-6">{{ track.date_released | time('L') }}</span>
|
||||||
</p>
|
</p>
|
||||||
<p v-else-if="track.year > 0">
|
<p v-else-if="track.year > 0">
|
||||||
<span class="heading">Year</span>
|
<span class="heading">Year</span>
|
||||||
@ -59,7 +59,12 @@
|
|||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<span class="heading">Quality</span>
|
<span class="heading">Quality</span>
|
||||||
<span class="title is-6">{{ track.type}} | {{ track.samplerate}} Hz | {{ track.channels }} channels | {{ track.bitrate}} Kb/s</span>
|
<span class="title is-6">
|
||||||
|
{{ track.type }}
|
||||||
|
<span v-if="track.samplerate"> | {{ track.samplerate }} Hz</span>
|
||||||
|
<span v-if="track.channels"> | {{ track.channels | channels }}</span>
|
||||||
|
<span v-if="track.bitrate"> | {{ track.bitrate }} Kb/s</span>
|
||||||
|
</span>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<span class="heading">Added at</span>
|
<span class="heading">Added at</span>
|
||||||
|
@ -49,6 +49,7 @@
|
|||||||
<navbar-item-link to="/music/artists"><span class="fd-navbar-item-level2">Artists</span></navbar-item-link>
|
<navbar-item-link to="/music/artists"><span class="fd-navbar-item-level2">Artists</span></navbar-item-link>
|
||||||
<navbar-item-link to="/music/albums"><span class="fd-navbar-item-level2">Albums</span></navbar-item-link>
|
<navbar-item-link to="/music/albums"><span class="fd-navbar-item-level2">Albums</span></navbar-item-link>
|
||||||
<navbar-item-link to="/music/genres"><span class="fd-navbar-item-level2">Genres</span></navbar-item-link>
|
<navbar-item-link to="/music/genres"><span class="fd-navbar-item-level2">Genres</span></navbar-item-link>
|
||||||
|
<navbar-item-link to="/music/radio"><span class="fd-navbar-item-level2">Radio</span></navbar-item-link>
|
||||||
<navbar-item-link to="/music/spotify" v-if="spotify_enabled"><span class="fd-navbar-item-level2">Spotify</span></navbar-item-link>
|
<navbar-item-link to="/music/spotify" v-if="spotify_enabled"><span class="fd-navbar-item-level2">Spotify</span></navbar-item-link>
|
||||||
<navbar-item-link to="/podcasts"><span class="icon"><i class="mdi mdi-microphone"></i></span> <b>Podcasts</b></navbar-item-link>
|
<navbar-item-link to="/podcasts"><span class="icon"><i class="mdi mdi-microphone"></i></span> <b>Podcasts</b></navbar-item-link>
|
||||||
<navbar-item-link to="/audiobooks"><span class="icon"><i class="mdi mdi-book-open-variant"></i></span> <b>Audiobooks</b></navbar-item-link>
|
<navbar-item-link to="/audiobooks"><span class="icon"><i class="mdi mdi-book-open-variant"></i></span> <b>Audiobooks</b></navbar-item-link>
|
||||||
@ -56,9 +57,6 @@
|
|||||||
<navbar-item-link to="/search"><span class="icon"><i class="mdi mdi-magnify"></i></span> <b>Search</b></navbar-item-link>
|
<navbar-item-link to="/search"><span class="icon"><i class="mdi mdi-magnify"></i></span> <b>Search</b></navbar-item-link>
|
||||||
<hr class="fd-navbar-divider">
|
<hr class="fd-navbar-divider">
|
||||||
|
|
||||||
<a class="navbar-item" href="/admin.html">Admin</a>
|
|
||||||
<hr class="fd-navbar-divider">
|
|
||||||
|
|
||||||
<navbar-item-link to="/settings/webinterface">Settings</navbar-item-link>
|
<navbar-item-link to="/settings/webinterface">Settings</navbar-item-link>
|
||||||
<navbar-item-link to="/about">About</navbar-item-link>
|
<navbar-item-link to="/about">About</navbar-item-link>
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<div class="media-content fd-has-action is-clipped" v-on:click="open_album">
|
<div class="media-content fd-has-action is-clipped" v-on:click="open_album">
|
||||||
<h1 class="title is-6">{{ album.name }}</h1>
|
<h1 class="title is-6">{{ album.name }}</h1>
|
||||||
<h2 class="subtitle is-7 has-text-grey"><b>{{ album.artists[0].name }}</b></h2>
|
<h2 class="subtitle is-7 has-text-grey"><b>{{ album.artists[0].name }}</b></h2>
|
||||||
<h2 class="subtitle is-7 has-text-grey has-text-weight-normal">({{ album.album_type }}, {{ album.release_date }})</h2>
|
<h2 class="subtitle is-7 has-text-grey has-text-weight-normal">({{ album.album_type }}, {{ album.release_date | time('L') }})</h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="media-right">
|
<div class="media-right">
|
||||||
<slot name="actions"></slot>
|
<slot name="actions"></slot>
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<span class="heading">Release date</span>
|
<span class="heading">Release date</span>
|
||||||
<span class="title is-6">{{ album.release_date }}</span>
|
<span class="title is-6">{{ album.release_date | time('L') }}</span>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<span class="heading">Type</span>
|
<span class="heading">Type</span>
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<span class="heading">Release date</span>
|
<span class="heading">Release date</span>
|
||||||
<span class="title is-6">{{ album.release_date }}</span>
|
<span class="title is-6">{{ album.release_date | time('L') }}</span>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<span class="heading">Track / Disc</span>
|
<span class="heading">Track / Disc</span>
|
||||||
|
@ -29,6 +29,12 @@
|
|||||||
<span class="">Genres</span>
|
<span class="">Genres</span>
|
||||||
</a>
|
</a>
|
||||||
</router-link>
|
</router-link>
|
||||||
|
<router-link tag="li" to="/music/radio" active-class="is-active">
|
||||||
|
<a>
|
||||||
|
<span class="icon is-small"><i class="mdi mdi-radio"></i></span>
|
||||||
|
<span class="">Radio</span>
|
||||||
|
</a>
|
||||||
|
</router-link>
|
||||||
<router-link tag="li" to="/music/spotify" v-if="spotify_enabled" active-class="is-active">
|
<router-link tag="li" to="/music/spotify" v-if="spotify_enabled" active-class="is-active">
|
||||||
<a>
|
<a>
|
||||||
<span class="icon is-small"><i class="mdi mdi-spotify"></i></span>
|
<span class="icon is-small"><i class="mdi mdi-spotify"></i></span>
|
||||||
|
@ -24,3 +24,16 @@ Vue.filter('timeFromNow', function (value, withoutSuffix) {
|
|||||||
Vue.filter('number', function (value) {
|
Vue.filter('number', function (value) {
|
||||||
return value.toLocaleString()
|
return value.toLocaleString()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Vue.filter('channels', function (value) {
|
||||||
|
if (value === 1) {
|
||||||
|
return 'mono'
|
||||||
|
}
|
||||||
|
if (value === 2) {
|
||||||
|
return 'stereo'
|
||||||
|
}
|
||||||
|
if (!value) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
return value + ' channels'
|
||||||
|
})
|
||||||
|
@ -7,14 +7,14 @@ import store from './store'
|
|||||||
import './filter'
|
import './filter'
|
||||||
import './progress'
|
import './progress'
|
||||||
import vClickOutside from 'v-click-outside'
|
import vClickOutside from 'v-click-outside'
|
||||||
import 'bulma/css/bulma.css'
|
import VueTinyLazyloadImg from 'vue-tiny-lazyload-img'
|
||||||
import 'mdi/css/materialdesignicons.css'
|
import 'mdi/css/materialdesignicons.css'
|
||||||
import 'vue-range-slider/dist/vue-range-slider.css'
|
import 'vue-range-slider/dist/vue-range-slider.css'
|
||||||
import './mystyles.css'
|
import './mystyles.scss'
|
||||||
|
|
||||||
Vue.config.productionTip = false
|
Vue.config.productionTip = false
|
||||||
|
|
||||||
Vue.use(vClickOutside)
|
Vue.use(vClickOutside)
|
||||||
|
Vue.use(VueTinyLazyloadImg)
|
||||||
|
|
||||||
/* eslint-disable no-new */
|
/* eslint-disable no-new */
|
||||||
new Vue({
|
new Vue({
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
|
|
||||||
|
@import 'bulma';
|
||||||
|
|
||||||
|
|
||||||
.slider {
|
.slider {
|
||||||
min-width: 250px;
|
min-width: 250px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -100,6 +103,10 @@ section.fd-tabs-section + section.fd-content {
|
|||||||
margin-top: 24px;
|
margin-top: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
section.hero + section.fd-content {
|
||||||
|
padding-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.fd-progress-bar {
|
.fd-progress-bar {
|
||||||
top: 52px !important;
|
top: 52px !important;
|
||||||
}
|
}
|
||||||
@ -246,4 +253,43 @@ hr.fd-navbar-divider {
|
|||||||
.fd-bottom-navbar .navbar-menu {
|
.fd-bottom-navbar .navbar-menu {
|
||||||
max-height: calc(100vh - 3.25rem - 3.25rem - 1rem);
|
max-height: calc(100vh - 3.25rem - 3.25rem - 1rem);
|
||||||
overflow: scroll;
|
overflow: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.buttons {
|
||||||
|
@include mobile {
|
||||||
|
&.fd-is-centered-mobile {
|
||||||
|
justify-content: center;
|
||||||
|
&:not(.has-addons) {
|
||||||
|
.button:not(.is-fullwidth) {
|
||||||
|
margin-left: 0.25rem;
|
||||||
|
margin-right: 0.25rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.column {
|
||||||
|
&.fd-has-cover {
|
||||||
|
max-height: 150px;
|
||||||
|
max-width: 150px;
|
||||||
|
@include mobile {
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
@include from($tablet) {
|
||||||
|
margin: auto 0 auto auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fd-overlay-fullscreen {
|
||||||
|
@extend .is-overlay;
|
||||||
|
z-index:25;
|
||||||
|
background-color: rgba(10, 10, 10, 0.2);
|
||||||
|
position: fixed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero-body {
|
||||||
|
padding: 1.5rem !important;
|
||||||
}
|
}
|
@ -1,21 +1,29 @@
|
|||||||
<template>
|
<template>
|
||||||
<content-with-heading>
|
<content-with-hero>
|
||||||
<template slot="heading-left">
|
<template slot="heading-left">
|
||||||
<div class="title is-4">{{ album.name }}</div>
|
<h1 class="title is-5">{{ album.name }}</h1>
|
||||||
<a class="title is-4 has-text-link has-text-weight-normal" @click="open_artist">{{ album.artist }}</a>
|
<h2 class="subtitle is-6 has-text-link has-text-weight-normal"><a class="has-text-link" @click="open_artist">{{ album.artist }}</a></h2>
|
||||||
</template>
|
|
||||||
<template slot="heading-right">
|
<div class="buttons fd-is-centered-mobile fd-has-margin-top">
|
||||||
<div class="buttons is-centered">
|
|
||||||
<a class="button is-small is-light is-rounded" @click="show_album_details_modal = true">
|
|
||||||
<span class="icon"><i class="mdi mdi-dots-horizontal mdi-18px"></i></span>
|
|
||||||
</a>
|
|
||||||
<a class="button is-small is-dark is-rounded" @click="play">
|
<a class="button is-small is-dark is-rounded" @click="play">
|
||||||
<span class="icon"><i class="mdi mdi-shuffle"></i></span> <span>Shuffle</span>
|
<span class="icon"><i class="mdi mdi-shuffle"></i></span> <span>Shuffle</span>
|
||||||
</a>
|
</a>
|
||||||
|
<a class="button is-small is-light is-rounded" @click="show_album_details_modal = true">
|
||||||
|
<span class="icon"><i class="mdi mdi-dots-horizontal mdi-18px"></i></span>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
<template slot="heading-right">
|
||||||
|
<p class="image is-square fd-has-shadow fd-has-action">
|
||||||
|
<cover-artwork
|
||||||
|
:artwork_url="album.artwork_url"
|
||||||
|
:artist="album.artist"
|
||||||
|
:album="album.name"
|
||||||
|
@click="show_album_details_modal = true" />
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
<template slot="content">
|
<template slot="content">
|
||||||
<p class="heading has-text-centered-mobile">{{ album.track_count }} tracks</p>
|
<p class="heading is-7 has-text-centered-mobile fd-has-margin-top">{{ album.track_count }} tracks</p>
|
||||||
<list-item-track v-for="(track, index) in tracks" :key="track.id" :track="track" @click="play_track(index)">
|
<list-item-track v-for="(track, index) in tracks" :key="track.id" :track="track" @click="play_track(index)">
|
||||||
<template slot="actions">
|
<template slot="actions">
|
||||||
<a @click="open_dialog(track)">
|
<a @click="open_dialog(track)">
|
||||||
@ -26,15 +34,16 @@
|
|||||||
<modal-dialog-track :show="show_details_modal" :track="selected_track" @close="show_details_modal = false" />
|
<modal-dialog-track :show="show_details_modal" :track="selected_track" @close="show_details_modal = false" />
|
||||||
<modal-dialog-album :show="show_album_details_modal" :album="album" @close="show_album_details_modal = false" />
|
<modal-dialog-album :show="show_album_details_modal" :album="album" @close="show_album_details_modal = false" />
|
||||||
</template>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-hero>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
import ContentWithHero from '@/templates/ContentWithHero'
|
||||||
import ListItemTrack from '@/components/ListItemTrack'
|
import ListItemTrack from '@/components/ListItemTrack'
|
||||||
import ModalDialogTrack from '@/components/ModalDialogTrack'
|
import ModalDialogTrack from '@/components/ModalDialogTrack'
|
||||||
import ModalDialogAlbum from '@/components/ModalDialogAlbum'
|
import ModalDialogAlbum from '@/components/ModalDialogAlbum'
|
||||||
|
import CoverArtwork from '@/components/CoverArtwork'
|
||||||
import webapi from '@/webapi'
|
import webapi from '@/webapi'
|
||||||
|
|
||||||
const albumData = {
|
const albumData = {
|
||||||
@ -54,7 +63,7 @@ const albumData = {
|
|||||||
export default {
|
export default {
|
||||||
name: 'PageAlbum',
|
name: 'PageAlbum',
|
||||||
mixins: [LoadDataBeforeEnterMixin(albumData)],
|
mixins: [LoadDataBeforeEnterMixin(albumData)],
|
||||||
components: { ContentWithHeading, ListItemTrack, ModalDialogTrack, ModalDialogAlbum },
|
components: { ContentWithHero, ListItemTrack, ModalDialogTrack, ModalDialogAlbum, CoverArtwork },
|
||||||
|
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
@ -47,7 +47,7 @@ import * as types from '@/store/mutation_types'
|
|||||||
|
|
||||||
const albumsData = {
|
const albumsData = {
|
||||||
load: function (to) {
|
load: function (to) {
|
||||||
return webapi.library_albums()
|
return webapi.library_albums('music')
|
||||||
},
|
},
|
||||||
|
|
||||||
set: function (vm, response) {
|
set: function (vm, response) {
|
||||||
|
@ -40,7 +40,7 @@ const artistData = {
|
|||||||
load: function (to) {
|
load: function (to) {
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
webapi.library_artist(to.params.artist_id),
|
webapi.library_artist(to.params.artist_id),
|
||||||
webapi.library_albums(to.params.artist_id)
|
webapi.library_artist_albums(to.params.artist_id)
|
||||||
])
|
])
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ import webapi from '@/webapi'
|
|||||||
|
|
||||||
const albumsData = {
|
const albumsData = {
|
||||||
load: function (to) {
|
load: function (to) {
|
||||||
return webapi.library_audiobooks()
|
return webapi.library_albums('audiobook')
|
||||||
},
|
},
|
||||||
|
|
||||||
set: function (vm, response) {
|
set: function (vm, response) {
|
||||||
|
106
web-src/src/pages/PageAudiobooksArtists.vue
Normal file
106
web-src/src/pages/PageAudiobooksArtists.vue
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<tabs-music></tabs-music>
|
||||||
|
|
||||||
|
<content-with-heading>
|
||||||
|
<template slot="options">
|
||||||
|
<index-button-list :index="index_list"></index-button-list>
|
||||||
|
</template>
|
||||||
|
<template slot="heading-left">
|
||||||
|
<p class="title is-4">Artists</p>
|
||||||
|
<p class="heading">{{ artists.total }} artists</p>
|
||||||
|
</template>
|
||||||
|
<template slot="heading-right">
|
||||||
|
<a class="button is-small" :class="{ 'is-info': hide_singles }" @click="update_hide_singles">
|
||||||
|
<span class="icon">
|
||||||
|
<i class="mdi mdi-numeric-1-box-multiple-outline"></i>
|
||||||
|
</span>
|
||||||
|
<span>Hide singles</span>
|
||||||
|
</a>
|
||||||
|
</template>
|
||||||
|
<template slot="content">
|
||||||
|
<list-item-artist v-for="artist in artists_filtered"
|
||||||
|
:key="artist.id"
|
||||||
|
:artist="artist"
|
||||||
|
@click="open_artist(artist)">
|
||||||
|
<template slot="actions">
|
||||||
|
<a @click="open_dialog(artist)">
|
||||||
|
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
||||||
|
</a>
|
||||||
|
</template>
|
||||||
|
</list-item-artist>
|
||||||
|
<modal-dialog-artist :show="show_details_modal" :artist="selected_artist" @close="show_details_modal = false" />
|
||||||
|
</template>
|
||||||
|
</content-with-heading>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||||
|
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||||
|
import TabsMusic from '@/components/TabsMusic'
|
||||||
|
import IndexButtonList from '@/components/IndexButtonList'
|
||||||
|
import ListItemArtist from '@/components/ListItemArtist'
|
||||||
|
import ModalDialogArtist from '@/components/ModalDialogArtist'
|
||||||
|
import webapi from '@/webapi'
|
||||||
|
import * as types from '@/store/mutation_types'
|
||||||
|
|
||||||
|
const artistsData = {
|
||||||
|
load: function (to) {
|
||||||
|
return webapi.library_artists()
|
||||||
|
},
|
||||||
|
|
||||||
|
set: function (vm, response) {
|
||||||
|
vm.artists = response.data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'PageArtists',
|
||||||
|
mixins: [LoadDataBeforeEnterMixin(artistsData)],
|
||||||
|
components: { ContentWithHeading, TabsMusic, IndexButtonList, ListItemArtist, ModalDialogArtist },
|
||||||
|
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
artists: { items: [] },
|
||||||
|
|
||||||
|
show_details_modal: false,
|
||||||
|
selected_artist: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
hide_singles () {
|
||||||
|
return this.$store.state.hide_singles
|
||||||
|
},
|
||||||
|
|
||||||
|
index_list () {
|
||||||
|
return [...new Set(this.artists.items
|
||||||
|
.filter(artist => !this.$store.state.hide_singles || artist.track_count > (artist.album_count * 2))
|
||||||
|
.map(artist => artist.name_sort.charAt(0).toUpperCase()))]
|
||||||
|
},
|
||||||
|
|
||||||
|
artists_filtered () {
|
||||||
|
return this.artists.items.filter(artist => !this.hide_singles || artist.track_count > (artist.album_count * 2))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
update_hide_singles: function (e) {
|
||||||
|
this.$store.commit(types.HIDE_SINGLES, !this.hide_singles)
|
||||||
|
},
|
||||||
|
|
||||||
|
open_artist: function (artist) {
|
||||||
|
this.$router.push({ path: '/music/artists/' + artist.id })
|
||||||
|
},
|
||||||
|
|
||||||
|
open_dialog: function (artist) {
|
||||||
|
this.selected_artist = artist
|
||||||
|
this.show_details_modal = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
</style>
|
@ -101,7 +101,7 @@ import webapi from '@/webapi'
|
|||||||
const albumsData = {
|
const albumsData = {
|
||||||
load: function (to) {
|
load: function (to) {
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
webapi.library_podcasts(),
|
webapi.library_albums('podcast'),
|
||||||
webapi.library_podcasts_new_episodes()
|
webapi.library_podcasts_new_episodes()
|
||||||
])
|
])
|
||||||
},
|
},
|
||||||
@ -195,7 +195,7 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
reload_podcasts: function () {
|
reload_podcasts: function () {
|
||||||
webapi.library_podcasts().then(({ data }) => {
|
webapi.library_albums('podcast').then(({ data }) => {
|
||||||
this.albums = data
|
this.albums = data
|
||||||
this.reload_new_episodes()
|
this.reload_new_episodes()
|
||||||
})
|
})
|
||||||
|
70
web-src/src/pages/PageRadioStreams.vue
Normal file
70
web-src/src/pages/PageRadioStreams.vue
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<tabs-music></tabs-music>
|
||||||
|
|
||||||
|
<content-with-heading>
|
||||||
|
<template slot="heading-left">
|
||||||
|
<p class="title is-4">Radio</p>
|
||||||
|
</template>
|
||||||
|
<template slot="content">
|
||||||
|
<p class="heading has-text-centered-mobile">{{ tracks.total }} tracks</p>
|
||||||
|
<list-item-track v-for="track in tracks.items" :key="track.id" :track="track" @click="play_track(track)">
|
||||||
|
<template slot="actions">
|
||||||
|
<a @click="open_dialog(track)">
|
||||||
|
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
||||||
|
</a>
|
||||||
|
</template>
|
||||||
|
</list-item-track>
|
||||||
|
<modal-dialog-track :show="show_details_modal" :track="selected_track" @close="show_details_modal = false" />
|
||||||
|
</template>
|
||||||
|
</content-with-heading>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||||
|
import TabsMusic from '@/components/TabsMusic'
|
||||||
|
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||||
|
import ListItemTrack from '@/components/ListItemTrack'
|
||||||
|
import ModalDialogTrack from '@/components/ModalDialogTrack'
|
||||||
|
import webapi from '@/webapi'
|
||||||
|
|
||||||
|
const streamsData = {
|
||||||
|
load: function (to) {
|
||||||
|
return webapi.library_radio_streams()
|
||||||
|
},
|
||||||
|
|
||||||
|
set: function (vm, response) {
|
||||||
|
vm.tracks = response.data.tracks
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'PageRadioStreams',
|
||||||
|
mixins: [LoadDataBeforeEnterMixin(streamsData)],
|
||||||
|
components: { TabsMusic, ContentWithHeading, ListItemTrack, ModalDialogTrack },
|
||||||
|
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
tracks: { items: [] },
|
||||||
|
|
||||||
|
show_details_modal: false,
|
||||||
|
selected_track: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
play_track: function (track) {
|
||||||
|
webapi.player_play_uri(track.uri, false)
|
||||||
|
},
|
||||||
|
|
||||||
|
open_dialog: function (track) {
|
||||||
|
this.selected_track = track
|
||||||
|
this.show_details_modal = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
</style>
|
@ -49,7 +49,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<form @submit.prevent="kickoff_verification" v-if="output.needs_auth_key" class="fd-has-margin-bottom">
|
<form @submit.prevent="kickoff_verification(output.id)" v-if="output.needs_auth_key" class="fd-has-margin-bottom">
|
||||||
<div class="field is-grouped">
|
<div class="field is-grouped">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<input class="input" type="text" placeholder="Enter verification code" v-model="verification_req.pin">
|
<input class="input" type="text" placeholder="Enter verification code" v-model="verification_req.pin">
|
||||||
@ -100,8 +100,8 @@ export default {
|
|||||||
webapi.output_toggle(outputId)
|
webapi.output_toggle(outputId)
|
||||||
},
|
},
|
||||||
|
|
||||||
kickoff_verification () {
|
kickoff_verification (outputId) {
|
||||||
webapi.verification_kickoff(this.verification_req)
|
webapi.output_update(outputId, this.verification_req)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1,21 +1,31 @@
|
|||||||
<template>
|
<template>
|
||||||
<content-with-heading>
|
<content-with-hero>
|
||||||
<template slot="heading-left">
|
<template slot="heading-left">
|
||||||
<div class="title is-4">{{ album.name }}</div>
|
<h1 class="title is-5">{{ album.name }}</h1>
|
||||||
<a class="title is-4 has-text-link has-text-weight-normal" @click="open_artist">{{ album.artists[0].name }}</a>
|
<h2 class="subtitle is-6 has-text-link has-text-weight-normal"><a class="has-text-link" @click="open_artist">{{ album.artists[0].name }}</a></h2>
|
||||||
</template>
|
|
||||||
<template slot="heading-right">
|
<div class="buttons fd-is-centered-mobile fd-has-margin-top">
|
||||||
<div class="buttons is-centered">
|
|
||||||
<a class="button is-small is-light is-rounded" @click="show_album_details_modal = true">
|
|
||||||
<span class="icon"><i class="mdi mdi-dots-horizontal mdi-18px"></i></span>
|
|
||||||
</a>
|
|
||||||
<a class="button is-small is-dark is-rounded" @click="play">
|
<a class="button is-small is-dark is-rounded" @click="play">
|
||||||
<span class="icon"><i class="mdi mdi-shuffle"></i></span> <span>Shuffle</span>
|
<span class="icon"><i class="mdi mdi-shuffle"></i></span> <span>Shuffle</span>
|
||||||
</a>
|
</a>
|
||||||
|
<a class="button is-small is-light is-rounded" @click="show_album_details_modal = true">
|
||||||
|
<span class="icon"><i class="mdi mdi-dots-horizontal mdi-18px"></i></span>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<template slot="heading-right">
|
||||||
|
<p class="image is-square fd-has-shadow fd-has-action">
|
||||||
|
<cover-artwork
|
||||||
|
:artwork_url="artwork_url"
|
||||||
|
:artist="album.artist"
|
||||||
|
:album="album.name"
|
||||||
|
@click="show_album_details_modal = true" />
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
|
|
||||||
<template slot="content">
|
<template slot="content">
|
||||||
<p class="heading has-text-centered-mobile">{{ album.tracks.total }} tracks</p>
|
<p class="heading is-7 has-text-centered-mobile fd-has-margin-top">{{ album.tracks.total }} tracks</p>
|
||||||
<spotify-list-item-track v-for="(track, index) in album.tracks.items" :key="track.id" :track="track" :position="index" :album="album" :context_uri="album.uri">
|
<spotify-list-item-track v-for="(track, index) in album.tracks.items" :key="track.id" :track="track" :position="index" :album="album" :context_uri="album.uri">
|
||||||
<template slot="actions">
|
<template slot="actions">
|
||||||
<a @click="open_track_dialog(track)">
|
<a @click="open_track_dialog(track)">
|
||||||
@ -26,15 +36,16 @@
|
|||||||
<spotify-modal-dialog-track :show="show_track_details_modal" :track="selected_track" :album="album" @close="show_track_details_modal = false" />
|
<spotify-modal-dialog-track :show="show_track_details_modal" :track="selected_track" :album="album" @close="show_track_details_modal = false" />
|
||||||
<spotify-modal-dialog-album :show="show_album_details_modal" :album="album" @close="show_album_details_modal = false" />
|
<spotify-modal-dialog-album :show="show_album_details_modal" :album="album" @close="show_album_details_modal = false" />
|
||||||
</template>
|
</template>
|
||||||
</content-with-heading>
|
</content-with-hero>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
import ContentWithHero from '@/templates/ContentWithHero'
|
||||||
import SpotifyListItemTrack from '@/components/SpotifyListItemTrack'
|
import SpotifyListItemTrack from '@/components/SpotifyListItemTrack'
|
||||||
import SpotifyModalDialogTrack from '@/components/SpotifyModalDialogTrack'
|
import SpotifyModalDialogTrack from '@/components/SpotifyModalDialogTrack'
|
||||||
import SpotifyModalDialogAlbum from '@/components/SpotifyModalDialogAlbum'
|
import SpotifyModalDialogAlbum from '@/components/SpotifyModalDialogAlbum'
|
||||||
|
import CoverArtwork from '@/components/CoverArtwork'
|
||||||
import store from '@/store'
|
import store from '@/store'
|
||||||
import webapi from '@/webapi'
|
import webapi from '@/webapi'
|
||||||
import SpotifyWebApi from 'spotify-web-api-js'
|
import SpotifyWebApi from 'spotify-web-api-js'
|
||||||
@ -54,7 +65,7 @@ const albumData = {
|
|||||||
export default {
|
export default {
|
||||||
name: 'PageAlbum',
|
name: 'PageAlbum',
|
||||||
mixins: [LoadDataBeforeEnterMixin(albumData)],
|
mixins: [LoadDataBeforeEnterMixin(albumData)],
|
||||||
components: { ContentWithHeading, SpotifyListItemTrack, SpotifyModalDialogTrack, SpotifyModalDialogAlbum },
|
components: { ContentWithHero, SpotifyListItemTrack, SpotifyModalDialogTrack, SpotifyModalDialogAlbum, CoverArtwork },
|
||||||
|
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
@ -67,6 +78,15 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
artwork_url: function () {
|
||||||
|
if (this.album.images && this.album.images.length > 0) {
|
||||||
|
return this.album.images[0].url
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
open_artist: function () {
|
open_artist: function () {
|
||||||
this.$router.push({ path: '/music/spotify/artists/' + this.album.artists[0].id })
|
this.$router.push({ path: '/music/spotify/artists/' + this.album.artists[0].id })
|
||||||
|
@ -22,6 +22,7 @@ import PageAudiobook from '@/pages/PageAudiobook'
|
|||||||
import PagePlaylists from '@/pages/PagePlaylists'
|
import PagePlaylists from '@/pages/PagePlaylists'
|
||||||
import PagePlaylist from '@/pages/PagePlaylist'
|
import PagePlaylist from '@/pages/PagePlaylist'
|
||||||
import PageFiles from '@/pages/PageFiles'
|
import PageFiles from '@/pages/PageFiles'
|
||||||
|
import PageRadioStreams from '@/pages/PageRadioStreams'
|
||||||
import PageSearch from '@/pages/PageSearch'
|
import PageSearch from '@/pages/PageSearch'
|
||||||
import PageAbout from '@/pages/PageAbout'
|
import PageAbout from '@/pages/PageAbout'
|
||||||
import SpotifyPageBrowse from '@/pages/SpotifyPageBrowse'
|
import SpotifyPageBrowse from '@/pages/SpotifyPageBrowse'
|
||||||
@ -125,6 +126,12 @@ export const router = new VueRouter({
|
|||||||
component: PageGenreTracks,
|
component: PageGenreTracks,
|
||||||
meta: { show_progress: true, has_index: true }
|
meta: { show_progress: true, has_index: true }
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/music/radio',
|
||||||
|
name: 'Radio',
|
||||||
|
component: PageRadioStreams,
|
||||||
|
meta: { show_progress: true, has_tabs: true }
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/podcasts',
|
path: '/podcasts',
|
||||||
name: 'Podcasts',
|
name: 'Podcasts',
|
||||||
|
44
web-src/src/templates/ContentWithHero.vue
Normal file
44
web-src/src/templates/ContentWithHero.vue
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<section class="hero is-light is-bold fd-content">
|
||||||
|
<div class="hero-body">
|
||||||
|
<div class="container">
|
||||||
|
<div class="columns is-centered">
|
||||||
|
<div class="column is-four-fifths">
|
||||||
|
<div class="columns" style="flex-direction: row-reverse;">
|
||||||
|
<div class="column fd-has-cover">
|
||||||
|
<!-- Slot heading right -->
|
||||||
|
<slot name="heading-right"></slot>
|
||||||
|
</div>
|
||||||
|
<div class="column is-three-fifths has-text-centered-mobile" style="margin: auto 0;">
|
||||||
|
<!-- Slot heading left -->
|
||||||
|
<slot name="heading-left"></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section class="section fd-content">
|
||||||
|
<div class="container">
|
||||||
|
<div class="columns is-centered">
|
||||||
|
<div class="column is-four-fifths">
|
||||||
|
<!-- Slot content -->
|
||||||
|
<slot name="content"></slot>
|
||||||
|
<div style="margin-top: 16px;">
|
||||||
|
<!-- Slot footer -->
|
||||||
|
<slot name="footer"></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
</style>
|
@ -208,11 +208,12 @@ export default {
|
|||||||
return axios.get('/api/library/artists/' + artistId)
|
return axios.get('/api/library/artists/' + artistId)
|
||||||
},
|
},
|
||||||
|
|
||||||
library_albums (artistId) {
|
library_artist_albums (artistId) {
|
||||||
if (artistId) {
|
return axios.get('/api/library/artists/' + artistId + '/albums')
|
||||||
return axios.get('/api/library/artists/' + artistId + '/albums')
|
},
|
||||||
}
|
|
||||||
return axios.get('/api/library/albums?media_kind=music')
|
library_albums (media_kind = undefined) {
|
||||||
|
return axios.get('/api/library/albums', { params: { media_kind: media_kind } })
|
||||||
},
|
},
|
||||||
|
|
||||||
library_album (albumId) {
|
library_album (albumId) {
|
||||||
@ -255,6 +256,17 @@ export default {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
library_radio_streams () {
|
||||||
|
var params = {
|
||||||
|
type: 'tracks',
|
||||||
|
media_kind: 'music',
|
||||||
|
expression: 'data_kind is url and song_length = 0'
|
||||||
|
}
|
||||||
|
return axios.get('/api/search', {
|
||||||
|
params: params
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
library_artist_tracks (artist) {
|
library_artist_tracks (artist) {
|
||||||
if (artist) {
|
if (artist) {
|
||||||
var artistParams = {
|
var artistParams = {
|
||||||
@ -267,10 +279,6 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
library_podcasts () {
|
|
||||||
return axios.get('/api/library/albums?media_kind=podcast')
|
|
||||||
},
|
|
||||||
|
|
||||||
library_podcasts_new_episodes () {
|
library_podcasts_new_episodes () {
|
||||||
var episodesParams = {
|
var episodesParams = {
|
||||||
type: 'tracks',
|
type: 'tracks',
|
||||||
@ -299,10 +307,6 @@ export default {
|
|||||||
return axios.delete('/api/library/playlists/' + playlistId, undefined)
|
return axios.delete('/api/library/playlists/' + playlistId, undefined)
|
||||||
},
|
},
|
||||||
|
|
||||||
library_audiobooks () {
|
|
||||||
return axios.get('/api/library/albums?media_kind=audiobook')
|
|
||||||
},
|
|
||||||
|
|
||||||
library_playlists () {
|
library_playlists () {
|
||||||
return axios.get('/api/library/playlists')
|
return axios.get('/api/library/playlists')
|
||||||
},
|
},
|
||||||
@ -372,10 +376,6 @@ export default {
|
|||||||
return axios.post('/api/pairing', pairingReq)
|
return axios.post('/api/pairing', pairingReq)
|
||||||
},
|
},
|
||||||
|
|
||||||
verification_kickoff (verificationReq) {
|
|
||||||
return axios.post('/api/verification', verificationReq)
|
|
||||||
},
|
|
||||||
|
|
||||||
artwork_url_append_size_params (artworkUrl, maxwidth = 600, maxheight = 600) {
|
artwork_url_append_size_params (artworkUrl, maxwidth = 600, maxheight = 600) {
|
||||||
if (artworkUrl && artworkUrl.startsWith('/')) {
|
if (artworkUrl && artworkUrl.startsWith('/')) {
|
||||||
if (artworkUrl.includes('?')) {
|
if (artworkUrl.includes('?')) {
|
||||||
|
Loading…
Reference in New Issue
Block a user