mirror of
https://github.com/owntone/owntone-server.git
synced 2024-12-27 15:45:56 -05:00
[web-src] add composer support
This commit is contained in:
parent
8c97ad591f
commit
269842d30a
84
web-src/src/components/ListComposers.vue
Normal file
84
web-src/src/components/ListComposers.vue
Normal file
@ -0,0 +1,84 @@
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="is_grouped">
|
||||
<div v-for="idx in composers.indexList" :key="idx" class="mb-6">
|
||||
<span class="tag is-info is-light is-small has-text-weight-bold" :id="'index_' + idx">{{ idx }}</span>
|
||||
<list-item-composer v-for="composer in composers.grouped[idx]"
|
||||
:key="composer.id"
|
||||
:composer="composer"
|
||||
@click="open_composer(composer)">
|
||||
<template slot="actions">
|
||||
<a @click="open_dialog(composer)">
|
||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
||||
</a>
|
||||
</template>
|
||||
</list-item-composer>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<list-item-composer v-for="composer in composers_list"
|
||||
:key="composer.id"
|
||||
:composer="composer"
|
||||
@click="open_composer(composer)">
|
||||
<template slot="actions">
|
||||
<a @click="open_dialog(composer)">
|
||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
||||
</a>
|
||||
</template>
|
||||
</list-item-composer>
|
||||
</div>
|
||||
<modal-dialog-composer :show="show_details_modal" :composer="selected_composer" :media_kind="media_kind" @close="show_details_modal = false" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ListItemComposer from '@/components/ListItemComposer'
|
||||
import ModalDialogComposer from '@/components/ModalDialogComposer'
|
||||
import Composers from '@/lib/Composers'
|
||||
|
||||
export default {
|
||||
name: 'ListComposers',
|
||||
components: { ListItemComposer, ModalDialogComposer },
|
||||
|
||||
props: ['composers', 'media_kind'],
|
||||
|
||||
data () {
|
||||
return {
|
||||
show_details_modal: false,
|
||||
selected_composer: {}
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
media_kind_resolved: function () {
|
||||
return this.media_kind ? this.media_kind : this.selected_composer.media_kind
|
||||
},
|
||||
|
||||
composers_list: function () {
|
||||
if (Array.isArray(this.composers)) {
|
||||
return this.composers
|
||||
}
|
||||
return this.composers.sortedAndFiltered
|
||||
},
|
||||
|
||||
is_grouped: function () {
|
||||
return (this.composers instanceof Composers && this.composers.options.group)
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
open_composer: function (composer) {
|
||||
this.selected_composer = composer
|
||||
this.$router.push({ name: 'ComposerTracks', params: { composer: composer.name } })
|
||||
},
|
||||
|
||||
open_dialog: function (composer) {
|
||||
this.selected_composer = composer
|
||||
this.show_details_modal = true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
20
web-src/src/components/ListItemComposer.vue
Normal file
20
web-src/src/components/ListItemComposer.vue
Normal file
@ -0,0 +1,20 @@
|
||||
<template functional>
|
||||
<div class="media" :id="'index_' + props.composer.name.charAt(0).toUpperCase()">
|
||||
<div class="media-content fd-has-action is-clipped" @click="listeners.click">
|
||||
<h1 class="title is-6">{{ props.composer.name }}</h1>
|
||||
</div>
|
||||
<div class="media-right">
|
||||
<slot name="actions"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ListItemComposer',
|
||||
props: ['composer']
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
77
web-src/src/components/ModalDialogComposer.vue
Normal file
77
web-src/src/components/ModalDialogComposer.vue
Normal file
@ -0,0 +1,77 @@
|
||||
<template>
|
||||
<div>
|
||||
<transition name="fade">
|
||||
<div class="modal is-active" v-if="show">
|
||||
<div class="modal-background" @click="$emit('close')"></div>
|
||||
<div class="modal-content fd-modal-card">
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<p class="title is-4">
|
||||
<a class="has-text-link" @click="open_albums">{{ composer.name }}</a>
|
||||
</p>
|
||||
<p>
|
||||
<span class="heading">Albums</span>
|
||||
<a class="has-text-link is-6" @click="open_albums">{{ composer.album_count }}</a>
|
||||
</p>
|
||||
<p>
|
||||
<span class="heading">Tracks</span>
|
||||
<a class="has-text-link is-6" @click="open_tracks">{{ composer.track_count }}</a>
|
||||
</p>
|
||||
</div>
|
||||
<footer class="card-footer">
|
||||
<a class="card-footer-item has-text-dark" @click="queue_add">
|
||||
<span class="icon"><i class="mdi mdi-playlist-plus"></i></span> <span class="is-size-7">Add</span>
|
||||
</a>
|
||||
<a class="card-footer-item has-text-dark" @click="queue_add_next">
|
||||
<span class="icon"><i class="mdi mdi-playlist-play"></i></span> <span class="is-size-7">Add Next</span>
|
||||
</a>
|
||||
<a class="card-footer-item has-text-dark" @click="play">
|
||||
<span class="icon"><i class="mdi mdi-play"></i></span> <span class="is-size-7">Play</span>
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
<button class="modal-close is-large" aria-label="close" @click="$emit('close')"></button>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import webapi from '@/webapi'
|
||||
|
||||
export default {
|
||||
name: 'ModalDialogComposer',
|
||||
props: ['show', 'composer'],
|
||||
|
||||
methods: {
|
||||
play: function () {
|
||||
this.$emit('close')
|
||||
webapi.player_play_expression('composer is "' + this.composer.name + '" and media_kind is music', false)
|
||||
},
|
||||
|
||||
queue_add: function () {
|
||||
this.$emit('close')
|
||||
webapi.queue_expression_add('composer is "' + this.composer.name + '" and media_kind is music')
|
||||
},
|
||||
|
||||
queue_add_next: function () {
|
||||
this.$emit('close')
|
||||
webapi.queue_expression_add_next('composer is "' + this.composer.name + '" and media_kind is music')
|
||||
},
|
||||
|
||||
open_albums: function () {
|
||||
this.$emit('close')
|
||||
this.$router.push({ name: 'ComposerAlbums', params: { composer: this.composer.name } })
|
||||
},
|
||||
|
||||
open_tracks: function () {
|
||||
this.show_details_modal = false
|
||||
this.$router.push({ name: 'ComposerTracks', params: { composer: this.composer.name } })
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
@ -29,6 +29,12 @@
|
||||
<span class="">Genres</span>
|
||||
</a>
|
||||
</router-link>
|
||||
<router-link tag="li" to="/music/composers" active-class="is-active">
|
||||
<a>
|
||||
<span class="icon is-small"><i class="mdi mdi-book-open-page-variant"></i></span>
|
||||
<span class="">Composers</span>
|
||||
</a>
|
||||
</router-link>
|
||||
<router-link tag="li" to="/music/spotify" v-if="spotify_enabled" active-class="is-active">
|
||||
<a>
|
||||
<span class="icon is-small"><i class="mdi mdi-spotify"></i></span>
|
||||
|
62
web-src/src/lib/Composers.js
Normal file
62
web-src/src/lib/Composers.js
Normal file
@ -0,0 +1,62 @@
|
||||
|
||||
export default class Composers {
|
||||
constructor (items, options = { hideSingles: false, hideSpotify: false, sort: 'Name', group: false }) {
|
||||
this.items = items
|
||||
this.options = options
|
||||
this.grouped = {}
|
||||
this.sortedAndFiltered = []
|
||||
this.indexList = []
|
||||
|
||||
this.init()
|
||||
}
|
||||
|
||||
init () {
|
||||
this.createSortedAndFilteredList()
|
||||
this.createGroupedList()
|
||||
this.createIndexList()
|
||||
}
|
||||
|
||||
getComposerIndex (composer) {
|
||||
if (this.options.sort === 'Name') {
|
||||
return composer.name_sort.charAt(0).toUpperCase()
|
||||
}
|
||||
return composer.time_added.substring(0, 4)
|
||||
}
|
||||
|
||||
isComposerVisible (composer) {
|
||||
if (this.options.hideSingles && composer.track_count <= (composer.album_count * 2)) {
|
||||
return false
|
||||
}
|
||||
if (this.options.hideSpotify && composer.data_kind === 'spotify') {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
createIndexList () {
|
||||
this.indexList = [...new Set(this.sortedAndFiltered
|
||||
.map(composer => this.getComposerIndex(composer)))]
|
||||
}
|
||||
|
||||
createSortedAndFilteredList () {
|
||||
var composersSorted = this.items
|
||||
if (this.options.hideSingles || this.options.hideSpotify || this.options.hideOther) {
|
||||
composersSorted = composersSorted.filter(composer => this.isComposerVisible(composer))
|
||||
}
|
||||
if (this.options.sort === 'Recently added') {
|
||||
composersSorted = [...composersSorted].sort((a, b) => b.time_added.localeCompare(a.time_added))
|
||||
}
|
||||
this.sortedAndFiltered = composersSorted
|
||||
}
|
||||
|
||||
createGroupedList () {
|
||||
if (!this.options.group) {
|
||||
this.grouped = {}
|
||||
}
|
||||
this.grouped = this.sortedAndFiltered.reduce((r, composer) => {
|
||||
const idx = this.getComposerIndex(composer)
|
||||
r[idx] = [...r[idx] || [], composer]
|
||||
return r
|
||||
}, {})
|
||||
}
|
||||
}
|
101
web-src/src/pages/PageComposer.vue
Normal file
101
web-src/src/pages/PageComposer.vue
Normal file
@ -0,0 +1,101 @@
|
||||
<template>
|
||||
<div>
|
||||
<tabs-music></tabs-music>
|
||||
|
||||
<content-with-heading>
|
||||
<template slot="heading-left">
|
||||
<p class="title is-4">{{ name }}</p>
|
||||
</template>
|
||||
<template slot="heading-right">
|
||||
<div class="buttons is-centered">
|
||||
<a class="button is-small is-light is-rounded" @click="show_composer_details_modal = true">
|
||||
<span class="icon"><i class="mdi mdi-dots-horizontal mdi-18px"></i></span>
|
||||
</a>
|
||||
<a class="button is-small is-dark is-rounded" @click="play">
|
||||
<span class="icon"><i class="mdi mdi-shuffle"></i></span> <span>Shuffle</span>
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<p class="heading has-text-centered-mobile">{{ composer_albums.total }} albums | <a class="has-text-link" @click="open_tracks">tracks</a></p>
|
||||
<list-item-albums v-for="album in composer_albums.items" :key="album.id" :album="album" @click="open_album(album)">
|
||||
<template slot="actions">
|
||||
<a @click="open_dialog(album)">
|
||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
||||
</a>
|
||||
</template>
|
||||
</list-item-albums>
|
||||
<modal-dialog-album :show="show_details_modal" :album="selected_album" @close="show_details_modal = false" />
|
||||
<modal-dialog-composer :show="show_composer_details_modal" :composer="{ 'name': name }" @close="show_composer_details_modal = false" />
|
||||
</template>
|
||||
</content-with-heading>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import TabsMusic from '@/components/TabsMusic'
|
||||
import ListItemAlbums from '@/components/ListItemAlbum'
|
||||
import ModalDialogAlbum from '@/components/ModalDialogAlbum'
|
||||
import ModalDialogComposer from '@/components/ModalDialogComposer'
|
||||
import webapi from '@/webapi'
|
||||
|
||||
const composerData = {
|
||||
load: function (to) {
|
||||
return webapi.library_composer(to.params.composer, 'albums')
|
||||
},
|
||||
|
||||
set: function (vm, response) {
|
||||
vm.name = vm.$route.params.composer
|
||||
vm.composer_albums = response.data.albums
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'PageComposer',
|
||||
mixins: [LoadDataBeforeEnterMixin(composerData)],
|
||||
components: { ContentWithHeading, TabsMusic, ListItemAlbums, ModalDialogAlbum, ModalDialogComposer },
|
||||
|
||||
data () {
|
||||
return {
|
||||
name: '',
|
||||
composer_albums: { items: [] },
|
||||
show_details_modal: false,
|
||||
selected_album: {},
|
||||
|
||||
show_composer_details_modal: false
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
index_list () {
|
||||
return [...new Set(this.composer_albums.items
|
||||
.map(album => album.name_sort.charAt(0).toUpperCase()))]
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
open_tracks: function () {
|
||||
this.show_details_modal = false
|
||||
this.$router.push({ name: 'ComposerTracks', params: { composer: this.name } })
|
||||
},
|
||||
|
||||
play: function () {
|
||||
webapi.player_play_expression('composer is "' + this.name + '" and media_kind is music', true)
|
||||
},
|
||||
|
||||
open_album: function (album) {
|
||||
this.$router.push({ path: '/music/albums/' + album.id })
|
||||
},
|
||||
|
||||
open_dialog: function (album) {
|
||||
this.selected_album = album
|
||||
this.show_details_modal = true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
116
web-src/src/pages/PageComposerTracks.vue
Normal file
116
web-src/src/pages/PageComposerTracks.vue
Normal file
@ -0,0 +1,116 @@
|
||||
<template>
|
||||
<div>
|
||||
<tabs-music></tabs-music>
|
||||
|
||||
<content-with-heading>
|
||||
<template slot="heading-left">
|
||||
<p class="title is-4">{{ composer }}</p>
|
||||
<p class="heading">{{ tracks.total }} tracks</p>
|
||||
</template>
|
||||
<template slot="heading-right">
|
||||
<div class="buttons is-centered">
|
||||
<a class="button is-small is-light is-rounded" @click="show_composer_details_modal = true">
|
||||
<span class="icon"><i class="mdi mdi-dots-horizontal mdi-18px"></i></span>
|
||||
</a>
|
||||
<a class="button is-small is-dark is-rounded" @click="play">
|
||||
<span class="icon"><i class="mdi mdi-shuffle"></i></span> <span>Shuffle</span>
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<p class="heading has-text-centered-mobile"><a class="has-text-link" @click="open_albums">albums</a> | {{ tracks.total }} tracks</p>
|
||||
<list-item-track v-for="(track, index) in rated_tracks" :key="track.id" :track="track" @click="play_track(index)">
|
||||
<template slot="actions">
|
||||
<a @click="open_dialog(track)">
|
||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
||||
</a>
|
||||
</template>
|
||||
</list-item-track>
|
||||
<modal-dialog-track :show="show_details_modal" :track="selected_track" @close="show_details_modal = false" />
|
||||
<modal-dialog-composer :show="show_composer_details_modal" :composer="{ 'name': composer }" @close="show_composer_details_modal = false" />
|
||||
</template>
|
||||
</content-with-heading>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import TabsMusic from '@/components/TabsMusic'
|
||||
import ListItemTrack from '@/components/ListItemTrack'
|
||||
import ModalDialogTrack from '@/components/ModalDialogTrack'
|
||||
import ModalDialogComposer from '@/components/ModalDialogComposer'
|
||||
import webapi from '@/webapi'
|
||||
|
||||
const tracksData = {
|
||||
load: function (to) {
|
||||
return webapi.library_composer(to.params.composer, 'tracks')
|
||||
},
|
||||
|
||||
set: function (vm, response) {
|
||||
vm.composer = vm.$route.params.composer
|
||||
vm.tracks = response.data.tracks
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'PageComposerTracks',
|
||||
mixins: [LoadDataBeforeEnterMixin(tracksData)],
|
||||
components: { ContentWithHeading, TabsMusic, ListItemTrack, ModalDialogTrack, ModalDialogComposer },
|
||||
|
||||
data () {
|
||||
return {
|
||||
tracks: { items: [] },
|
||||
composer: '',
|
||||
|
||||
min_rating: 0,
|
||||
|
||||
show_details_modal: false,
|
||||
selected_track: {},
|
||||
|
||||
show_composer_details_modal: false
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
index_list () {
|
||||
return [...new Set(this.tracks.items
|
||||
.map(track => track.title_sort.charAt(0).toUpperCase()))]
|
||||
},
|
||||
|
||||
rated_tracks () {
|
||||
return this.tracks.items.filter(track => track.rating >= this.min_rating)
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
open_albums: function () {
|
||||
this.show_details_modal = false
|
||||
this.$router.push({ name: 'ComposerAlbums', params: { composer: this.composer } })
|
||||
},
|
||||
|
||||
play: function () {
|
||||
webapi.player_play_expression('composer is "' + this.composer + '" and media_kind is music', true)
|
||||
},
|
||||
|
||||
play_track: function (position) {
|
||||
webapi.player_play_expression('composer is "' + this.composer + '" and media_kind is music', false, position)
|
||||
},
|
||||
|
||||
show_rating: function (rating) {
|
||||
if (rating === 0.5) {
|
||||
rating = 0
|
||||
}
|
||||
this.min_rating = Math.ceil(rating) * 20
|
||||
},
|
||||
|
||||
open_dialog: function (track) {
|
||||
this.selected_track = track
|
||||
this.show_details_modal = true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
88
web-src/src/pages/PageComposers.vue
Normal file
88
web-src/src/pages/PageComposers.vue
Normal file
@ -0,0 +1,88 @@
|
||||
<template>
|
||||
<div>
|
||||
<tabs-music></tabs-music>
|
||||
|
||||
<content-with-heading>
|
||||
<template slot="heading-left">
|
||||
<p class="title is-4">{{ heading }}</p>
|
||||
<p class="heading">{{ composers.total }} composers</p>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<list-item-composer v-for="composer in composers.items" :key="composer.name" :composer="composer" @click="open_composer(composer)">
|
||||
<template slot="actions">
|
||||
<a @click="open_dialog(composer)">
|
||||
<span class="icon has-text-dark"><i class="mdi mdi-dots-vertical mdi-18px"></i></span>
|
||||
</a>
|
||||
</template>
|
||||
</list-item-composer>
|
||||
<modal-dialog-composer :show="show_details_modal" :composer="selected_composer" @close="show_details_modal = false" />
|
||||
</template>
|
||||
</content-with-heading>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { LoadDataBeforeEnterMixin } from './mixin'
|
||||
import ContentWithHeading from '@/templates/ContentWithHeading'
|
||||
import TabsMusic from '@/components/TabsMusic'
|
||||
import ListItemComposer from '@/components/ListItemComposer'
|
||||
import ModalDialogComposer from '@/components/ModalDialogComposer'
|
||||
import webapi from '@/webapi'
|
||||
|
||||
const composersData = {
|
||||
load: function (to) {
|
||||
if (to.params.genre) {
|
||||
return webapi.library_genre_composers(to.params.genre)
|
||||
} else {
|
||||
return webapi.library_composers()
|
||||
}
|
||||
},
|
||||
|
||||
set: function (vm, response) {
|
||||
if (response.data.composers) {
|
||||
vm.composers = response.data.composers
|
||||
vm.heading = vm.$route.params.genre
|
||||
} else {
|
||||
vm.composers = response.data
|
||||
vm.heading = 'Composers'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'PageComposers',
|
||||
mixins: [LoadDataBeforeEnterMixin(composersData)],
|
||||
components: { ContentWithHeading, TabsMusic, ListItemComposer, ModalDialogComposer },
|
||||
|
||||
data () {
|
||||
return {
|
||||
composers: { items: [] },
|
||||
heading: '',
|
||||
|
||||
show_details_modal: false,
|
||||
selected_composer: {}
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
index_list () {
|
||||
return [...new Set(this.composers.items
|
||||
.map(composer => composer.name.charAt(0).toUpperCase()))]
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
open_composer: function (composer) {
|
||||
this.$router.push({ name: 'ComposerAlbums', params: { composer: composer.name } })
|
||||
},
|
||||
|
||||
open_dialog: function (composer) {
|
||||
this.selected_composer = composer
|
||||
this.show_details_modal = true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
@ -94,6 +94,28 @@
|
||||
</template>
|
||||
</content-text>
|
||||
|
||||
<!-- Composers -->
|
||||
<content-with-heading v-if="show_composers && composers.total">
|
||||
<template slot="heading-left">
|
||||
<p class="title is-4">Composers</p>
|
||||
</template>
|
||||
<template slot="content">
|
||||
<list-composers :composers="composers.items"></list-composers>
|
||||
</template>
|
||||
<template slot="footer">
|
||||
<nav v-if="show_all_composers_button" class="level">
|
||||
<p class="level-item">
|
||||
<a class="button is-light is-small is-rounded" v-on:click="open_search_composers">Show all {{ composers.total }} composers</a>
|
||||
</p>
|
||||
</nav>
|
||||
</template>
|
||||
</content-with-heading>
|
||||
<content-text v-if="show_composers && !composers.total">
|
||||
<template slot="content">
|
||||
<p><i>No composers found</i></p>
|
||||
</template>
|
||||
</content-text>
|
||||
|
||||
<!-- Playlists -->
|
||||
<content-with-heading v-if="show_playlists && playlists.total">
|
||||
<template slot="heading-left">
|
||||
@ -169,13 +191,14 @@ import TabsSearch from '@/components/TabsSearch'
|
||||
import ListTracks from '@/components/ListTracks'
|
||||
import ListArtists from '@/components/ListArtists'
|
||||
import ListAlbums from '@/components/ListAlbums'
|
||||
import ListComposers from '@/components/ListComposers'
|
||||
import ListPlaylists from '@/components/ListPlaylists'
|
||||
import webapi from '@/webapi'
|
||||
import * as types from '@/store/mutation_types'
|
||||
|
||||
export default {
|
||||
name: 'PageSearch',
|
||||
components: { ContentWithHeading, ContentText, TabsSearch, ListTracks, ListArtists, ListAlbums, ListPlaylists },
|
||||
components: { ContentWithHeading, ContentText, TabsSearch, ListTracks, ListArtists, ListAlbums, ListPlaylists, ListComposers },
|
||||
|
||||
data () {
|
||||
return {
|
||||
@ -184,6 +207,7 @@ export default {
|
||||
tracks: { items: [], total: 0 },
|
||||
artists: { items: [], total: 0 },
|
||||
albums: { items: [], total: 0 },
|
||||
composers: { items: [], total: 0 },
|
||||
playlists: { items: [], total: 0 },
|
||||
audiobooks: { items: [], total: 0 },
|
||||
podcasts: { items: [], total: 0 }
|
||||
@ -216,6 +240,13 @@ export default {
|
||||
return this.albums.total > this.albums.items.length
|
||||
},
|
||||
|
||||
show_composers () {
|
||||
return this.$route.query.type && this.$route.query.type.includes('composer')
|
||||
},
|
||||
show_all_composers_button () {
|
||||
return this.composers.total > this.composers.items.length
|
||||
},
|
||||
|
||||
show_playlists () {
|
||||
return this.$route.query.type && this.$route.query.type.includes('playlist')
|
||||
},
|
||||
@ -282,6 +313,7 @@ export default {
|
||||
this.tracks = data.tracks ? data.tracks : { items: [], total: 0 }
|
||||
this.artists = data.artists ? data.artists : { items: [], total: 0 }
|
||||
this.albums = data.albums ? data.albums : { items: [], total: 0 }
|
||||
this.composers = data.composers ? data.composers : { items: [], total: 0 }
|
||||
this.playlists = data.playlists ? data.playlists : { items: [], total: 0 }
|
||||
})
|
||||
},
|
||||
@ -346,7 +378,7 @@ export default {
|
||||
this.$router.push({
|
||||
path: '/search/library',
|
||||
query: {
|
||||
type: 'track,artist,album,playlist,audiobook,podcast',
|
||||
type: 'track,artist,album,playlist,audiobook,podcast,composer',
|
||||
query: this.search_query,
|
||||
limit: 3,
|
||||
offset: 0
|
||||
@ -385,6 +417,16 @@ export default {
|
||||
})
|
||||
},
|
||||
|
||||
open_search_composers: function () {
|
||||
this.$router.push({
|
||||
path: '/search/library',
|
||||
query: {
|
||||
type: 'tracks',
|
||||
query: this.$route.query.query
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
open_search_playlists: function () {
|
||||
this.$router.push({
|
||||
path: '/search/library',
|
||||
@ -415,9 +457,42 @@ export default {
|
||||
})
|
||||
},
|
||||
|
||||
open_composer: function (composer) {
|
||||
this.$router.push({ name: 'ComposerAlbums', params: { composer: composer.name } })
|
||||
},
|
||||
|
||||
open_playlist: function (playlist) {
|
||||
this.$router.push({ path: '/playlists/' + playlist.id + '/tracks' })
|
||||
},
|
||||
|
||||
open_recent_search: function (query) {
|
||||
this.search_query = query
|
||||
this.new_search()
|
||||
},
|
||||
|
||||
open_track_dialog: function (track) {
|
||||
this.selected_track = track
|
||||
this.show_track_details_modal = true
|
||||
},
|
||||
|
||||
open_album_dialog: function (album) {
|
||||
this.selected_album = album
|
||||
this.show_album_details_modal = true
|
||||
},
|
||||
|
||||
open_artist_dialog: function (artist) {
|
||||
this.selected_artist = artist
|
||||
this.show_artist_details_modal = true
|
||||
},
|
||||
|
||||
open_composer_dialog: function (composer) {
|
||||
this.selected_composer = composer
|
||||
this.show_composer_details_modal = true
|
||||
},
|
||||
|
||||
open_playlist_dialog: function (playlist) {
|
||||
this.selected_playlist = playlist
|
||||
this.show_playlist_details_modal = true
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -15,6 +15,9 @@ import PageGenres from '@/pages/PageGenres'
|
||||
import PageGenre from '@/pages/PageGenre'
|
||||
import PageGenreTracks from '@/pages/PageGenreTracks'
|
||||
import PageArtistTracks from '@/pages/PageArtistTracks'
|
||||
import PageComposers from '@/pages/PageComposers'
|
||||
import PageComposer from '@/pages/PageComposer'
|
||||
import PageComposerTracks from '@/pages/PageComposerTracks'
|
||||
import PagePodcasts from '@/pages/PagePodcasts'
|
||||
import PagePodcast from '@/pages/PagePodcast'
|
||||
import PageAudiobooksAlbums from '@/pages/PageAudiobooksAlbums'
|
||||
@ -128,6 +131,24 @@ export const router = new VueRouter({
|
||||
component: PageGenreTracks,
|
||||
meta: { show_progress: true, has_index: true }
|
||||
},
|
||||
{
|
||||
path: '/music/composers',
|
||||
name: 'Composers',
|
||||
component: PageComposers,
|
||||
meta: { show_progress: true, has_tabs: true, has_index: true }
|
||||
},
|
||||
{
|
||||
path: '/music/composers/:composer/albums',
|
||||
name: 'ComposerAlbums',
|
||||
component: PageComposer,
|
||||
meta: { show_progress: true, has_tabs: true, has_index: true }
|
||||
},
|
||||
{
|
||||
path: '/music/composers/:composer/tracks',
|
||||
name: 'ComposerTracks',
|
||||
component: PageComposerTracks,
|
||||
meta: { show_progress: true, has_tabs: true, has_index: true }
|
||||
},
|
||||
{
|
||||
path: '/podcasts',
|
||||
name: 'Podcasts',
|
||||
|
@ -267,6 +267,21 @@ export default {
|
||||
})
|
||||
},
|
||||
|
||||
library_composers () {
|
||||
return axios.get('/api/library/composers')
|
||||
},
|
||||
|
||||
library_composer (composer, type) {
|
||||
var params = {
|
||||
type: type,
|
||||
media_kind: 'music',
|
||||
expression: 'composer is "' + composer + '"'
|
||||
}
|
||||
return axios.get('/api/search', {
|
||||
params: params
|
||||
})
|
||||
},
|
||||
|
||||
library_artist_tracks (artist) {
|
||||
if (artist) {
|
||||
const artistParams = {
|
||||
|
Loading…
Reference in New Issue
Block a user