mirror of
https://github.com/owntone/owntone-server.git
synced 2025-01-23 20:53:19 -05:00
Merge pull request #875 from chme/web
Update player web interface v0.6.0
This commit is contained in:
commit
aee916a63f
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
2581
web-src/package-lock.json
generated
2581
web-src/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "forked-daapd-web",
|
||||
"version": "0.5.6",
|
||||
"version": "0.6.0",
|
||||
"description": "forked-daapd web interface",
|
||||
"author": "chme <christian.meffert@googlemail.com>",
|
||||
"license": "GPL-2.0",
|
||||
@ -12,28 +12,28 @@
|
||||
"lint": "vue-cli-service lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.19.0",
|
||||
"bulma": "^0.7.5",
|
||||
"axios": "^0.19.1",
|
||||
"bulma": "^0.8.0",
|
||||
"mdi": "^2.2.43",
|
||||
"moment": "^2.24.0",
|
||||
"moment-duration-format": "^2.3.2",
|
||||
"npm": "^6.11.2",
|
||||
"npm": "^6.13.6",
|
||||
"reconnectingwebsocket": "^1.0.0",
|
||||
"spotify-web-api-js": "^1.2.0",
|
||||
"v-click-outside": "^2.1.4",
|
||||
"vue": "^2.6.10",
|
||||
"v-click-outside": "^3.0.0",
|
||||
"vue": "^2.6.11",
|
||||
"vue-infinite-loading": "^2.4.4",
|
||||
"vue-progressbar": "^0.7.5",
|
||||
"vue-range-slider": "^0.6.0",
|
||||
"vue-router": "^3.1.3",
|
||||
"vuedraggable": "^2.23.0",
|
||||
"vuex": "^3.1.1"
|
||||
"vuedraggable": "^2.23.2",
|
||||
"vuex": "^3.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "^3.11.0",
|
||||
"@vue/cli-plugin-eslint": "^3.11.0",
|
||||
"@vue/cli-service": "^3.11.0",
|
||||
"@vue/cli-plugin-babel": "^3.12.1",
|
||||
"@vue/cli-plugin-eslint": "^3.12.1",
|
||||
"@vue/cli-service": "^3.12.1",
|
||||
"@vue/eslint-config-standard": "^4.0.0",
|
||||
"vue-template-compiler": "^2.6.10"
|
||||
"vue-template-compiler": "^2.6.11"
|
||||
}
|
||||
}
|
||||
|
@ -101,7 +101,7 @@ export default {
|
||||
socket.onopen = function () {
|
||||
vm.$store.dispatch('add_notification', { text: 'Connection to server established', type: 'primary', topic: 'connection', timeout: 2000 })
|
||||
vm.reconnect_attempts = 0
|
||||
socket.send(JSON.stringify({ notify: ['update', 'database', 'player', 'options', 'outputs', 'volume', 'spotify'] }))
|
||||
socket.send(JSON.stringify({ notify: ['update', 'database', 'player', 'options', 'outputs', 'volume', 'spotify', 'lastfm', 'pairing'] }))
|
||||
|
||||
vm.update_outputs()
|
||||
vm.update_player_status()
|
||||
@ -109,6 +109,8 @@ export default {
|
||||
vm.update_settings()
|
||||
vm.update_queue()
|
||||
vm.update_spotify()
|
||||
vm.update_lastfm()
|
||||
vm.update_pairing()
|
||||
}
|
||||
socket.onclose = function () {
|
||||
// vm.$store.dispatch('add_notification', { text: 'Connection closed', type: 'danger', timeout: 2000 })
|
||||
@ -134,6 +136,12 @@ export default {
|
||||
if (data.notify.includes('spotify')) {
|
||||
vm.update_spotify()
|
||||
}
|
||||
if (data.notify.includes('lastfm')) {
|
||||
vm.update_lastfm()
|
||||
}
|
||||
if (data.notify.includes('pairing')) {
|
||||
vm.update_pairing()
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -173,6 +181,12 @@ export default {
|
||||
})
|
||||
},
|
||||
|
||||
update_lastfm: function () {
|
||||
webapi.lastfm().then(({ data }) => {
|
||||
this.$store.commit(types.UPDATE_LASTFM, data)
|
||||
})
|
||||
},
|
||||
|
||||
update_spotify: function () {
|
||||
webapi.spotify().then(({ data }) => {
|
||||
this.$store.commit(types.UPDATE_SPOTIFY, data)
|
||||
@ -185,6 +199,12 @@ export default {
|
||||
this.token_timer_id = window.setTimeout(this.update_spotify, 1000 * data.webapi_token_expires_in)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
update_pairing: function () {
|
||||
webapi.pairing().then(({ data }) => {
|
||||
this.$store.commit(types.UPDATE_PAIRING, data)
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
<div class="media-content fd-has-action is-clipped" v-on:click="open_album">
|
||||
<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 has-text-weight-normal">({{ album.album_type }}, {{ album.release_date }})</h2>
|
||||
</div>
|
||||
<div class="media-right">
|
||||
<slot name="actions"></slot>
|
||||
|
41
web-src/src/components/TabsSettings.vue
Normal file
41
web-src/src/components/TabsSettings.vue
Normal file
@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<section class="section fd-tabs-section">
|
||||
<div class="container">
|
||||
<div class="columns is-centered">
|
||||
<div class="column is-four-fifths">
|
||||
<div class="tabs is-centered is-small">
|
||||
<ul>
|
||||
<router-link tag="li" to="/settings/webinterface" active-class="is-active">
|
||||
<a>
|
||||
<span class="">Webinterface</span>
|
||||
</a>
|
||||
</router-link>
|
||||
<router-link tag="li" to="/settings/remotes-outputs" active-class="is-active">
|
||||
<a>
|
||||
<span class="">Remotes & Outputs</span>
|
||||
</a>
|
||||
</router-link>
|
||||
<router-link tag="li" to="/settings/online-services" active-class="is-active">
|
||||
<a>
|
||||
<span class="">Online Services</span>
|
||||
</a>
|
||||
</router-link>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'TabsSettings',
|
||||
|
||||
computed: {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
205
web-src/src/pages/SettingsPageOnlineServices.vue
Normal file
205
web-src/src/pages/SettingsPageOnlineServices.vue
Normal file
@ -0,0 +1,205 @@
|
||||
<template>
|
||||
<div>
|
||||
<tabs-settings></tabs-settings>
|
||||
|
||||
<content-with-heading>
|
||||
<template slot="heading-left">
|
||||
<div class="title is-4">Spotify</div>
|
||||
</template>
|
||||
|
||||
<template slot="content">
|
||||
<div class="notification is-size-7" v-if="!spotify.libspotify_installed">
|
||||
<p>forked-daapd was either built without support for Spotify or libspotify is not installed.</p>
|
||||
</div>
|
||||
<div v-if="spotify.libspotify_installed">
|
||||
<div class="notification is-size-7">
|
||||
<b>You must have a Spotify premium account</b>. If you normally log into Spotify with your Facebook account you must first go to Spotify's web site where you can get the Spotify username and password that matches your account.
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p class="content">
|
||||
<b>libspotify</b> - Login with your Spotify username and password
|
||||
</p>
|
||||
<p v-if="spotify.libspotify_logged_in" class="fd-has-margin-bottom">
|
||||
Logged in as <b><code>{{ spotify.libspotify_user }}</code></b>
|
||||
</p>
|
||||
<form v-if="spotify.libspotify_installed && !spotify.libspotify_logged_in" @submit.prevent="login_libspotify">
|
||||
<div class="field is-grouped">
|
||||
<div class="control is-expanded">
|
||||
<input class="input" type="text" placeholder="Username" v-model="libspotify.user">
|
||||
<p class="help is-danger">{{ libspotify.errors.user }}</p>
|
||||
</div>
|
||||
<div class="control is-expanded">
|
||||
<input class="input" type="password" placeholder="Password" v-model="libspotify.password">
|
||||
<p class="help is-danger">{{ libspotify.errors.password }}</p>
|
||||
</div>
|
||||
<div class="control">
|
||||
<button class="button is-info">Login</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<p class="help is-danger">{{ libspotify.errors.error }}</p>
|
||||
<p class="help">
|
||||
libspotify enables forked-daapd to play Spotify tracks.
|
||||
</p>
|
||||
<p class="help">
|
||||
forked-daapd will not store your password, but will still be able to log you in automatically afterwards, because libspotify saves a login token.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="fd-has-margin-top">
|
||||
<p class="content">
|
||||
<b>Spotify Web API</b> - Grant access to the Spotify Web API
|
||||
</p>
|
||||
<p v-if="spotify.webapi_token_valid">
|
||||
Access granted for <b><code>{{ spotify.webapi_user }}</code></b>
|
||||
</p>
|
||||
<p class="help is-danger" v-if="spotify_missing_scope.length > 0">
|
||||
Please reauthorize Web API access to grant forked-daapd the following additional access rights:
|
||||
<b><code>{{ spotify_missing_scope | join }}</code></b>
|
||||
</p>
|
||||
<div class="field fd-has-margin-top ">
|
||||
<div class="control">
|
||||
<a class="button" :class="{ 'is-info': !spotify.webapi_token_valid || spotify_missing_scope.length > 0 }" :href="spotify.oauth_uri">Authorize Web API access</a>
|
||||
</div>
|
||||
</div>
|
||||
<p class="help">
|
||||
Access to the Spotify Web API enables scanning of your Spotify library. Required scopes are
|
||||
<code>{{ spotify_required_scope | join }}</code>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</content-with-heading>
|
||||
|
||||
<content-with-heading>
|
||||
<template slot="heading-left">
|
||||
<div class="title is-4">Last.fm</div>
|
||||
</template>
|
||||
|
||||
<template slot="content">
|
||||
<div class="notification is-size-7" v-if="!lastfm.enabled">
|
||||
<p>forked-daapd was built without support for Last.fm.</p>
|
||||
</div>
|
||||
<div v-if="lastfm.enabled">
|
||||
<p class="content">
|
||||
<b>Last.fm</b> - Login with your Last.fm username and password to enable scrobbling
|
||||
</p>
|
||||
<div v-if="lastfm.scrobbling_enabled">
|
||||
<a class="button" @click="logoutLastfm">Stop scrobbling</a>
|
||||
</div>
|
||||
<div v-if="!lastfm.scrobbling_enabled">
|
||||
<form @submit.prevent="login_lastfm">
|
||||
<div class="field is-grouped">
|
||||
<div class="control is-expanded">
|
||||
<input class="input" type="text" placeholder="Username" v-model="lastfm_login.user">
|
||||
<p class="help is-danger">{{ lastfm_login.errors.user }}</p>
|
||||
</div>
|
||||
<div class="control is-expanded">
|
||||
<input class="input" type="password" placeholder="Password" v-model="lastfm_login.password">
|
||||
<p class="help is-danger">{{ lastfm_login.errors.password }}</p>
|
||||
</div>
|
||||
<div class="control">
|
||||
<button class="button is-info" type="submit">Login</button>
|
||||
</div>
|
||||
</div>
|
||||
<p class="help is-danger">{{ lastfm_login.errors.error }}</p>
|
||||
<p class="help">
|
||||
forked-daapd will not store your Last.fm username/password, only the session key. The session key does not expire.
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</content-with-heading>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import TabsSettings from '@/components/TabsSettings'
|
||||
import webapi from '@/webapi'
|
||||
|
||||
export default {
|
||||
name: 'SettingsPageOnlineServices',
|
||||
components: { ContentWithHeading, TabsSettings },
|
||||
|
||||
data () {
|
||||
return {
|
||||
libspotify: { user: '', password: '', errors: { user: '', password: '', error: '' } },
|
||||
lastfm_login: { user: '', password: '', errors: { user: '', password: '', error: '' } }
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
lastfm () {
|
||||
return this.$store.state.lastfm
|
||||
},
|
||||
|
||||
spotify () {
|
||||
return this.$store.state.spotify
|
||||
},
|
||||
|
||||
spotify_required_scope () {
|
||||
if (this.spotify.webapi_token_valid && this.spotify.webapi_granted_scope && this.spotify.webapi_required_scope) {
|
||||
return this.spotify.webapi_required_scope.split(' ')
|
||||
}
|
||||
return []
|
||||
},
|
||||
|
||||
spotify_missing_scope () {
|
||||
if (this.spotify.webapi_token_valid && this.spotify.webapi_granted_scope && this.spotify.webapi_required_scope) {
|
||||
return this.spotify.webapi_required_scope.split(' ').filter(scope => this.spotify.webapi_granted_scope.indexOf(scope) < 0)
|
||||
}
|
||||
return []
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
login_libspotify () {
|
||||
webapi.spotify_login(this.libspotify).then(response => {
|
||||
this.libspotify.user = ''
|
||||
this.libspotify.password = ''
|
||||
this.libspotify.errors.user = ''
|
||||
this.libspotify.errors.password = ''
|
||||
this.libspotify.errors.error = ''
|
||||
|
||||
if (!response.data.success) {
|
||||
this.libspotify.errors.user = response.data.errors.user
|
||||
this.libspotify.errors.password = response.data.errors.password
|
||||
this.libspotify.errors.error = response.data.errors.error
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
login_lastfm () {
|
||||
webapi.lastfm_login(this.lastfm_login).then(response => {
|
||||
this.lastfm_login.user = ''
|
||||
this.lastfm_login.password = ''
|
||||
this.lastfm_login.errors.user = ''
|
||||
this.lastfm_login.errors.password = ''
|
||||
this.lastfm_login.errors.error = ''
|
||||
|
||||
if (!response.data.success) {
|
||||
this.lastfm_login.errors.user = response.data.errors.user
|
||||
this.lastfm_login.errors.password = response.data.errors.password
|
||||
this.lastfm_login.errors.error = response.data.errors.error
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
logoutLastfm () {
|
||||
webapi.lastfm_logout()
|
||||
}
|
||||
},
|
||||
|
||||
filters: {
|
||||
join (array) {
|
||||
return array.join(', ')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
114
web-src/src/pages/SettingsPageRemotesOutputs.vue
Normal file
114
web-src/src/pages/SettingsPageRemotesOutputs.vue
Normal file
@ -0,0 +1,114 @@
|
||||
<template>
|
||||
<div>
|
||||
<tabs-settings></tabs-settings>
|
||||
|
||||
<content-with-heading>
|
||||
<template slot="heading-left">
|
||||
<div class="title is-4">Remote Pairing</div>
|
||||
</template>
|
||||
|
||||
<template slot="content">
|
||||
<!-- Paring request active -->
|
||||
<div class="notification" v-if="pairing.active">
|
||||
<form v-on:submit.prevent="kickoff_pairing">
|
||||
<label class="label has-text-weight-normal">
|
||||
Remote pairing request from <b>{{ pairing.remote }}</b>
|
||||
</label>
|
||||
<div class="field is-grouped">
|
||||
<div class="control">
|
||||
<input class="input" type="text" placeholder="Enter pairing code" v-model="pairing_req.pin">
|
||||
</div>
|
||||
<div class="control">
|
||||
<button class="button is-info" type="submit">Send</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<!-- No pairing requests -->
|
||||
<div class="content" v-if="!pairing.active">
|
||||
<p>No active pairing request.</p>
|
||||
</div>
|
||||
</template>
|
||||
</content-with-heading>
|
||||
|
||||
<content-with-heading>
|
||||
<template slot="heading-left">
|
||||
<div class="title is-4">Device Verification</div>
|
||||
</template>
|
||||
|
||||
<template slot="content">
|
||||
<p class="content">
|
||||
If your Apple TV requires device verification then activate the device below and enter the PIN that the Apple TV displays.
|
||||
</p>
|
||||
|
||||
<div v-for="output in outputs" :key="output.id">
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" v-model="output.selected" @change="output_toggle(output.id)"> {{ output.name }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<form @submit.prevent="kickoff_verification" v-if="output.needs_auth_key" class="fd-has-margin-bottom">
|
||||
<div class="field is-grouped">
|
||||
<div class="control">
|
||||
<input class="input" type="text" placeholder="Enter verification code" v-model="verification_req.pin">
|
||||
</div>
|
||||
<div class="control">
|
||||
<button class="button is-info" type="submit">Verify</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</template>
|
||||
</content-with-heading>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import TabsSettings from '@/components/TabsSettings'
|
||||
import webapi from '@/webapi'
|
||||
|
||||
export default {
|
||||
name: 'SettingsPageRemotesOutputs',
|
||||
components: { ContentWithHeading, TabsSettings },
|
||||
|
||||
data () {
|
||||
return {
|
||||
pairing_req: { pin: '' },
|
||||
verification_req: { pin: '' }
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
pairing () {
|
||||
return this.$store.state.pairing
|
||||
},
|
||||
|
||||
outputs () {
|
||||
return this.$store.state.outputs
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
kickoff_pairing () {
|
||||
webapi.pairing_kickoff(this.pairing_req)
|
||||
},
|
||||
|
||||
output_toggle (outputId) {
|
||||
webapi.output_toggle(outputId)
|
||||
},
|
||||
|
||||
kickoff_verification () {
|
||||
webapi.verification_kickoff(this.verification_req)
|
||||
}
|
||||
},
|
||||
|
||||
filters: {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
@ -1,71 +1,70 @@
|
||||
<template>
|
||||
<content-with-heading>
|
||||
<template slot="heading-left">
|
||||
<div class="title is-4">Settings</div>
|
||||
</template>
|
||||
<div>
|
||||
<tabs-settings></tabs-settings>
|
||||
|
||||
<template slot="heading-right">
|
||||
</template>
|
||||
<content-with-heading>
|
||||
<template slot="heading-left">
|
||||
<div class="title is-4">Now playing page</div>
|
||||
</template>
|
||||
|
||||
<template slot="content">
|
||||
<div class="heading fd-has-margin-bottom">Now playing page</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" :checked="settings_option_show_composer_now_playing" @change="set_timer_show_composer_now_playing" ref="checkbox_show_composer">
|
||||
Show composer
|
||||
<i class="is-size-7"
|
||||
:class="{
|
||||
'has-text-info': statusUpdateShowComposerNowPlaying === 'success',
|
||||
'has-text-danger': statusUpdateShowComposerNowPlaying === 'error'
|
||||
}">{{ info_option_show_composer_now_playing }}</i>
|
||||
</label>
|
||||
<p class="help has-text-justified">
|
||||
If enabled the composer of the current playing track is shown on the "now playing page"
|
||||
</p>
|
||||
</div>
|
||||
<fieldset :disabled="!settings_option_show_composer_now_playing">
|
||||
<template slot="content">
|
||||
<div class="field">
|
||||
<label class="label has-text-weight-normal">
|
||||
Show composer only for listed genres
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" :checked="settings_option_show_composer_now_playing" @change="set_timer_show_composer_now_playing" ref="checkbox_show_composer">
|
||||
Show composer
|
||||
<i class="is-size-7"
|
||||
:class="{
|
||||
'has-text-info': statusUpdateShowComposerForGenre === 'success',
|
||||
'has-text-danger': statusUpdateShowComposerForGenre === 'error'
|
||||
}">{{ info_option_show_composer_for_genre }}</i>
|
||||
'has-text-info': statusUpdateShowComposerNowPlaying === 'success',
|
||||
'has-text-danger': statusUpdateShowComposerNowPlaying === 'error'
|
||||
}">{{ info_option_show_composer_now_playing }}</i>
|
||||
</label>
|
||||
<div class="control">
|
||||
<input class="input" type="text" placeholder="Genres"
|
||||
:value="settings_option_show_composer_for_genre"
|
||||
@input="set_timer_show_composer_for_genre"
|
||||
ref="field_composer_for_genre">
|
||||
</div>
|
||||
<p class="help">
|
||||
Comma separated list of genres the composer should be displayed on the "now playing page".
|
||||
</p>
|
||||
<p class="help">
|
||||
Leave empty to always show the composer.
|
||||
</p>
|
||||
<p class="help">
|
||||
The genre tag of the current track is matched by checking, if one of the defined genres are included.
|
||||
For example setting to <code>classical, soundtrack</code> will show the composer for tracks with
|
||||
a genre tag of "Contemporary Classical".<br>
|
||||
<p class="help has-text-justified">
|
||||
If enabled the composer of the current playing track is shown on the "now playing page"
|
||||
</p>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
</template>
|
||||
</content-with-heading>
|
||||
<fieldset :disabled="!settings_option_show_composer_now_playing">
|
||||
<div class="field">
|
||||
<label class="label has-text-weight-normal">
|
||||
Show composer only for listed genres
|
||||
<i class="is-size-7"
|
||||
:class="{
|
||||
'has-text-info': statusUpdateShowComposerForGenre === 'success',
|
||||
'has-text-danger': statusUpdateShowComposerForGenre === 'error'
|
||||
}">{{ info_option_show_composer_for_genre }}</i>
|
||||
</label>
|
||||
<div class="control">
|
||||
<input class="input" type="text" placeholder="Genres"
|
||||
:value="settings_option_show_composer_for_genre"
|
||||
@input="set_timer_show_composer_for_genre"
|
||||
ref="field_composer_for_genre">
|
||||
</div>
|
||||
<p class="help">
|
||||
Comma separated list of genres the composer should be displayed on the "now playing page".
|
||||
</p>
|
||||
<p class="help">
|
||||
Leave empty to always show the composer.
|
||||
</p>
|
||||
<p class="help">
|
||||
The genre tag of the current track is matched by checking, if one of the defined genres are included.
|
||||
For example setting to <code>classical, soundtrack</code> will show the composer for tracks with
|
||||
a genre tag of "Contemporary Classical".<br>
|
||||
</p>
|
||||
</div>
|
||||
</fieldset>
|
||||
</template>
|
||||
</content-with-heading>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import TabsSettings from '@/components/TabsSettings'
|
||||
import webapi from '@/webapi'
|
||||
import * as types from '@/store/mutation_types'
|
||||
|
||||
export default {
|
||||
name: 'SettingsPageWebinterface',
|
||||
components: { ContentWithHeading },
|
||||
components: { ContentWithHeading, TabsSettings },
|
||||
|
||||
data () {
|
||||
return {
|
||||
|
@ -32,6 +32,8 @@ import SpotifyPageAlbum from '@/pages/SpotifyPageAlbum'
|
||||
import SpotifyPagePlaylist from '@/pages/SpotifyPagePlaylist'
|
||||
import SpotifyPageSearch from '@/pages/SpotifyPageSearch'
|
||||
import SettingsPageWebinterface from '@/pages/SettingsPageWebinterface'
|
||||
import SettingsPageOnlineServices from '@/pages/SettingsPageOnlineServices'
|
||||
import SettingsPageRemotesOutputs from '@/pages/SettingsPageRemotesOutputs'
|
||||
|
||||
Vue.use(VueRouter)
|
||||
|
||||
@ -218,6 +220,16 @@ export const router = new VueRouter({
|
||||
path: '/settings/webinterface',
|
||||
name: 'Settings Webinterface',
|
||||
component: SettingsPageWebinterface
|
||||
},
|
||||
{
|
||||
path: '/settings/online-services',
|
||||
name: 'Settings Online Services',
|
||||
component: SettingsPageOnlineServices
|
||||
},
|
||||
{
|
||||
path: '/settings/remotes-outputs',
|
||||
name: 'Settings Remotes Outputs',
|
||||
component: SettingsPageRemotesOutputs
|
||||
}
|
||||
],
|
||||
scrollBehavior (to, from, savedPosition) {
|
||||
|
@ -39,7 +39,9 @@ export default new Vuex.Store({
|
||||
'count': 0,
|
||||
'items': [ ]
|
||||
},
|
||||
lastfm: {},
|
||||
spotify: {},
|
||||
pairing: {},
|
||||
|
||||
spotify_new_releases: [],
|
||||
spotify_featured_playlists: [],
|
||||
@ -121,9 +123,15 @@ export default new Vuex.Store({
|
||||
[types.UPDATE_QUEUE] (state, queue) {
|
||||
state.queue = queue
|
||||
},
|
||||
[types.UPDATE_LASTFM] (state, lastfm) {
|
||||
state.lastfm = lastfm
|
||||
},
|
||||
[types.UPDATE_SPOTIFY] (state, spotify) {
|
||||
state.spotify = spotify
|
||||
},
|
||||
[types.UPDATE_PAIRING] (state, pairing) {
|
||||
state.pairing = pairing
|
||||
},
|
||||
[types.SPOTIFY_NEW_RELEASES] (state, newReleases) {
|
||||
state.spotify_new_releases = newReleases
|
||||
},
|
||||
|
@ -7,7 +7,9 @@ export const UPDATE_LIBRARY_PODCASTS_COUNT = 'UPDATE_LIBRARY_PODCASTS_COUNT'
|
||||
export const UPDATE_OUTPUTS = 'UPDATE_OUTPUTS'
|
||||
export const UPDATE_PLAYER_STATUS = 'UPDATE_PLAYER_STATUS'
|
||||
export const UPDATE_QUEUE = 'UPDATE_QUEUE'
|
||||
export const UPDATE_LASTFM = 'UPDATE_LASTFM'
|
||||
export const UPDATE_SPOTIFY = 'UPDATE_SPOTIFY'
|
||||
export const UPDATE_PAIRING = 'UPDATE_PAIRING'
|
||||
|
||||
export const SPOTIFY_NEW_RELEASES = 'SPOTIFY_NEW_RELEASES'
|
||||
export const SPOTIFY_FEATURED_PLAYLISTS = 'SPOTIFY_FEATURED_PLAYLISTS'
|
||||
|
@ -190,6 +190,10 @@ export default {
|
||||
return axios.put('/api/outputs/' + outputId, output)
|
||||
},
|
||||
|
||||
output_toggle (outputId) {
|
||||
return axios.put('/api/outputs/' + outputId + '/toggle')
|
||||
},
|
||||
|
||||
library_artists () {
|
||||
return axios.get('/api/library/artists?media_kind=music')
|
||||
},
|
||||
@ -316,6 +320,34 @@ export default {
|
||||
return axios.get('/api/spotify')
|
||||
},
|
||||
|
||||
spotify_login (credentials) {
|
||||
return axios.post('/api/spotify-login', credentials)
|
||||
},
|
||||
|
||||
lastfm () {
|
||||
return axios.get('/api/lastfm')
|
||||
},
|
||||
|
||||
lastfm_login (credentials) {
|
||||
return axios.post('/api/lastfm-login', credentials)
|
||||
},
|
||||
|
||||
lastfm_logout (credentials) {
|
||||
return axios.get('/api/lastfm-logout')
|
||||
},
|
||||
|
||||
pairing () {
|
||||
return axios.get('/api/pairing')
|
||||
},
|
||||
|
||||
pairing_kickoff (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) {
|
||||
if (artworkUrl && artworkUrl.startsWith('/')) {
|
||||
if (artworkUrl.includes('?')) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user