[web] Refactor API calls

This commit is contained in:
Alain Nussbaumer
2025-05-04 11:36:34 +02:00
parent 3677f9d757
commit 80b9d8d648
87 changed files with 763 additions and 844 deletions

View File

@@ -32,7 +32,7 @@
"devDependencies": {
"@intlify/unplugin-vue-i18n": "^6.0.8",
"@vitejs/plugin-vue": "^5.2.3",
"eslint": "^9.25.1",
"eslint": "^9.26.0",
"eslint-config-prettier": "^10.1.2",
"eslint-plugin-vue": "^10.1.0",
"prettier": "^3.5.3",

View File

@@ -28,6 +28,9 @@ import ModalDialogUpdate from '@/components/ModalDialogUpdate.vue'
import NavbarBottom from '@/components/NavbarBottom.vue'
import NavbarTop from '@/components/NavbarTop.vue'
import ReconnectingWebSocket from 'reconnectingwebsocket'
import configuration from '@/api/configuration'
import library from '@/api/library'
import services from '@/api/services'
import { useConfigurationStore } from '@/stores/configuration'
import { useLibraryStore } from '@/stores/library'
import { useLyricsStore } from '@/stores/lyrics'
@@ -39,7 +42,6 @@ import { useRemotesStore } from './stores/remotes'
import { useServicesStore } from '@/stores/services'
import { useSettingsStore } from '@/stores/settings'
import { useUIStore } from './stores/ui'
import webapi from '@/webapi'
export default {
name: 'App',
@@ -96,8 +98,8 @@ export default {
},
methods: {
connect() {
webapi
.config()
configuration
.list()
.then((data) => {
this.configurationStore.$state = data
this.uiStore.hideSingles = data.hide_singles
@@ -133,29 +135,28 @@ export default {
})
)
this.updateOutputs()
this.updatePlayerStatus()
this.updateLibraryStats()
this.updatePlayer()
this.updateLibrary()
this.updateSettings()
this.updateQueue()
this.updateSpotify()
this.updateLastfm()
this.updatePairing()
this.updateRemotes()
}
let updateThrottled = false
const updateInfo = () => {
if (updateThrottled) {
return
}
this.updateOutputs()
this.updatePlayerStatus()
this.updateLibraryStats()
this.updatePlayer()
this.updateLibrary()
this.updateSettings()
this.updateQueue()
this.updateSpotify()
this.updateLastfm()
this.updatePairing()
this.updateRemotes()
updateThrottled = true
setTimeout(() => {
updateThrottled = false
@@ -171,34 +172,25 @@ export default {
socket.onmessage = (response) => {
const data = JSON.parse(response.data)
if (
data.notify.includes('update') ||
data.notify.includes('database')
) {
this.updateLibraryStats()
}
if (
data.notify.includes('player') ||
data.notify.includes('options') ||
data.notify.includes('volume')
) {
this.updatePlayerStatus()
}
if (data.notify.includes('outputs') || data.notify.includes('volume')) {
this.updateOutputs()
}
if (data.notify.includes('queue')) {
this.updateQueue()
}
if (data.notify.includes('spotify')) {
this.updateSpotify()
}
if (data.notify.includes('lastfm')) {
this.updateLastfm()
}
if (data.notify.includes('pairing')) {
this.updatePairing()
}
const notify = new Set(data.notify || [])
const handlers = [
{ handler: this.updateLibrary, triggers: ['update', 'database'] },
{
handler: this.updatePlayer,
triggers: ['player', 'options', 'volume']
},
{ handler: this.updateOutputs, triggers: ['outputs', 'volume'] },
{ handler: this.updateQueue, triggers: ['queue'] },
{ handler: this.updateSpotify, triggers: ['spotify'] },
{ handler: this.updateLastfm, triggers: ['lastfm'] },
{ handler: this.updateRemotes, triggers: ['pairing'] },
{ handler: this.updateLyrics, triggers: ['player', 'queue'] }
]
handlers.forEach(({ handler, triggers }) => {
if (triggers.some((key) => notify.has(key))) {
handler.call(this)
}
})
}
},
createWebsocket() {
@@ -223,22 +215,17 @@ export default {
}
},
updateLastfm() {
webapi.lastfm().then((data) => {
services.lastfm().then((data) => {
this.servicesStore.lastfm = data
})
},
updateLibraryStats() {
webapi.library_stats().then((data) => {
this.libraryStore.$state = data
})
webapi.library_count('scan_kind is rss').then((data) => {
this.libraryStore.rss = data
})
updateLibrary() {
this.libraryStore.initialise()
},
updateLyrics() {
const track = this.queueStore.current
if (track?.track_id) {
webapi.library_track(track.track_id).then((data) => {
library.track(track.track_id).then((data) => {
this.lyricsStore.lyrics = data.lyrics
})
} else {
@@ -246,34 +233,22 @@ export default {
}
},
updateOutputs() {
webapi.outputs().then((data) => {
this.outputsStore.outputs = data.outputs
})
this.outputsStore.initialise()
},
updatePairing() {
webapi.pairing().then((data) => {
this.remotesStore.$state = data
})
updateRemotes() {
this.remotesStore.initialise()
},
updatePlayerStatus() {
webapi.player_status().then((data) => {
this.playerStore.$state = data
this.updateLyrics()
})
updatePlayer() {
this.playerStore.initialise()
},
updateQueue() {
webapi.queue().then((data) => {
this.queueStore.$state = data
this.updateLyrics()
})
this.queueStore.initialise()
},
updateSettings() {
webapi.settings().then((data) => {
this.settingsStore.$state = data
})
this.settingsStore.initialise()
},
updateSpotify() {
webapi.spotify().then((data) => {
services.spotify().then((data) => {
this.servicesStore.spotify = data
if (this.timerId > 0) {
window.clearTimeout(this.timerId)

View File

@@ -0,0 +1,7 @@
import api from '@/api'
export default {
list() {
return api.get('./api/config')
}
}

24
web-src/src/api/index.js Normal file
View File

@@ -0,0 +1,24 @@
import api from 'axios'
import i18n from '@/i18n'
import { useNotificationsStore } from '@/stores/notifications'
const { t } = i18n.global
api.interceptors.response.use(
(response) => response.data,
(error) => {
if (error.request.status && error.request.responseURL) {
useNotificationsStore().add({
text: t('server.request-failed', {
cause: error.request.statusText,
status: error.request.status,
url: error.request.responseURL
}),
type: 'danger'
})
}
return Promise.reject(error)
}
)
export default api

163
web-src/src/api/library.js Normal file
View File

@@ -0,0 +1,163 @@
import api from '@/api'
export default {
add(url) {
return api.post('./api/library/add', null, { params: { url } })
},
album(id) {
return api.get(`./api/library/albums/${id}`)
},
albumTracks(id, filter = { limit: -1, offset: 0 }) {
return api.get(`./api/library/albums/${id}/tracks`, {
params: filter
})
},
albums(media_kind) {
return api.get('./api/library/albums', { params: { media_kind } })
},
artist(id) {
return api.get(`./api/library/artists/${id}`)
},
artistAlbums(id) {
return api.get(`./api/library/artists/${id}/albums`)
},
async artistTracks(artist) {
const params = {
expression: `songartistid is "${artist}"`,
type: 'tracks'
}
const data = await api.get('./api/search', { params })
return data.tracks
},
artists(media_kind) {
return api.get('./api/library/artists', { params: { media_kind } })
},
composer(composer) {
return api.get(`./api/library/composers/${encodeURIComponent(composer)}`)
},
async composerAlbums(composer) {
const params = {
expression: `composer is "${composer}" and media_kind is music`,
type: 'albums'
}
const data = await api.get('./api/search', { params })
return data.albums
},
async composerTracks(composer) {
const params = {
expression: `composer is "${composer}" and media_kind is music`,
type: 'tracks'
}
const data = await api.get('./api/search', { params })
return data.tracks
},
composers(media_kind) {
return api.get('./api/library/composers', { params: { media_kind } })
},
files(directory) {
return api.get('./api/library/files', { params: { directory } })
},
async genre(genre, mediaKind) {
const params = {
expression: `genre is "${genre}" and media_kind is ${mediaKind}`,
type: 'genres'
}
const data = await api.get('./api/search', { params })
return data.genres
},
async genreAlbums(genre, mediaKind) {
const params = {
expression: `genre is "${genre}" and media_kind is ${mediaKind}`,
type: 'albums'
}
const data = await api.get('./api/search', { params })
return data.albums
},
async genreTracks(genre, mediaKind) {
const params = {
expression: `genre is "${genre}" and media_kind is ${mediaKind}`,
type: 'tracks'
}
const data = await api.get('./api/search', { params })
return data.tracks
},
async genres(mediaKind) {
const params = {
expression: `media_kind is ${mediaKind}`,
type: 'genres'
}
const data = await api.get('./api/search', { params })
return data.genres
},
async newPodcastEpisodes() {
const params = {
expression:
'media_kind is podcast and play_count = 0 ORDER BY time_added DESC',
type: 'tracks'
}
const data = await api.get('./api/search', { params })
return data.tracks
},
playlist(id) {
return api.get(`./api/library/playlists/${id}`)
},
playlistDelete(id) {
return api.delete(`./api/library/playlists/${id}`)
},
playlistFolder(id = 0) {
return api.get(`./api/library/playlists/${id}/playlists`)
},
playlistTracks(id) {
return api.get(`./api/library/playlists/${id}/tracks`)
},
async podcastEpisodes(id) {
const params = {
expression: `media_kind is podcast and songalbumid is "${id}" ORDER BY date_released DESC`,
type: 'tracks'
}
const data = await api.get('./api/search', { params })
return data.tracks
},
async radioStreams() {
const params = {
expression: 'data_kind is url and song_length = 0',
media_kind: 'music',
type: 'tracks'
}
const data = await api.get('./api/search', { params })
return data.tracks
},
rescan(scan_kind) {
return api.put('./api/rescan', null, { params: { scan_kind } })
},
rssCount() {
return api.get('./api/library/count', {
params: { expression: 'scan_kind is rss' }
})
},
search(params) {
return api.get('./api/search', { params })
},
state() {
return api.get('./api/library')
},
track(id) {
return api.get(`./api/library/tracks/${id}`)
},
trackPlaylists(id) {
return api.get(`./api/library/tracks/${id}/playlists`)
},
update(scan_kind) {
return api.put('./api/update', null, { params: { scan_kind } })
},
updateAlbum(id, attributes) {
return api.put(`./api/library/albums/${id}/tracks`, null, {
params: attributes
})
},
updateTrack(id, attributes = {}) {
return api.put(`./api/library/tracks/${id}`, null, {
params: attributes
})
}
}

View File

@@ -0,0 +1,13 @@
import api from '@/api'
export default {
toggle(id) {
return api.put(`./api/outputs/${id}/toggle`)
},
state() {
return api.get('./api/outputs')
},
update(id, output) {
return api.put(`./api/outputs/${id}`, output)
}
}

45
web-src/src/api/player.js Normal file
View File

@@ -0,0 +1,45 @@
import api from '@/api'
export default {
consume(state) {
return api.put(`./api/player/consume?state=${state}`)
},
next() {
return api.put('./api/player/next')
},
outputVolume(outputId, outputVolume) {
return api.put(
`./api/player/volume?volume=${outputVolume}&output_id=${outputId}`
)
},
pause() {
return api.put('./api/player/pause')
},
play(params = {}) {
return api.put('./api/player/play', null, { params })
},
previous() {
return api.put('./api/player/previous')
},
repeat(mode) {
return api.put(`./api/player/repeat?state=${mode}`)
},
seek(seekMs) {
return api.put(`./api/player/seek?seek_ms=${seekMs}`)
},
seekToPosition(position) {
return api.put(`./api/player/seek?position_ms=${position}`)
},
shuffle(state) {
return api.put(`./api/player/shuffle?state=${state}`)
},
state() {
return api.get('./api/player')
},
stop() {
return api.put('./api/player/stop')
},
volume(volume) {
return api.put(`./api/player/volume?volume=${volume}`)
}
}

73
web-src/src/api/queue.js Normal file
View File

@@ -0,0 +1,73 @@
import api from '@/api'
import i18n from '@/i18n'
import { useNotificationsStore } from '@/stores/notifications'
import { useQueueStore } from '@/stores/queue'
const { t } = i18n.global
export default {
state() {
return api.get('./api/queue')
},
addUri(uris, next = false) {
return this.addToQueue({ uris }, next)
},
addExpression(expression, next = false) {
return this.addToQueue({ expression }, next)
},
async addToQueue(params, next = false) {
if (next) {
const { current } = useQueueStore()
if (current?.id) {
params.position = current.position + 1
}
}
const data = await api.post('./api/queue/items/add', null, { params })
useNotificationsStore().add({
text: t('server.appended-tracks', { count: data.count }),
timeout: 2000,
type: 'info'
})
return data
},
clear() {
return api.put('./api/queue/clear')
},
move(id, position) {
return api.put(`./api/queue/items/${id}?new_position=${position}`)
},
playExpression(expression, shuffle, position) {
const params = {
clear: 'true',
expression,
playback: 'start',
playback_from_position: position,
shuffle
}
return api.post('./api/queue/items/add', null, { params })
},
playUri(uris, shuffle, position) {
const params = {
clear: 'true',
playback: 'start',
playback_from_position: position,
shuffle,
uris
}
return api.post('./api/queue/items/add', null, { params })
},
remove(id) {
return api.delete(`./api/queue/items/${id}`)
},
async saveToPlaylist(name) {
const response = await api.post('./api/queue/save', null, {
params: { name }
})
useNotificationsStore().add({
text: t('server.queue-saved', { name }),
timeout: 2000,
type: 'info'
})
return await Promise.resolve(response)
}
}

View File

@@ -0,0 +1,10 @@
import api from '@/api'
export default {
pair(pin) {
return api.post('./api/pairing', { pin })
},
state() {
return api.get('./api/pairing')
}
}

View File

@@ -0,0 +1,19 @@
import api from '@/api'
export default {
lastfm() {
return api.get('./api/lastfm')
},
loginLastfm(credentials) {
return api.post('./api/lastfm-login', credentials)
},
logoutLastfm() {
return api.get('./api/lastfm-logout')
},
logoutSpotify() {
return api.get('./api/spotify-logout')
},
spotify() {
return api.get('./api/spotify')
}
}

View File

@@ -0,0 +1,10 @@
import api from '@/api'
export default {
state() {
return api.get('./api/settings')
},
update(categoryName, option) {
return api.put(`./api/settings/${categoryName}/${option.name}`, option)
}
}

View File

@@ -18,8 +18,8 @@
<script>
import ControlSlider from '@/components/ControlSlider.vue'
import player from '@/api/player'
import { usePlayerStore } from '@/stores/player'
import webapi from '@/webapi'
export default {
name: 'ControlVolume',
@@ -48,7 +48,7 @@ export default {
},
methods: {
changeVolume() {
webapi.player_volume(this.player.volume)
player.volume(this.player.volume)
},
toggle() {
this.player.volume = this.player.volume > 0 ? 0 : this.volume

View File

@@ -27,7 +27,8 @@
<script>
import ControlSlider from '@/components/ControlSlider.vue'
import webapi from '@/webapi'
import outputs from '@/api/outputs'
import player from '@/api/player'
export default {
name: 'ControlOutputVolume',
@@ -63,13 +64,10 @@ export default {
methods: {
changeVolume() {
webapi.player_output_volume(this.output.id, this.volume)
player.outputVolume(this.output.id, this.volume)
},
toggle() {
const values = {
selected: !this.output.selected
}
webapi.output_update(this.output.id, values)
outputs.update(this.output.id, { selected: !this.output.selected })
}
}
}

View File

@@ -9,9 +9,9 @@
</template>
<script>
import player from '@/api/player'
import { usePlayerStore } from '@/stores/player'
import { useQueueStore } from '@/stores/queue'
import webapi from '@/webapi'
export default {
name: 'ControlPlayerBack',
@@ -40,9 +40,7 @@ export default {
},
methods: {
seek() {
if (!this.disabled) {
webapi.player_seek(this.offset * -1)
}
player.seek(this.offset * -1)
}
}
}

View File

@@ -1,17 +1,17 @@
<template>
<a :class="{ 'is-dark': playerStore.consume }" @click="toggle">
<button :class="{ 'is-dark': playerStore.consume }" @click="toggle">
<mdicon
class="icon"
name="fire"
size="16"
:title="$t('player.button.consume')"
/>
</a>
</button>
</template>
<script>
import player from '@/api/player'
import { usePlayerStore } from '@/stores/player'
import webapi from '@/webapi'
export default {
name: 'ControlPlayerConsume',
@@ -22,7 +22,7 @@ export default {
},
methods: {
toggle() {
webapi.player_consume(!this.playerStore.consume)
player.consume(!this.playerStore.consume)
}
}
}

View File

@@ -9,9 +9,9 @@
</template>
<script>
import player from '@/api/player'
import { usePlayerStore } from '@/stores/player'
import { useQueueStore } from '@/stores/queue'
import webapi from '@/webapi'
export default {
name: 'ControlPlayerForward',
@@ -40,9 +40,7 @@ export default {
},
methods: {
seek() {
if (!this.disabled) {
webapi.player_seek(this.offset)
}
player.seek(this.offset)
}
}
}

View File

@@ -1,12 +1,15 @@
<template>
<a :class="{ 'is-dark': lyricsStore.active }" @click="lyricsStore.toggle">
<button
:class="{ 'is-dark': lyricsStore.active }"
@click="lyricsStore.toggle"
>
<mdicon
class="icon"
:name="icon"
:size="16"
:title="$t('player.button.toggle-lyrics')"
/>
</a>
</button>
</template>
<script>

View File

@@ -9,8 +9,8 @@
</template>
<script>
import player from '@/api/player'
import { useQueueStore } from '@/stores/queue'
import webapi from '@/webapi'
export default {
name: 'ControlPlayerNext',
@@ -24,10 +24,7 @@ export default {
},
methods: {
next() {
if (this.disabled) {
return
}
webapi.player_next()
player.next()
}
}
}

View File

@@ -5,16 +5,14 @@
</template>
<script>
import { useNotificationsStore } from '@/stores/notifications'
import player from '@/api/player'
import { usePlayerStore } from '@/stores/player'
import { useQueueStore } from '@/stores/queue'
import webapi from '@/webapi'
export default {
name: 'ControlPlayerPlay',
setup() {
return {
notificationsStore: useNotificationsStore(),
playerStore: usePlayerStore(),
queueStore: useQueueStore()
}
@@ -34,24 +32,15 @@ export default {
},
methods: {
toggle() {
if (this.disabled) {
this.notificationsStore.add({
text: this.$t('server.empty-queue'),
timeout: 2000,
topic: 'connection',
type: 'info'
})
return
}
if (this.playerStore.isPlaying && this.queueStore.isPauseAllowed) {
webapi.player_pause()
player.pause()
} else if (
this.playerStore.isPlaying &&
!this.queueStore.isPauseAllowed
) {
webapi.player_stop()
player.stop()
} else {
webapi.player_play()
player.play()
}
}
}

View File

@@ -1,5 +1,5 @@
<template>
<a :disabled="disabled" @click="playPrevious">
<a :disabled="disabled" @click="previous">
<mdicon
class="icon"
name="skip-backward"
@@ -9,8 +9,8 @@
</template>
<script>
import player from '@/api/player'
import { useQueueStore } from '@/stores/queue'
import webapi from '@/webapi'
export default {
name: 'ControlPlayerPrevious',
@@ -23,11 +23,8 @@ export default {
}
},
methods: {
playPrevious() {
if (this.disabled) {
return
}
webapi.player_previous()
previous() {
player.previous()
}
}
}

View File

@@ -1,17 +1,17 @@
<template>
<a :class="{ 'is-dark': !playerStore.isRepeatOff }" @click="toggle">
<button :class="{ 'is-dark': !playerStore.isRepeatOff }" @click="toggle">
<mdicon
class="icon"
:name="icon"
:size="16"
:title="$t(`player.button.${icon}`)"
/>
</a>
</button>
</template>
<script>
import player from '@/api/player'
import { usePlayerStore } from '@/stores/player'
import webapi from '@/webapi'
export default {
name: 'ControlPlayerRepeat',
@@ -31,11 +31,11 @@ export default {
methods: {
toggle() {
if (this.playerStore.isRepeatAll) {
webapi.player_repeat('single')
player.repeat('single')
} else if (this.playerStore.isRepeatSingle) {
webapi.player_repeat('off')
player.repeat('off')
} else {
webapi.player_repeat('all')
player.repeat('all')
}
}
}

View File

@@ -1,17 +1,17 @@
<template>
<a :class="{ 'is-dark': playerStore.shuffle }" @click="toggle">
<button :class="{ 'is-dark': playerStore.shuffle }" @click="toggle">
<mdicon
class="icon"
:name="icon"
:size="16"
:title="$t(`player.button.${icon}`)"
/>
</a>
</button>
</template>
<script>
import player from '@/api/player'
import { usePlayerStore } from '@/stores/player'
import webapi from '@/webapi'
export default {
name: 'ControlPlayerShuffle',
@@ -28,7 +28,7 @@ export default {
},
methods: {
toggle() {
webapi.player_shuffle(!this.playerStore.shuffle)
player.shuffle(!this.playerStore.shuffle)
}
}
}

View File

@@ -19,8 +19,8 @@
</template>
<script>
import settings from '@/api/settings'
import { useSettingsStore } from '@/stores/settings'
import webapi from '@/webapi'
export default {
name: 'ControlSetting',
@@ -60,8 +60,8 @@ export default {
name: this.name,
value
}
webapi
.settings_update(this.category, setting)
settings
.update(this.category, setting)
.then(() => {
window.clearTimeout(this.timerId)
this.settingsStore.update(setting)

View File

@@ -9,9 +9,7 @@
<nav class="breadcrumb">
<ul>
<li v-for="directory in directories" :key="directory.index">
<a @click="open(directory)">
<span v-text="directory.name" />
</a>
<a @click="open(directory)" v-text="directory.name" />
</li>
</ul>
</nav>

View File

@@ -46,8 +46,8 @@
</template>
<script>
import player from '@/api/player'
import { usePlayerStore } from '@/stores/player'
import webapi from '@/webapi'
export default {
name: 'ListItemQueueItem',
@@ -71,7 +71,7 @@ export default {
},
methods: {
play() {
webapi.player_play({ item_id: this.item.id })
player.play({ item_id: this.item.id })
}
}
}

View File

@@ -22,7 +22,7 @@
<script>
import ListItem from '@/components/ListItem.vue'
import ModalDialogTrack from '@/components/ModalDialogTrack.vue'
import webapi from '@/webapi'
import queue from '@/api/queue'
export default {
name: 'ListTracks',
@@ -44,15 +44,15 @@ export default {
},
open(item) {
if (this.uris) {
webapi.player_play_uri(this.uris, false, this.items.items.indexOf(item))
queue.playUri(this.uris, false, this.items.items.indexOf(item))
} else if (this.expression) {
webapi.player_play_expression(
queue.playExpression(
this.expression,
false,
this.items.items.indexOf(item)
)
} else {
webapi.player_play_uri(item.uri, false)
queue.playUri(item.uri, false)
}
},
openDetails(item) {

View File

@@ -31,7 +31,7 @@
import ListItem from '@/components/ListItem.vue'
import LoaderListItem from '@/components/LoaderListItem.vue'
import ModalDialogTrackSpotify from '@/components/ModalDialogTrackSpotify.vue'
import webapi from '@/webapi'
import queue from '@/api/queue'
export default {
name: 'ListTracksSpotify',
@@ -48,11 +48,7 @@ export default {
methods: {
open(item) {
if (item.is_playable) {
webapi.player_play_uri(
this.contextUri || item.uri,
false,
item.position || 0
)
queue.playUri(this.contextUri || item.uri, false, item.position || 0)
}
},
openDetails(item) {

View File

@@ -22,7 +22,7 @@
<script>
import ControlUrlField from '@/components/ControlUrlField.vue'
import ModalDialog from '@/components/ModalDialog.vue'
import webapi from '@/webapi'
import library from '@/api/library'
export default {
name: 'ModalDialogAddRss',
@@ -55,8 +55,8 @@ export default {
methods: {
add() {
this.loading = true
webapi
.library_add(this.url)
library
.add(this.url)
.then(() => {
this.$emit('podcast-added')
this.$emit('close')

View File

@@ -21,7 +21,7 @@
<script>
import ControlUrlField from '@/components/ControlUrlField.vue'
import ModalDialog from '@/components/ModalDialog.vue'
import webapi from '@/webapi'
import queue from '@/api/queue'
export default {
name: 'ModalDialogAddStream',
@@ -60,8 +60,8 @@ export default {
methods: {
add() {
this.loading = true
webapi
.queue_add(this.url)
queue
.add(this.url)
.then(() => {
this.$emit('close')
})
@@ -79,8 +79,8 @@ export default {
},
play() {
this.loading = true
webapi
.player_play_uri(this.url, false)
queue
.playUri(this.url, false)
.then(() => {
this.$emit('close')
this.url = ''

View File

@@ -28,7 +28,7 @@
<script>
import ModalDialog from '@/components/ModalDialog.vue'
import ModalDialogPlayable from '@/components/ModalDialogPlayable.vue'
import webapi from '@/webapi'
import library from '@/api/library'
export default {
name: 'ModalDialogAlbum',
@@ -60,7 +60,7 @@ export default {
]
},
buttons() {
if (this.media_kind_resolved === 'podcast') {
if (this.mediaKindResolved === 'podcast') {
if (this.item.data_kind === 'url') {
return [
{ handler: this.markAsPlayed, key: 'actions.mark-as-played' },
@@ -74,7 +74,7 @@ export default {
}
return []
},
media_kind_resolved() {
mediaKindResolved() {
return this.mediaKind || this.item.media_kind
},
playable() {
@@ -115,16 +115,14 @@ export default {
this.showRemovePodcastModal = false
},
markAsPlayed() {
webapi
.library_album_track_update(this.item.id, { play_count: 'played' })
.then(() => {
this.$emit('play-count-changed')
this.$emit('close')
})
library.updateAlbum(this.item.id, { play_count: 'played' }).then(() => {
this.$emit('play-count-changed')
this.$emit('close')
})
},
openArtist() {
this.$emit('close')
if (this.media_kind_resolved === 'audiobook') {
if (this.mediaKindResolved === 'audiobook') {
this.$router.push({
name: 'audiobooks-artist',
params: { id: this.item.artist_id }
@@ -142,10 +140,10 @@ export default {
},
removePodcast() {
this.showRemovePodcastModal = false
webapi.library_album_tracks(this.item.id, { limit: 1 }).then((album) => {
webapi.library_track_playlists(album.items[0].id).then((data) => {
library.albumTracks(this.item.id, { limit: 1 }).then((album) => {
library.trackPlaylists(album.items[0].id).then((data) => {
const { id } = data.items.find((item) => item.type === 'rss')
webapi.library_playlist_delete(id).then(() => {
library.playlistDelete(id).then(() => {
this.$emit('podcast-deleted')
this.$emit('close')
})

View File

@@ -25,7 +25,7 @@
import ControlButton from '@/components/ControlButton.vue'
import ListProperties from '@/components/ListProperties.vue'
import ModalDialog from '@/components/ModalDialog.vue'
import webapi from '@/webapi'
import queue from '@/api/queue'
export default {
name: 'ModalDialogPlayable',
@@ -53,25 +53,25 @@ export default {
play() {
this.$emit('close')
if (this.item.expression) {
webapi.player_play_expression(this.item.expression, false)
queue.playExpression(this.item.expression, false)
} else {
webapi.player_play_uri(this.item.uris || this.item.uri, false)
queue.playUri(this.item.uris || this.item.uri, false)
}
},
addToQueue() {
this.$emit('close')
if (this.item.expression) {
webapi.queue_expression_add(this.item.expression)
queue.addExpression(this.item.expression)
} else {
webapi.queue_add(this.item.uris || this.item.uri)
queue.addUri(this.item.uris || this.item.uri)
}
},
addNextToQueue() {
this.$emit('close')
if (this.item.expression) {
webapi.queue_expression_add_next(this.item.expression)
queue.addExpression(this.item.expression, true)
} else {
webapi.queue_add_next(this.item.uris || this.item.uri)
queue.addUri(this.item.uris || this.item.uri, true)
}
}
}

View File

@@ -30,7 +30,7 @@
<script>
import ModalDialog from '@/components/ModalDialog.vue'
import webapi from '@/webapi'
import queue from '@/api/queue'
export default {
name: 'ModalDialogPlaylistSave',
@@ -81,8 +81,8 @@ export default {
},
save() {
this.loading = true
webapi
.queue_save_playlist(this.playlistName)
queue
.saveToPlaylist(this.playlistName)
.then(() => {
this.$emit('close')
this.playlistName = ''

View File

@@ -15,8 +15,9 @@
import ListProperties from '@/components/ListProperties.vue'
import ModalDialog from '@/components/ModalDialog.vue'
import SpotifyWebApi from 'spotify-web-api-js'
import player from '@/api/player'
import queue from '@/api/queue'
import { useServicesStore } from '@/stores/services'
import webapi from '@/webapi'
export default {
name: 'ModalDialogQueueItem',
@@ -158,11 +159,11 @@ export default {
},
play() {
this.$emit('close')
webapi.player_play({ item_id: this.item.id })
player.play({ item_id: this.item.id })
},
remove() {
this.$emit('close')
webapi.queue_remove(this.item.id)
queue.remove(this.item.id)
}
}
}

View File

@@ -20,8 +20,8 @@
<script>
import ControlPinField from '@/components/ControlPinField.vue'
import ModalDialog from '@/components/ModalDialog.vue'
import remotes from '@/api/remotes'
import { useRemotesStore } from '@/stores/remotes'
import webapi from '@/webapi'
export default {
name: 'ModalDialogRemotePairing',
@@ -59,7 +59,7 @@ export default {
this.disabled = disabled
},
pair() {
webapi.pairing_kickoff({ pin: this.pin }).then(() => {
remotes.pair(this.pin).then(() => {
this.pin = ''
})
}

View File

@@ -9,7 +9,7 @@
<script>
import ModalDialogPlayable from '@/components/ModalDialogPlayable.vue'
import webapi from '@/webapi'
import library from '@/api/library'
export default {
name: 'ModalDialogTrack',
@@ -92,16 +92,14 @@ export default {
},
methods: {
markAsNew() {
webapi
.library_track_update(this.item.id, { play_count: 'reset' })
.then(() => {
this.$emit('play-count-changed')
this.$emit('close')
})
library.updateTrack(this.item.id, { play_count: 'reset' }).then(() => {
this.$emit('play-count-changed')
this.$emit('close')
})
},
markAsPlayed() {
webapi
.library_track_update(this.item.id, { play_count: 'increment' })
library
.updateTrack(this.item.id, { play_count: 'increment' })
.then(() => {
this.$emit('play-count-changed')
this.$emit('close')

View File

@@ -7,10 +7,7 @@
>
<template #content>
<div v-if="!libraryStore.updating">
<div
v-if="servicesStore.isSpotifyActive || rss.tracks > 0"
class="field"
>
<div v-if="servicesStore.isSpotifyActive" class="field">
<label class="label" v-text="$t('dialog.update.info')" />
<div class="control">
<div class="select is-small">
@@ -22,11 +19,7 @@
value="spotify"
v-text="$t('dialog.update.spotify')"
/>
<option
v-if="rss.tracks > 0"
value="rss"
v-text="$t('dialog.update.feeds')"
/>
<option value="rss" v-text="$t('dialog.update.feeds')" />
</select>
</div>
</div>
@@ -47,9 +40,9 @@
<script>
import ControlSwitch from '@/components/ControlSwitch.vue'
import ModalDialog from '@/components/ModalDialog.vue'
import library from '@/api/library'
import { useLibraryStore } from '@/stores/library'
import { useServicesStore } from '@/stores/services'
import webapi from '@/webapi'
export default {
name: 'ModalDialogUpdate',
@@ -80,17 +73,14 @@ export default {
})
}
return actions
},
rss() {
return this.libraryStore.rss
}
},
methods: {
analyse() {
if (this.rescanMetadata) {
webapi.library_rescan(this.libraryStore.update_dialog_scan_kind)
library.rescan(this.libraryStore.update_dialog_scan_kind)
} else {
webapi.library_update(this.libraryStore.update_dialog_scan_kind)
library.update(this.libraryStore.update_dialog_scan_kind)
}
},
cancel() {

View File

@@ -383,8 +383,7 @@
"connection-failed": "Fehler bei Verbindung zum OwnTone-Server",
"request-failed": "Anfrage gescheitert (Status: {status} {cause} {url})",
"queue-saved": "Warteschlange zu Playlist {name} gesichert",
"appended-tracks": "{count} Track an die Abspielliste angehängt|{count} Tracks an die Abspielliste angehängt",
"empty-queue": "Warteschlange ist leer"
"appended-tracks": "{count} Track an die Abspielliste angehängt|{count} Tracks an die Abspielliste angehängt"
},
"grouped-list": {
"today": "Heute",

View File

@@ -383,8 +383,7 @@
"connection-failed": "Failed to connect to OwnTone server",
"request-failed": "Request failed (status: {status} {cause} {url})",
"queue-saved": "Queue saved to playlist {name}",
"appended-tracks": "{count} track appended to the queue|{count} tracks appended to the queue",
"empty-queue": "Queue is empty"
"appended-tracks": "{count} track appended to the queue|{count} tracks appended to the queue"
},
"grouped-list": {
"today": "Today",

View File

@@ -383,8 +383,7 @@
"connection-failed": "Échec de connexion au serveur",
"request-failed": "La requête a échoué (status: {status} {cause} {url})",
"queue-saved": "La file dattente enregistrée dans la liste de lecture {name}",
"appended-tracks": "{count} piste ajoutée à la file dattente|{count} pistes ajoutées à la file dattente",
"empty-queue": "La file dattente est vide"
"appended-tracks": "{count} piste ajoutée à la file dattente|{count} pistes ajoutées à la file dattente"
},
"grouped-list": {
"today": "Aujourdhui",

View File

@@ -383,8 +383,7 @@
"connection-failed": "无法连接到 OwnTone 服务器",
"request-failed": "请求失败 (状态:{status} {cause} {url})",
"queue-saved": "清单以添加到播放列表 {name}",
"appended-tracks": "已附加到队列的 {count} 只曲目|已附加到队列的 {count} 只曲目",
"empty-queue": "清单是空的"
"appended-tracks": "已附加到队列的 {count} 只曲目|已附加到队列的 {count} 只曲目"
},
"grouped-list": {
"today": "今日",

View File

@@ -383,8 +383,7 @@
"connection-failed": "無法連接到 OwnTone 伺服器",
"request-failed": "請求失敗 (狀態:{status} {cause} {url})",
"queue-saved": "清單以新增到播放列表 {name}",
"appended-tracks": "已附加到隊列的 {count} 首曲目|已附加到隊列的 {count} 首曲目",
"empty-queue": "清單是空的"
"appended-tracks": "已附加到隊列的 {count} 首曲目|已附加到隊列的 {count} 首曲目"
},
"grouped-list": {
"today": "今日",

View File

@@ -29,7 +29,8 @@ import { GroupedList } from '@/lib/GroupedList'
import HeadingHero from '@/components/HeadingHero.vue'
import ListTracks from '@/components/ListTracks.vue'
import ModalDialogAlbum from '@/components/ModalDialogAlbum.vue'
import webapi from '@/webapi'
import library from '@/api/library'
import queue from '@/api/queue'
export default {
name: 'PageAlbum',
@@ -42,8 +43,8 @@ export default {
},
beforeRouteEnter(to, from, next) {
Promise.all([
webapi.library_album(to.params.id),
webapi.library_album_tracks(to.params.id)
library.album(to.params.id),
library.albumTracks(to.params.id)
]).then(([album, tracks]) => {
next((vm) => {
vm.album = album
@@ -90,7 +91,7 @@ export default {
this.showDetailsModal = true
},
play() {
webapi.player_play_uri(this.album.uri, true)
queue.playUri(this.album.uri, true)
}
}
}

View File

@@ -29,8 +29,9 @@ import HeadingHero from '@/components/HeadingHero.vue'
import ListTracksSpotify from '@/components/ListTracksSpotify.vue'
import ModalDialogAlbumSpotify from '@/components/ModalDialogAlbumSpotify.vue'
import SpotifyWebApi from 'spotify-web-api-js'
import queue from '@/api/queue'
import services from '@/api/services'
import { useServicesStore } from '@/stores/services'
import webapi from '@/webapi'
export default {
name: 'PageAlbumSpotify',
@@ -43,7 +44,7 @@ export default {
},
beforeRouteEnter(to, from, next) {
const spotifyApi = new SpotifyWebApi()
webapi.spotify().then((data) => {
services.spotify().then((data) => {
spotifyApi.setAccessToken(data.webapi_token)
spotifyApi
.getAlbum(to.params.id, {
@@ -98,7 +99,7 @@ export default {
},
play() {
this.showDetailsModal = false
webapi.player_play_uri(this.album.uri, true)
queue.playUri(this.album.uri, true)
}
}
}

View File

@@ -52,9 +52,9 @@ import ListAlbums from '@/components/ListAlbums.vue'
import ListIndexButtons from '@/components/ListIndexButtons.vue'
import ListOptions from '@/components/ListOptions.vue'
import TabsMusic from '@/components/TabsMusic.vue'
import library from '@/api/library'
import { useServicesStore } from '@/stores/services'
import { useUIStore } from '@/stores/ui'
import webapi from '@/webapi'
export default {
name: 'PageAlbums',
@@ -69,7 +69,7 @@ export default {
TabsMusic
},
beforeRouteEnter(to, from, next) {
webapi.library_albums('music').then((albums) => {
library.albums('music').then((albums) => {
next((vm) => {
vm.albumList = new GroupedList(albums)
})

View File

@@ -55,9 +55,10 @@ import HeadingTitle from '@/components/HeadingTitle.vue'
import ListAlbums from '@/components/ListAlbums.vue'
import ListOptions from '@/components/ListOptions.vue'
import ModalDialogArtist from '@/components/ModalDialogArtist.vue'
import library from '@/api/library'
import queue from '@/api/queue'
import { useServicesStore } from '@/stores/services'
import { useUIStore } from '@/stores/ui'
import webapi from '@/webapi'
export default {
name: 'PageArtist',
@@ -73,8 +74,8 @@ export default {
},
beforeRouteEnter(to, from, next) {
Promise.all([
webapi.library_artist(to.params.id),
webapi.library_artist_albums(to.params.id)
library.artist(to.params.id),
library.artistAlbums(to.params.id)
]).then(([artist, albums]) => {
next((vm) => {
vm.artist = artist
@@ -148,10 +149,7 @@ export default {
})
},
play() {
webapi.player_play_uri(
this.albums.items.map((item) => item.uri).join(),
true
)
queue.playUri(this.albums.items.map((item) => item.uri).join(), true)
}
}
}

View File

@@ -29,8 +29,9 @@ import HeadingTitle from '@/components/HeadingTitle.vue'
import ListAlbumsSpotify from '@/components/ListAlbumsSpotify.vue'
import ModalDialogArtistSpotify from '@/components/ModalDialogArtistSpotify.vue'
import SpotifyWebApi from 'spotify-web-api-js'
import queue from '@/api/queue'
import services from '@/api/services'
import { useServicesStore } from '@/stores/services'
import webapi from '@/webapi'
const PAGE_SIZE = 50
@@ -44,7 +45,7 @@ export default {
ModalDialogArtistSpotify
},
beforeRouteEnter(to, from, next) {
webapi.spotify().then((data) => {
services.spotify().then((data) => {
const spotifyApi = new SpotifyWebApi()
spotifyApi.setAccessToken(data.webapi_token)
Promise.all([
@@ -95,7 +96,7 @@ export default {
this.offset += data.limit
},
load({ loaded }) {
webapi.spotify().then((data) => {
api.spotify().then((data) => {
const spotifyApi = new SpotifyWebApi()
spotifyApi.setAccessToken(data.webapi_token)
spotifyApi
@@ -115,7 +116,7 @@ export default {
},
play() {
this.showDetailsModal = false
webapi.player_play_uri(this.artist.uri, true)
queue.playUri(this.artist.uri, true)
}
}
}

View File

@@ -57,9 +57,10 @@ import ListIndexButtons from '@/components/ListIndexButtons.vue'
import ListOptions from '@/components/ListOptions.vue'
import ListTracks from '@/components/ListTracks.vue'
import ModalDialogArtist from '@/components/ModalDialogArtist.vue'
import library from '@/api/library'
import queue from '@/api/queue'
import { useServicesStore } from '@/stores/services'
import { useUIStore } from '@/stores/ui'
import webapi from '@/webapi'
export default {
name: 'PageArtistTracks',
@@ -76,8 +77,8 @@ export default {
},
beforeRouteEnter(to, from, next) {
Promise.all([
webapi.library_artist(to.params.id),
webapi.library_artist_tracks(to.params.id)
library.artist(to.params.id),
library.artistTracks(to.params.id)
]).then(([artist, tracks]) => {
next((vm) => {
vm.artist = artist
@@ -158,10 +159,7 @@ export default {
this.showDetailsModal = true
},
play() {
webapi.player_play_uri(
this.trackList.items.map((item) => item.uri).join(),
true
)
queue.playUri(this.trackList.items.map((item) => item.uri).join(), true)
}
}
}

View File

@@ -52,9 +52,9 @@ import ListArtists from '@/components/ListArtists.vue'
import ListIndexButtons from '@/components/ListIndexButtons.vue'
import ListOptions from '@/components/ListOptions.vue'
import TabsMusic from '@/components/TabsMusic.vue'
import library from '@/api/library'
import { useServicesStore } from '@/stores/services'
import { useUIStore } from '@/stores/ui'
import webapi from '@/webapi'
export default {
name: 'PageArtists',
@@ -69,7 +69,7 @@ export default {
TabsMusic
},
beforeRouteEnter(to, from, next) {
webapi.library_artists('music').then((artists) => {
library.artists('music').then((artists) => {
next((vm) => {
vm.artistList = new GroupedList(artists)
})

View File

@@ -30,7 +30,8 @@ import { GroupedList } from '@/lib/GroupedList'
import HeadingHero from '@/components/HeadingHero.vue'
import ListTracks from '@/components/ListTracks.vue'
import ModalDialogAlbum from '@/components/ModalDialogAlbum.vue'
import webapi from '@/webapi'
import library from '@/api/library'
import queue from '@/api/queue'
export default {
name: 'PageAudiobooksAlbum',
@@ -43,8 +44,8 @@ export default {
},
beforeRouteEnter(to, from, next) {
Promise.all([
webapi.library_album(to.params.id),
webapi.library_album_tracks(to.params.id)
library.album(to.params.id),
library.albumTracks(to.params.id)
]).then(([album, tracks]) => {
next((vm) => {
vm.album = album
@@ -85,7 +86,7 @@ export default {
this.showDetailsModal = true
},
play() {
webapi.player_play_uri(this.album.uri, false)
queue.playUri(this.album.uri, false)
}
}
}

View File

@@ -20,7 +20,7 @@ import HeadingTitle from '@/components/HeadingTitle.vue'
import ListAlbums from '@/components/ListAlbums.vue'
import ListIndexButtons from '@/components/ListIndexButtons.vue'
import TabsAudiobooks from '@/components/TabsAudiobooks.vue'
import webapi from '@/webapi'
import library from '@/api/library'
export default {
name: 'PageAudiobooksAlbums',
@@ -32,7 +32,7 @@ export default {
TabsAudiobooks
},
beforeRouteEnter(to, from, next) {
webapi.library_albums('audiobook').then((albums) => {
library.albums('audiobook').then((albums) => {
next((vm) => {
vm.albums = new GroupedList(albums, {
index: { field: 'name_sort', type: String }

View File

@@ -29,7 +29,8 @@ import { GroupedList } from '@/lib/GroupedList'
import HeadingTitle from '@/components/HeadingTitle.vue'
import ListAlbums from '@/components/ListAlbums.vue'
import ModalDialogArtist from '@/components/ModalDialogArtist.vue'
import webapi from '@/webapi'
import library from '@/api/library'
import queue from '@/api/queue'
export default {
name: 'PageAudiobooksArtist',
@@ -42,8 +43,8 @@ export default {
},
beforeRouteEnter(to, from, next) {
Promise.all([
webapi.library_artist(to.params.id),
webapi.library_artist_albums(to.params.id)
library.artist(to.params.id),
library.artistAlbums(to.params.id)
]).then(([artist, albums]) => {
next((vm) => {
vm.artist = artist
@@ -76,10 +77,7 @@ export default {
this.showDetailsModal = true
},
play() {
webapi.player_play_uri(
this.albums.items.map((item) => item.uri).join(),
false
)
queue.playUri(this.albums.items.map((item) => item.uri).join(), false)
}
}
}

View File

@@ -20,7 +20,7 @@ import HeadingTitle from '@/components/HeadingTitle.vue'
import ListArtists from '@/components/ListArtists.vue'
import ListIndexButtons from '@/components/ListIndexButtons.vue'
import TabsAudiobooks from '@/components/TabsAudiobooks.vue'
import webapi from '@/webapi'
import library from '@/api/library'
export default {
name: 'PageAudiobooksArtists',
@@ -32,7 +32,7 @@ export default {
TabsAudiobooks
},
beforeRouteEnter(to, from, next) {
webapi.library_artists('audiobook').then((artists) => {
library.artists('audiobook').then((artists) => {
next((vm) => {
vm.artists = new GroupedList(artists, {
index: { field: 'name_sort', type: String }

View File

@@ -20,7 +20,7 @@ import HeadingTitle from '@/components/HeadingTitle.vue'
import ListGenres from '@/components/ListGenres.vue'
import ListIndexButtons from '@/components/ListIndexButtons.vue'
import TabsAudiobooks from '@/components/TabsAudiobooks.vue'
import webapi from '@/webapi'
import library from '@/api/library'
export default {
name: 'PageAudiobooksGenres',
@@ -32,7 +32,7 @@ export default {
TabsAudiobooks
},
beforeRouteEnter(to, from, next) {
webapi.library_genres('audiobook').then((genres) => {
library.genres('audiobook').then((genres) => {
next((vm) => {
vm.genres = new GroupedList(genres, {
index: { field: 'name_sort', type: String }

View File

@@ -29,7 +29,8 @@ import { GroupedList } from '@/lib/GroupedList'
import HeadingTitle from '@/components/HeadingTitle.vue'
import ListAlbums from '@/components/ListAlbums.vue'
import ModalDialogComposer from '@/components/ModalDialogComposer.vue'
import webapi from '@/webapi'
import library from '@/api/library'
import queue from '@/api/queue'
export default {
name: 'PageComposerAlbums',
@@ -42,8 +43,8 @@ export default {
},
beforeRouteEnter(to, from, next) {
Promise.all([
webapi.library_composer(to.params.name),
webapi.library_composer_albums(to.params.name)
library.composer(to.params.name),
library.composerAlbums(to.params.name)
]).then(([composer, albums]) => {
next((vm) => {
vm.composer = composer
@@ -90,7 +91,7 @@ export default {
})
},
play() {
webapi.player_play_expression(this.expression, true)
queue.playExpression(this.expression, true)
}
}
}

View File

@@ -43,8 +43,9 @@ import ListIndexButtons from '@/components/ListIndexButtons.vue'
import ListOptions from '@/components/ListOptions.vue'
import ListTracks from '@/components/ListTracks.vue'
import ModalDialogComposer from '@/components/ModalDialogComposer.vue'
import library from '@/api/library'
import queue from '@/api/queue'
import { useUIStore } from '@/stores/ui'
import webapi from '@/webapi'
export default {
name: 'PageComposerTracks',
@@ -60,8 +61,8 @@ export default {
},
beforeRouteEnter(to, from, next) {
Promise.all([
webapi.library_composer(to.params.name),
webapi.library_composer_tracks(to.params.name)
library.composer(to.params.name),
library.composerTracks(to.params.name)
]).then(([composer, tracks]) => {
next((vm) => {
vm.composer = composer
@@ -134,7 +135,7 @@ export default {
this.showDetailsModal = true
},
play() {
webapi.player_play_expression(this.expression, true)
queue.playExpression(this.expression, true)
}
}
}

View File

@@ -20,7 +20,7 @@ import HeadingTitle from '@/components/HeadingTitle.vue'
import ListComposers from '@/components/ListComposers.vue'
import ListIndexButtons from '@/components/ListIndexButtons.vue'
import TabsMusic from '@/components/TabsMusic.vue'
import webapi from '@/webapi'
import library from '@/api/library'
export default {
name: 'PageComposers',
@@ -32,7 +32,7 @@ export default {
TabsMusic
},
beforeRouteEnter(to, from, next) {
webapi.library_composers('music').then((composers) => {
library.composers('music').then((composers) => {
next((vm) => {
vm.composers = new GroupedList(composers, {
index: { field: 'name_sort', type: String }

View File

@@ -33,8 +33,10 @@ import ListDirectories from '@/components/ListDirectories.vue'
import ListPlaylists from '@/components/ListPlaylists.vue'
import ListTracks from '@/components/ListTracks.vue'
import ModalDialogPlayable from '@/components/ModalDialogPlayable.vue'
import configuration from '@/api/configuration'
import library from '@/api/library'
import queue from '@/api/queue'
import { useConfigurationStore } from '@/stores/configuration'
import webapi from '@/webapi'
export default {
name: 'PageFiles',
@@ -93,7 +95,7 @@ export default {
methods: {
async fetchData(to) {
if (to.query.directory) {
const data = await webapi.library_files(to.query.directory)
const data = await library.files(to.query.directory)
if (data) {
this.directories = data.directories.map((directory) =>
this.transform(directory.path)
@@ -102,7 +104,7 @@ export default {
this.tracks = new GroupedList(data.tracks)
}
} else {
const config = await webapi.config()
const config = await configuration.list()
this.directories = config.directories.map((path) =>
this.transform(path)
)
@@ -114,7 +116,7 @@ export default {
this.showDetailsModal = true
},
play() {
webapi.player_play_expression(this.expression, false)
queue.playExpression(this.expression, false)
},
transform(path) {
return { name: path.slice(path.lastIndexOf('/') + 1), path }

View File

@@ -34,7 +34,8 @@ import HeadingTitle from '@/components/HeadingTitle.vue'
import ListAlbums from '@/components/ListAlbums.vue'
import ListIndexButtons from '@/components/ListIndexButtons.vue'
import ModalDialogGenre from '@/components/ModalDialogGenre.vue'
import webapi from '@/webapi'
import library from '@/api/library'
import queue from '@/api/queue'
export default {
name: 'PageGenreAlbums',
@@ -48,8 +49,8 @@ export default {
},
beforeRouteEnter(to, from, next) {
Promise.all([
webapi.library_genre(to.params.name, to.query.mediaKind),
webapi.library_genre_albums(to.params.name, to.query.mediaKind)
library.genre(to.params.name, to.query.mediaKind),
library.genreAlbums(to.params.name, to.query.mediaKind)
]).then(([genre, albums]) => {
next((vm) => {
vm.genre = genre.items.shift()
@@ -98,7 +99,7 @@ export default {
})
},
play() {
webapi.player_play_expression(
queue.playExpression(
`genre is "${this.genre.name}" and media_kind is ${this.mediaKind}`,
true
)

View File

@@ -44,8 +44,9 @@ import ListIndexButtons from '@/components/ListIndexButtons.vue'
import ListOptions from '@/components/ListOptions.vue'
import ListTracks from '@/components/ListTracks.vue'
import ModalDialogGenre from '@/components/ModalDialogGenre.vue'
import library from '@/api/library'
import queue from '@/api/queue'
import { useUIStore } from '@/stores/ui'
import webapi from '@/webapi'
export default {
name: 'PageGenreTracks',
@@ -61,8 +62,8 @@ export default {
},
beforeRouteEnter(to, from, next) {
Promise.all([
webapi.library_genre(to.params.name, to.query.mediaKind),
webapi.library_genre_tracks(to.params.name, to.query.mediaKind)
library.genre(to.params.name, to.query.mediaKind),
library.genreTracks(to.params.name, to.query.mediaKind)
]).then(([genre, tracks]) => {
next((vm) => {
vm.genre = genre.items.shift()
@@ -135,7 +136,7 @@ export default {
})
},
play() {
webapi.player_play_expression(this.expression, true)
queue.playExpression(this.expression, true)
},
openDetails() {
this.showDetailsModal = true

View File

@@ -20,7 +20,7 @@ import HeadingTitle from '@/components/HeadingTitle.vue'
import ListGenres from '@/components/ListGenres.vue'
import ListIndexButtons from '@/components/ListIndexButtons.vue'
import TabsMusic from '@/components/TabsMusic.vue'
import webapi from '@/webapi'
import library from '@/api/library'
export default {
name: 'PageGenres',
@@ -32,7 +32,7 @@ export default {
TabsMusic
},
beforeRouteEnter(to, from, next) {
webapi.library_genres('music').then((genres) => {
library.genres('music').then((genres) => {
next((vm) => {
vm.genres = new GroupedList(genres, {
index: { field: 'name_sort', type: String }

View File

@@ -45,7 +45,7 @@ import HeadingTitle from '@/components/HeadingTitle.vue'
import ListAlbums from '@/components/ListAlbums.vue'
import ListTracks from '@/components/ListTracks.vue'
import TabsMusic from '@/components/TabsMusic.vue'
import webapi from '@/webapi'
import library from '@/api/library'
export default {
name: 'PageMusic',
@@ -58,13 +58,13 @@ export default {
},
beforeRouteEnter(to, from, next) {
Promise.all([
webapi.search({
library.search({
expression:
'time_added after 8 weeks ago and media_kind is music having track_count > 3 order by time_added desc',
limit: 3,
type: 'album'
}),
webapi.search({
library.search({
expression:
'time_played after 8 weeks ago and media_kind is music order by time_played desc',
limit: 3,

View File

@@ -18,15 +18,15 @@ import { GroupedList } from '@/lib/GroupedList'
import HeadingTitle from '@/components/HeadingTitle.vue'
import ListAlbums from '@/components/ListAlbums.vue'
import TabsMusic from '@/components/TabsMusic.vue'
import library from '@/api/library'
import { useSettingsStore } from '@/stores/settings'
import webapi from '@/webapi'
export default {
name: 'PageMusicRecentlyAdded',
components: { ContentWithHeading, HeadingTitle, ListAlbums, TabsMusic },
beforeRouteEnter(to, from, next) {
const limit = useSettingsStore().recentlyAddedLimit
webapi
library
.search({
expression:
'media_kind is music having track_count > 3 order by time_added desc',

View File

@@ -18,13 +18,13 @@ import { GroupedList } from '@/lib/GroupedList'
import HeadingTitle from '@/components/HeadingTitle.vue'
import ListTracks from '@/components/ListTracks.vue'
import TabsMusic from '@/components/TabsMusic.vue'
import webapi from '@/webapi'
import library from '@/api/library'
export default {
name: 'PageMusicRecentlyPlayed',
components: { ContentWithHeading, HeadingTitle, ListTracks, TabsMusic },
beforeRouteEnter(to, from, next) {
webapi
library
.search({
expression:
'time_played after 8 weeks ago and media_kind is music order by time_played desc',

View File

@@ -45,7 +45,7 @@ import ListAlbumsSpotify from '@/components/ListAlbumsSpotify.vue'
import ListPlaylistsSpotify from '@/components/ListPlaylistsSpotify.vue'
import SpotifyWebApi from 'spotify-web-api-js'
import TabsMusic from '@/components/TabsMusic.vue'
import webapi from '@/webapi'
import services from '@/api/services'
export default {
name: 'PageMusicSpotify',
@@ -57,7 +57,7 @@ export default {
TabsMusic
},
beforeRouteEnter(to, from, next) {
webapi.spotify().then((data) => {
services.spotify().then((data) => {
const spotifyApi = new SpotifyWebApi()
spotifyApi.setAccessToken(data.webapi_token)
Promise.all([

View File

@@ -16,7 +16,7 @@ import HeadingTitle from '@/components/HeadingTitle.vue'
import ListPlaylistsSpotify from '@/components/ListPlaylistsSpotify.vue'
import SpotifyWebApi from 'spotify-web-api-js'
import TabsMusic from '@/components/TabsMusic.vue'
import webapi from '@/webapi'
import services from '@/api/services'
export default {
name: 'PageMusicSpotifyFeaturedPlaylists',
@@ -27,7 +27,7 @@ export default {
TabsMusic
},
beforeRouteEnter(to, from, next) {
webapi.spotify().then((data) => {
services.spotify().then((data) => {
const spotifyApi = new SpotifyWebApi()
spotifyApi.setAccessToken(data.webapi_token)
spotifyApi

View File

@@ -16,7 +16,7 @@ import HeadingTitle from '@/components/HeadingTitle.vue'
import ListAlbumsSpotify from '@/components/ListAlbumsSpotify.vue'
import SpotifyWebApi from 'spotify-web-api-js'
import TabsMusic from '@/components/TabsMusic.vue'
import webapi from '@/webapi'
import services from '@/api/services'
export default {
name: 'PageMusicSpotifyNewReleases',
@@ -27,7 +27,7 @@ export default {
TabsMusic
},
beforeRouteEnter(to, from, next) {
webapi.spotify().then((data) => {
services.spotify().then((data) => {
const spotifyApi = new SpotifyWebApi()
spotifyApi.setAccessToken(data.webapi_token)
spotifyApi

View File

@@ -55,11 +55,11 @@ import ControlImage from '@/components/ControlImage.vue'
import ControlSlider from '@/components/ControlSlider.vue'
import LyricsPane from '@/components/LyricsPane.vue'
import ModalDialogQueueItem from '@/components/ModalDialogQueueItem.vue'
import player from '@/api/player'
import { useLyricsStore } from '@/stores/lyrics'
import { usePlayerStore } from '@/stores/player'
import { useQueueStore } from '@/stores/queue'
import { useSettingsStore } from '@/stores/settings'
import webapi from '@/webapi'
const INTERVAL = 1000
@@ -148,7 +148,7 @@ export default {
}
},
created() {
webapi.player_status().then((data) => {
player.state().then((data) => {
this.playerStore.$state = data
if (this.playerStore.state === 'play') {
this.intervalId = window.setInterval(this.tick, INTERVAL)
@@ -171,7 +171,7 @@ export default {
},
seek() {
if (!this.isLive) {
webapi.player_seek_to_pos(this.trackProgress * INTERVAL)
player.seekToPosition(this.trackProgress * INTERVAL)
}
},
startDragging() {

View File

@@ -14,8 +14,8 @@ import ContentWithHeading from '@/templates/ContentWithHeading.vue'
import { GroupedList } from '@/lib/GroupedList'
import HeadingTitle from '@/components/HeadingTitle.vue'
import ListPlaylists from '@/components/ListPlaylists.vue'
import library from '@/api/library'
import { useConfigurationStore } from '@/stores/configuration'
import webapi from '@/webapi'
export default {
name: 'PagePlaylistFolder',
@@ -63,8 +63,8 @@ export default {
methods: {
async fetchData(id) {
const [playlist, playlistFolder] = await Promise.all([
webapi.library_playlist(id),
webapi.library_playlist_folder(id)
library.playlist(id),
library.playlistFolder(id)
])
this.playlist = playlist
this.playlistList = new GroupedList(playlistFolder)

View File

@@ -35,7 +35,8 @@ import { GroupedList } from '@/lib/GroupedList'
import HeadingTitle from '@/components/HeadingTitle.vue'
import ListTracks from '@/components/ListTracks.vue'
import ModalDialogPlaylist from '@/components/ModalDialogPlaylist.vue'
import webapi from '@/webapi'
import library from '@/api/library'
import queue from '@/api/queue'
export default {
name: 'PagePlaylistTracks',
@@ -48,8 +49,8 @@ export default {
},
beforeRouteEnter(to, from, next) {
Promise.all([
webapi.library_playlist(to.params.id),
webapi.library_playlist_tracks(to.params.id)
library.playlist(to.params.id),
library.playlistTracks(to.params.id)
]).then(([playlist, tracks]) => {
next((vm) => {
vm.playlist = playlist
@@ -80,7 +81,7 @@ export default {
},
methods: {
play() {
webapi.player_play_uri(this.uris, true)
queue.playUri(this.uris, true)
},
openDetails() {
this.showDetailsModal = true

View File

@@ -39,8 +39,8 @@ import HeadingTitle from '@/components/HeadingTitle.vue'
import ListTracksSpotify from '@/components/ListTracksSpotify.vue'
import ModalDialogPlaylistSpotify from '@/components/ModalDialogPlaylistSpotify.vue'
import SpotifyWebApi from 'spotify-web-api-js'
import queue from '@/api/queue'
import { useServicesStore } from '@/stores/services'
import webapi from '@/webapi'
const PAGE_SIZE = 50
@@ -137,7 +137,7 @@ export default {
},
play() {
this.showDetailsModal = false
webapi.player_play_uri(this.playlist.uri, true)
queue.playUri(this.playlist.uri, true)
},
openDetails() {
this.showDetailsModal = true

View File

@@ -36,7 +36,8 @@ import { GroupedList } from '@/lib/GroupedList'
import HeadingHero from '@/components/HeadingHero.vue'
import ListTracks from '@/components/ListTracks.vue'
import ModalDialogAlbum from '@/components/ModalDialogAlbum.vue'
import webapi from '@/webapi'
import library from '@/api/library'
import queue from '@/api/queue'
export default {
name: 'PagePodcast',
@@ -49,8 +50,8 @@ export default {
},
beforeRouteEnter(to, from, next) {
Promise.all([
webapi.library_album(to.params.id),
webapi.library_podcast_episodes(to.params.id)
library.album(to.params.id),
library.podcastEpisodes(to.params.id)
]).then(([album, tracks]) => {
next((vm) => {
vm.album = album
@@ -80,10 +81,10 @@ export default {
},
methods: {
play() {
webapi.player_play_uri(this.album.uri, false)
queue.playUri(this.album.uri, false)
},
reloadTracks() {
webapi.library_podcast_episodes(this.album.id).then((tracks) => {
library.podcastEpisodes(this.album.id).then((tracks) => {
this.tracks = new GroupedList(tracks)
})
},

View File

@@ -1,5 +1,5 @@
<template>
<content-with-heading v-if="tracks.items.length > 0">
<content-with-heading v-if="episodes.items.length > 0">
<template #heading>
<heading-title :content="{ title: $t('page.podcasts.new-episodes') }" />
</template>
@@ -14,9 +14,9 @@
</template>
<template #content>
<list-tracks
:items="tracks"
:items="episodes"
:show-progress="true"
@play-count-changed="reloadNewEpisodes"
@play-count-changed="reloadEpisodes"
/>
</template>
</content-with-heading>
@@ -26,7 +26,7 @@
</template>
<template #actions>
<control-button
v-if="libraryStore.rss"
v-if="hasRss"
:button="{
handler: updateRss,
icon: 'refresh',
@@ -44,7 +44,7 @@
<template #content>
<list-albums
:items="albums"
@play-count-changed="reloadNewEpisodes"
@play-count-changed="reloadEpisodes"
@podcast-deleted="reloadPodcasts"
/>
</template>
@@ -64,9 +64,9 @@ import HeadingTitle from '@/components/HeadingTitle.vue'
import ListAlbums from '@/components/ListAlbums.vue'
import ListTracks from '@/components/ListTracks.vue'
import ModalDialogAddRss from '@/components/ModalDialogAddRss.vue'
import library from '@/api/library'
import { useLibraryStore } from '@/stores/library'
import { useUIStore } from '@/stores/ui'
import webapi from '@/webapi'
export default {
name: 'PagePodcasts',
@@ -80,12 +80,14 @@ export default {
},
beforeRouteEnter(to, from, next) {
Promise.all([
webapi.library_albums('podcast'),
webapi.library_podcasts_new_episodes()
]).then(([albums, tracks]) => {
library.albums('podcast'),
library.newPodcastEpisodes(),
library.rssCount()
]).then(([albums, episodes, rssCount]) => {
next((vm) => {
vm.albums = new GroupedList(albums)
vm.tracks = new GroupedList(tracks)
vm.episodes = new GroupedList(episodes)
vm.rssCount = rssCount
})
})
},
@@ -95,11 +97,15 @@ export default {
data() {
return {
albums: [],
showAddPodcastModal: false,
tracks: { items: [] }
episodes: { items: [] },
rssCount: {},
showAddPodcastModal: false
}
},
computed: {
hasRss() {
return (this.rssCount.albums ?? 0) > 0
},
heading() {
if (this.albums.total) {
return {
@@ -112,23 +118,29 @@ export default {
},
methods: {
markAllAsPlayed() {
this.tracks.items.forEach((ep) => {
webapi.library_track_update(ep.id, { play_count: 'increment' })
this.episodes.items.forEach((episode) => {
library.updateTrack(episode.id, { play_count: 'increment' })
})
this.tracks.items = {}
this.episodes.items = {}
},
openAddPodcastDialog() {
this.showAddPodcastModal = true
},
reloadNewEpisodes() {
webapi.library_podcasts_new_episodes().then((tracks) => {
this.tracks = new GroupedList(tracks)
reloadEpisodes() {
library.newPodcastEpisodes().then((episodes) => {
this.episodes = new GroupedList(episodes)
})
},
reloadPodcasts() {
webapi.library_albums('podcast').then((albums) => {
library.albums('podcast').then((albums) => {
this.albums = new GroupedList(albums)
this.reloadNewEpisodes()
this.reloadEpisodes()
this.reloadRssCount()
})
},
reloadRssCount() {
library.rssCount().then((rssCount) => {
this.rssCount = rssCount
})
},
updateRss() {

View File

@@ -101,11 +101,11 @@ import ModalDialogAddStream from '@/components/ModalDialogAddStream.vue'
import ModalDialogPlaylistSave from '@/components/ModalDialogPlaylistSave.vue'
import ModalDialogQueueItem from '@/components/ModalDialogQueueItem.vue'
import draggable from 'vuedraggable'
import queue from '@/api/queue'
import { useConfigurationStore } from '@/stores/configuration'
import { usePlayerStore } from '@/stores/player'
import { useQueueStore } from '@/stores/queue'
import { useUIStore } from '@/stores/ui'
import webapi from '@/webapi'
export default {
name: 'PageQueue',
@@ -157,7 +157,7 @@ export default {
},
methods: {
clearQueue() {
webapi.queue_clear()
queue.clear()
},
isRemovable(item) {
return item.id !== this.playerStore.item_id && this.editing
@@ -168,7 +168,7 @@ export default {
const item = this.items[oldPosition]
const newPosition = item.position + (event.newIndex - event.oldIndex)
if (newPosition !== oldPosition) {
webapi.queue_move(item.id, newPosition)
queue.move(item.id, newPosition)
}
},
openAddStreamDialog() {
@@ -184,7 +184,7 @@ export default {
}
},
remove(item) {
webapi.queue_remove(item.id)
queue.remove(item.id)
},
toggleEdit() {
this.editing = !this.editing

View File

@@ -18,7 +18,7 @@ import { GroupedList } from '@/lib/GroupedList'
import HeadingTitle from '@/components/HeadingTitle.vue'
import ListIndexButtons from '@/components/ListIndexButtons.vue'
import ListTracks from '@/components/ListTracks.vue'
import webapi from '@/webapi'
import library from '@/api/library'
export default {
name: 'PageRadioStreams',
@@ -29,7 +29,7 @@ export default {
ListTracks
},
beforeRouteEnter(to, from, next) {
webapi.library_radio_streams().then((tracks) => {
library.radioStreams().then((tracks) => {
next((vm) => {
vm.tracks = new GroupedList(tracks, {
index: { field: 'title_sort', type: String }

View File

@@ -84,8 +84,8 @@ import ListComposers from '@/components/ListComposers.vue'
import ListPlaylists from '@/components/ListPlaylists.vue'
import ListTracks from '@/components/ListTracks.vue'
import TabsSearch from '@/components/TabsSearch.vue'
import library from '@/api/library'
import { useSearchStore } from '@/stores/search'
import webapi from '@/webapi'
const PAGE_SIZE = 3,
SEARCH_TYPES = [
@@ -190,7 +190,7 @@ export default {
} else {
parameters.expression = `(album includes "${this.searchStore.query}" or artist includes "${this.searchStore.query}") and media_kind is ${kind}`
}
webapi.search(parameters).then((data) => {
library.search(parameters).then((data) => {
this.results.set(type, new GroupedList(data[`${parameters.type}s`]))
})
},

View File

@@ -71,8 +71,8 @@ import ListPlaylistsSpotify from '@/components/ListPlaylistsSpotify.vue'
import ListTracksSpotify from '@/components/ListTracksSpotify.vue'
import SpotifyWebApi from 'spotify-web-api-js'
import TabsSearch from '@/components/TabsSearch.vue'
import services from '@/api/services'
import { useSearchStore } from '@/stores/search'
import webapi from '@/webapi'
const PAGE_SIZE = 3,
PAGE_SIZE_EXPANDED = 50,
@@ -158,7 +158,7 @@ export default {
}
},
searchItems() {
return webapi.spotify().then((data) => {
return services.spotify().then((data) => {
this.parameters.market = data.webapi_country
const spotifyApi = new SpotifyWebApi()
spotifyApi.setAccessToken(data.webapi_token)

View File

@@ -40,7 +40,11 @@
class="content"
v-text="$t('page.settings.devices.speaker-pairing-info')"
/>
<div v-for="output in outputs" :key="output.id" class="field is-grouped">
<div
v-for="output in outputsStore.outputs"
:key="output.id"
class="field is-grouped"
>
<control-switch
v-model="output.selected"
@update:model-value="toggleOutput(output.id)"
@@ -77,9 +81,9 @@ import ControlPinField from '@/components/ControlPinField.vue'
import ControlSwitch from '@/components/ControlSwitch.vue'
import HeadingTitle from '@/components/HeadingTitle.vue'
import TabsSettings from '@/components/TabsSettings.vue'
import outputs from '@/api/outputs'
import { useOutputsStore } from '@/stores/outputs'
import { useRemotesStore } from '@/stores/remotes'
import webapi from '@/webapi'
export default {
name: 'PageSettingsDevices',
@@ -100,17 +104,12 @@ export default {
remotePin: ''
}
},
computed: {
outputs() {
return this.outputsStore.outputs
}
},
methods: {
pairRemote() {
webapi.pairing_kickoff({ pin: this.remotePin })
remotes.pair(this.remotePin)
},
pairOutput(identifier) {
webapi.output_update(identifier, { pin: this.outputPin })
outputs.update(identifier, { pin: this.outputPin })
},
onRemotePinChange(pin, disabled) {
this.remotePin = pin
@@ -120,7 +119,7 @@ export default {
this.outputPin = pin
},
toggleOutput(identifier) {
webapi.output_toggle(identifier)
outputs.toggle(identifier)
}
}
}

View File

@@ -73,23 +73,28 @@
<div class="field is-grouped">
<div class="control">
<input
v-model="lastfm_login.user"
v-model="lastfmCredentials.user"
class="input"
type="text"
:placeholder="$t('page.settings.services.username')"
/>
<div class="help is-danger" v-text="lastfm_login.errors.user" />
<div
v-if="lastfmErrors"
class="help is-danger"
v-text="lastfmErrors.user"
/>
</div>
<div class="control">
<input
v-model="lastfm_login.password"
v-model="lastfmCredentials.password"
class="input"
type="password"
:placeholder="$t('page.settings.services.password')"
/>
<div
v-if="lastfmErrors"
class="help is-danger"
v-text="lastfm_login.errors.password"
v-text="lastfmErrors.password"
/>
</div>
<div class="control">
@@ -100,7 +105,11 @@
/>
</div>
</div>
<div class="help is-danger" v-text="lastfm_login.errors.error" />
<div
v-if="lastfmErrors"
class="help is-danger"
v-text="lastfmErrors.error"
/>
</form>
</div>
<div v-else>
@@ -120,8 +129,8 @@
import ContentWithHeading from '@/templates/ContentWithHeading.vue'
import HeadingTitle from '@/components/HeadingTitle.vue'
import TabsSettings from '@/components/TabsSettings.vue'
import services from '@/api/services'
import { useServicesStore } from '@/stores/services'
import webapi from '@/webapi'
export default {
name: 'PageSettingsOnlineServices',
@@ -131,33 +140,25 @@ export default {
},
data() {
return {
lastfm_login: {
errors: { error: '', password: '', user: '' },
password: '',
user: ''
}
lastfmCredentials: { password: '', user: '' },
lastfmErrors: { error: '', password: '', user: '' }
}
},
methods: {
loginLastfm() {
webapi.lastfm_login(this.lastfm_login).then((data) => {
this.lastfm_login.user = ''
this.lastfm_login.password = ''
this.lastfm_login.errors.user = ''
this.lastfm_login.errors.password = ''
this.lastfm_login.errors.error = ''
if (!data.success) {
this.lastfm_login.errors.user = data.errors.user
this.lastfm_login.errors.password = data.errors.password
this.lastfm_login.errors.error = data.errors.error
services.loginLastfm(this.lastfmCredentials).then((data) => {
this.lastfmErrors = data.errors
this.lastfmCredentials.password = ''
if (data.success) {
this.lastfmCredentials.user = ''
}
})
},
logoutLastfm() {
webapi.lastfm_logout()
services.logoutLastfm()
},
logoutSpotify() {
webapi.spotify_logout()
services.logoutSpotify()
}
}
}

View File

@@ -1,11 +1,16 @@
import { defineStore } from 'pinia'
import library from '@/api/library'
export const useLibraryStore = defineStore('LibraryStore', {
actions: {
async initialise() {
this.$state = await library.state()
}
},
state: () => ({
albums: 0,
artists: 0,
db_playtime: 0,
rss: {},
songs: 0,
started_at: '01',
update_dialog_scan_kind: '',

View File

@@ -1,6 +1,12 @@
import { defineStore } from 'pinia'
import outputs from '@/api/outputs'
export const useOutputsStore = defineStore('OutputsStore', {
actions: {
async initialise() {
this.$state = await outputs.state()
}
},
state: () => ({
outputs: []
})

View File

@@ -1,6 +1,12 @@
import { defineStore } from 'pinia'
import player from '@/api/player'
export const usePlayerStore = defineStore('PlayerStore', {
actions: {
async initialise() {
this.$state = await player.state()
}
},
getters: {
isPlaying: (state) => state.state === 'play',
isRepeatAll: (state) => state.repeat === 'all',

View File

@@ -1,8 +1,14 @@
import { defineStore } from 'pinia'
import queue from '@/api/queue'
import { useConfigurationStore } from '@/stores/configuration'
import { usePlayerStore } from '@/stores/player'
export const useQueueStore = defineStore('QueueStore', {
actions: {
async initialise() {
this.$state = await queue.state()
}
},
getters: {
current(state) {
const player = usePlayerStore()

View File

@@ -1,6 +1,12 @@
import { defineStore } from 'pinia'
import remotes from '@/api/remotes'
export const useRemotesStore = defineStore('RemotesStore', {
actions: {
async initialise() {
this.$state = await remotes.state()
}
},
state: () => ({
active: false,
remote: ''

View File

@@ -1,5 +1,6 @@
import { defineStore } from 'pinia'
import i18n from '@/i18n'
import settings from '@/api/settings'
const { t, availableLocales } = i18n.global
@@ -12,6 +13,9 @@ export const useSettingsStore = defineStore('SettingsStore', {
?.options.find((option) => option.name === optionName) ?? {}
)
},
async initialise() {
this.$state = await settings.state()
},
update(option) {
const settingCategory = this.categories.find(
(category) => category.name === option.category

View File

@@ -1,427 +0,0 @@
import axios from 'axios'
import i18n from '@/i18n'
import { useNotificationsStore } from '@/stores/notifications'
import { useQueueStore } from '@/stores/queue'
const { t } = i18n.global
axios.interceptors.response.use(
(response) => response.data,
(error) => {
if (error.request.status && error.request.responseURL) {
useNotificationsStore().add({
text: t('server.request-failed', {
cause: error.request.statusText,
status: error.request.status,
url: error.request.responseURL
}),
type: 'danger'
})
}
return Promise.reject(error)
}
)
export default {
config() {
return axios.get('./api/config')
},
lastfm() {
return axios.get('./api/lastfm')
},
lastfm_login(credentials) {
return axios.post('./api/lastfm-login', credentials)
},
lastfm_logout() {
return axios.get('./api/lastfm-logout')
},
library_add(url) {
return axios.post('./api/library/add', null, { params: { url } })
},
library_album(albumId) {
return axios.get(`./api/library/albums/${albumId}`)
},
library_album_track_update(albumId, attributes) {
return axios.put(`./api/library/albums/${albumId}/tracks`, null, {
params: attributes
})
},
library_album_tracks(albumId, filter = { limit: -1, offset: 0 }) {
return axios.get(`./api/library/albums/${albumId}/tracks`, {
params: filter
})
},
library_albums(mediaKind) {
return axios.get('./api/library/albums', { params: { mediaKind } })
},
library_artist(artistId) {
return axios.get(`./api/library/artists/${artistId}`)
},
library_artist_albums(artistId) {
return axios.get(`./api/library/artists/${artistId}/albums`)
},
async library_artist_tracks(artist) {
const params = {
expression: `songartistid is "${artist}"`,
type: 'tracks'
}
const data = await axios.get('./api/search', { params })
return data.tracks
},
library_artists(mediaKind) {
return axios.get('./api/library/artists', { params: { mediaKind } })
},
library_composer(composer) {
return axios.get(`./api/library/composers/${encodeURIComponent(composer)}`)
},
async library_composer_albums(composer) {
const params = {
expression: `composer is "${composer}" and media_kind is music`,
type: 'albums'
}
const data = await axios.get('./api/search', { params })
return data.albums
},
async library_composer_tracks(composer) {
const params = {
expression: `composer is "${composer}" and media_kind is music`,
type: 'tracks'
}
const data = await axios.get('./api/search', { params })
return data.tracks
},
library_composers(mediaKind) {
return axios.get('./api/library/composers', { params: { mediaKind } })
},
library_count(expression) {
return axios.get(`./api/library/count?expression=${expression}`)
},
library_files(directory) {
return axios.get('./api/library/files', { params: { directory } })
},
async library_genre(genre, mediaKind) {
const params = {
expression: `genre is "${genre}" and media_kind is ${mediaKind}`,
type: 'genres'
}
const data = await axios.get('./api/search', { params })
return data.genres
},
async library_genre_albums(genre, mediaKind) {
const params = {
expression: `genre is "${genre}" and media_kind is ${mediaKind}`,
type: 'albums'
}
const data = await axios.get('./api/search', { params })
return data.albums
},
async library_genre_tracks(genre, mediaKind) {
const params = {
expression: `genre is "${genre}" and media_kind is ${mediaKind}`,
type: 'tracks'
}
const data = await axios.get('./api/search', { params })
return data.tracks
},
async library_genres(mediaKind) {
const params = {
expression: `media_kind is ${mediaKind}`,
type: 'genres'
}
const data = await axios.get('./api/search', { params })
return data.genres
},
library_playlist(playlistId) {
return axios.get(`./api/library/playlists/${playlistId}`)
},
library_playlist_delete(playlistId) {
return axios.delete(`./api/library/playlists/${playlistId}`)
},
library_playlist_folder(playlistId = 0) {
return axios.get(`./api/library/playlists/${playlistId}/playlists`)
},
library_playlist_tracks(playlistId) {
return axios.get(`./api/library/playlists/${playlistId}/tracks`)
},
async library_podcast_episodes(albumId) {
const params = {
expression: `media_kind is podcast and songalbumid is "${albumId}" ORDER BY date_released DESC`,
type: 'tracks'
}
const data = await axios.get('./api/search', { params })
return data.tracks
},
async library_podcasts_new_episodes() {
const params = {
expression:
'media_kind is podcast and play_count = 0 ORDER BY time_added DESC',
type: 'tracks'
}
const data = await axios.get('./api/search', { params })
return data.tracks
},
async library_radio_streams() {
const params = {
expression: 'data_kind is url and song_length = 0',
media_kind: 'music',
type: 'tracks'
}
const data = await axios.get('./api/search', { params })
return data.tracks
},
library_rescan(scanKind) {
return axios.put('./api/rescan', null, { params: { scanKind } })
},
library_stats() {
return axios.get('./api/library')
},
library_track(trackId) {
return axios.get(`./api/library/tracks/${trackId}`)
},
library_track_playlists(trackId) {
return axios.get(`./api/library/tracks/${trackId}/playlists`)
},
library_track_update(trackId, attributes = {}) {
return axios.put(`./api/library/tracks/${trackId}`, null, {
params: attributes
})
},
library_update(scanKind) {
return axios.put('./api/update', null, { params: { scanKind } })
},
output_toggle(outputId) {
return axios.put(`./api/outputs/${outputId}/toggle`)
},
output_update(outputId, output) {
return axios.put(`./api/outputs/${outputId}`, output)
},
outputs() {
return axios.get('./api/outputs')
},
pairing() {
return axios.get('./api/pairing')
},
pairing_kickoff(request) {
return axios.post('./api/pairing', request)
},
player_consume(state) {
return axios.put(`./api/player/consume?state=${state}`)
},
player_next() {
return axios.put('./api/player/next')
},
player_output_volume(outputId, outputVolume) {
return axios.put(
`./api/player/volume?volume=${outputVolume}&output_id=${outputId}`
)
},
player_pause() {
return axios.put('./api/player/pause')
},
player_play(options = {}) {
return axios.put('./api/player/play', null, { params: options })
},
player_play_expression(expression, shuffle, position) {
const params = {
clear: 'true',
expression,
playback: 'start',
playback_from_position: position,
shuffle
}
return axios.post('./api/queue/items/add', null, { params })
},
player_play_uri(uris, shuffle, position) {
const params = {
clear: 'true',
playback: 'start',
playback_from_position: position,
shuffle,
uris
}
return axios.post('./api/queue/items/add', null, { params })
},
player_previous() {
return axios.put('./api/player/previous')
},
player_repeat(mode) {
return axios.put(`./api/player/repeat?state=${mode}`)
},
player_seek(seekMs) {
return axios.put(`./api/player/seek?seek_ms=${seekMs}`)
},
player_seek_to_pos(position) {
return axios.put(`./api/player/seek?position_ms=${position}`)
},
player_shuffle(state) {
return axios.put(`./api/player/shuffle?state=${state}`)
},
player_status() {
return axios.get('./api/player')
},
player_stop() {
return axios.put('./api/player/stop')
},
player_volume(volume) {
return axios.put(`./api/player/volume?volume=${volume}`)
},
queue() {
return axios.get('./api/queue')
},
async queue_add(uri) {
const data = await axios.post(`./api/queue/items/add?uris=${uri}`)
useNotificationsStore().add({
text: t('server.appended-tracks', { count: data.count }),
timeout: 2000,
type: 'info'
})
return await Promise.resolve(data)
},
async queue_add_next(uri) {
let position = 0
const { current } = useQueueStore()
if (current?.id) {
position = current.position + 1
}
const data = await axios.post(
`./api/queue/items/add?uris=${uri}&position=${position}`
)
useNotificationsStore().add({
text: t('server.appended-tracks', { count: data.count }),
timeout: 2000,
type: 'info'
})
return await Promise.resolve(data)
},
queue_clear() {
return axios.put('./api/queue/clear')
},
async queue_expression_add(expression) {
const data = await axios.post('./api/queue/items/add', null, {
params: { expression }
})
useNotificationsStore().add({
text: t('server.appended-tracks', { count: data.count }),
timeout: 2000,
type: 'info'
})
return await Promise.resolve(data)
},
async queue_expression_add_next(expression) {
const params = {}
params.expression = expression
params.position = 0
const { current } = useQueueStore()
if (current?.id) {
params.position = current.position + 1
}
const data = await axios.post('./api/queue/items/add', null, { params })
useNotificationsStore().add({
text: t('server.appended-tracks', { count: data.count }),
timeout: 2000,
type: 'info'
})
return await Promise.resolve(data)
},
queue_move(itemId, position) {
return axios.put(`./api/queue/items/${itemId}?new_position=${position}`)
},
queue_remove(itemId) {
return axios.delete(`./api/queue/items/${itemId}`)
},
async queue_save_playlist(name) {
const response = await axios.post('./api/queue/save', null, {
params: { name }
})
useNotificationsStore().add({
text: t('server.queue-saved', { name }),
timeout: 2000,
type: 'info'
})
return await Promise.resolve(response)
},
search(params) {
return axios.get('./api/search', { params })
},
settings() {
return axios.get('./api/settings')
},
settings_update(categoryName, option) {
return axios.put(`./api/settings/${categoryName}/${option.name}`, option)
},
spotify() {
return axios.get('./api/spotify')
},
spotify_logout() {
return axios.get('./api/spotify-logout')
}
}