[web-src] add composer support

This commit is contained in:
whatdoineed2do/Ray
2021-10-23 10:48:11 +01:00
parent 8c97ad591f
commit 269842d30a
11 changed files with 667 additions and 2 deletions

View 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>

View 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>

View 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>

View File

@@ -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
}
},