mirror of
https://github.com/koel/koel
synced 2024-11-28 15:00:42 +00:00
Display playlist meta
This commit is contained in:
parent
2aa2f5f27e
commit
eb9e9a2317
14 changed files with 365 additions and 264 deletions
|
@ -187,6 +187,7 @@
|
|||
* Load the Favorites view.
|
||||
*/
|
||||
loadFavorites() {
|
||||
this.$broadcast('favorites:load');
|
||||
this.loadMainView('favorites');
|
||||
},
|
||||
|
||||
|
|
|
@ -8,6 +8,12 @@
|
|||
<i class="fa fa-angle-up toggler"
|
||||
v-show="isPhone && showingControls"
|
||||
@click.prevent="showingControls = false"></i>
|
||||
|
||||
<span class="meta" v-show="meta.songCount">
|
||||
{{ meta.songCount }} song{{ meta.songCount === 1 ? '' : 's' }}
|
||||
•
|
||||
{{ meta.totalLength }}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<div class="buttons" v-show="!isPhone || showingControls">
|
||||
|
@ -50,16 +56,12 @@
|
|||
<script>
|
||||
import isMobile from 'ismobilejs';
|
||||
|
||||
import songList from '../../shared/song-list.vue';
|
||||
import favoriteStore from '../../../stores/favorite';
|
||||
import playback from '../../../services/playback';
|
||||
import shuffleSelectedMixin from '../../../mixins/shuffle-selected';
|
||||
import hasAddToMenuMixin from '../../../mixins/has-add-to-menu';
|
||||
import hasSongList from '../../../mixins/has-song-list';
|
||||
|
||||
export default {
|
||||
mixins: [shuffleSelectedMixin, hasAddToMenuMixin],
|
||||
|
||||
components: { songList },
|
||||
mixins: [hasSongList],
|
||||
|
||||
data () {
|
||||
return {
|
||||
|
|
|
@ -88,10 +88,10 @@
|
|||
|
||||
h1.heading {
|
||||
font-weight: $fontWeight_UltraThin;
|
||||
font-size: 48px;
|
||||
font-size: 36px;
|
||||
padding: 12px 24px;
|
||||
border-bottom: 1px solid $color2ndBgr;
|
||||
min-height: 90px;
|
||||
min-height: 96px;
|
||||
position: relative;
|
||||
align-items: center;
|
||||
align-content: stretch;
|
||||
|
@ -101,6 +101,13 @@
|
|||
flex: 1;
|
||||
}
|
||||
|
||||
.meta {
|
||||
display: block;
|
||||
opacity: .5;
|
||||
font-size: $fontSize;
|
||||
margin: 12px 0 0 2px;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
text-align: right;
|
||||
z-index: 2;
|
||||
|
@ -161,6 +168,10 @@
|
|||
color: $colorHighlight;
|
||||
}
|
||||
|
||||
.meta {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.buttons, input[type="search"] {
|
||||
justify-content: center;
|
||||
margin-top: 8px;
|
||||
|
|
|
@ -8,6 +8,12 @@
|
|||
<i class="fa fa-angle-up toggler"
|
||||
v-show="isPhone && showingControls"
|
||||
@click.prevent="showingControls = false"></i>
|
||||
|
||||
<span class="meta" v-show="meta.songCount">
|
||||
{{ meta.songCount }} song{{ meta.songCount === 1 ? '' : 's' }}
|
||||
•
|
||||
{{ meta.totalLength }}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<div class="buttons" v-show="!isPhone || showingControls">
|
||||
|
@ -53,16 +59,12 @@
|
|||
<script>
|
||||
import isMobile from 'ismobilejs';
|
||||
|
||||
import songList from '../../shared/song-list.vue';
|
||||
import playlistStore from '../../../stores/playlist';
|
||||
import playback from '../../../services/playback';
|
||||
import shuffleSelectedMixin from '../../../mixins/shuffle-selected';
|
||||
import hasAddToMenuMixin from '../../../mixins/has-add-to-menu';
|
||||
import hasSongList from '../../../mixins/has-song-list';
|
||||
|
||||
export default {
|
||||
mixins: [shuffleSelectedMixin, hasAddToMenuMixin],
|
||||
|
||||
components: { songList },
|
||||
mixins: [hasSongList],
|
||||
|
||||
data() {
|
||||
return {
|
||||
|
|
|
@ -8,6 +8,12 @@
|
|||
<i class="fa fa-angle-up toggler"
|
||||
v-show="isPhone && showingControls"
|
||||
@click.prevent="showingControls = false"></i>
|
||||
|
||||
<span class="meta" v-show="meta.songCount">
|
||||
{{ meta.songCount }} song{{ meta.songCount === 1 ? '' : 's' }}
|
||||
•
|
||||
{{ meta.totalLength }}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<div class="buttons" v-show="!isPhone || showingControls">
|
||||
|
@ -55,18 +61,14 @@
|
|||
import _ from 'lodash';
|
||||
import isMobile from 'ismobilejs';
|
||||
|
||||
import songList from '../../shared/song-list.vue';
|
||||
import playlistStore from '../../../stores/playlist';
|
||||
import queueStore from '../../../stores/queue';
|
||||
import songStore from '../../../stores/song';
|
||||
import playback from '../../../services/playback';
|
||||
import shuffleSelectedMixin from '../../../mixins/shuffle-selected';
|
||||
import hasAddToMenuMixin from '../../../mixins/has-add-to-menu';
|
||||
import hasSongList from '../../../mixins/has-song-list';
|
||||
|
||||
export default {
|
||||
mixins: [shuffleSelectedMixin, hasAddToMenuMixin],
|
||||
|
||||
components: { songList },
|
||||
mixins: [hasSongList],
|
||||
|
||||
data() {
|
||||
return {
|
||||
|
|
|
@ -8,6 +8,12 @@
|
|||
<i class="fa fa-angle-up toggler"
|
||||
v-show="isPhone && showingControls"
|
||||
@click.prevent="showingControls = false"></i>
|
||||
|
||||
<span class="meta" v-show="meta.songCount">
|
||||
{{ meta.songCount }} song{{ meta.songCount === 1 ? '' : 's' }}
|
||||
•
|
||||
{{ meta.totalLength }}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<div class="buttons" v-show="!isPhone || showingControls">
|
||||
|
@ -44,16 +50,12 @@
|
|||
<script>
|
||||
import isMobile from 'ismobilejs';
|
||||
|
||||
import songList from '../../shared/song-list.vue';
|
||||
import songStore from '../../../stores/song';
|
||||
import playback from '../../../services/playback';
|
||||
import shuffleSelectedMixin from '../../../mixins/shuffle-selected';
|
||||
import hasAddToMenuMixin from '../../../mixins/has-add-to-menu';
|
||||
import hasSongList from '../../../mixins/has-song-list';
|
||||
|
||||
export default {
|
||||
mixins: [shuffleSelectedMixin, hasAddToMenuMixin],
|
||||
|
||||
components: { songList },
|
||||
mixins: [hasSongList],
|
||||
|
||||
data() {
|
||||
return {
|
||||
|
|
|
@ -0,0 +1,216 @@
|
|||
<template>
|
||||
<li @dblclick.prevent="edit" class="playlist" :class="[type, editing ? 'editing' : '']">
|
||||
<a @click.prevent="load"
|
||||
@dragleave="removeDroppableState"
|
||||
@dragover.prevent="allowDrop"
|
||||
@drop.stop="handleDrop($event)"
|
||||
:class="{ 'active': active }"
|
||||
>{{ playlist.name }}</a>
|
||||
|
||||
<input type="text"
|
||||
@keyup.esc="cancelEdit"
|
||||
@keyup.enter="update"
|
||||
@blur="update"
|
||||
v-model="playlist.name"
|
||||
v-koel-focus="editing"
|
||||
required
|
||||
>
|
||||
</li>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import $ from 'jquery';
|
||||
|
||||
import songStore from '../../../stores/song';
|
||||
import playlistStore from '../../../stores/playlist';
|
||||
import favoriteStore from '../../../stores/favorite';
|
||||
|
||||
export default {
|
||||
props: ['playlist', 'type'],
|
||||
|
||||
data() {
|
||||
return {
|
||||
newName: '',
|
||||
editing: false,
|
||||
active: false,
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
/**
|
||||
* Determine if the current menu item is the "Favorites" one.
|
||||
*
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isFavorites() {
|
||||
return this.type === 'favorites';
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
/**
|
||||
* Load songs in the current playlist.
|
||||
*/
|
||||
load() {
|
||||
if (this.isFavorites) {
|
||||
this.$root.loadFavorites();
|
||||
} else {
|
||||
this.$root.loadPlaylist(this.playlist);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Show the form to edit the playlist.
|
||||
*/
|
||||
edit() {
|
||||
if (this.isFavorites) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.beforeEditCache = this.playlist.name;
|
||||
this.editing = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the playlist's name.
|
||||
*/
|
||||
update() {
|
||||
if (this.isFavorites || !this.editing) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.editing = false;
|
||||
|
||||
this.playlist.name = this.playlist.name.trim();
|
||||
if (!this.playlist.name) {
|
||||
this.playlist.name = this.beforeEditCache;
|
||||
return;
|
||||
}
|
||||
|
||||
playlistStore.update(this.playlist);
|
||||
},
|
||||
|
||||
/**
|
||||
* Cancel editing.
|
||||
*/
|
||||
cancelEdit() {
|
||||
this.editing = false;
|
||||
this.playlist.name = this.beforeEditCache;
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove the droppable state when a dragleave event occurs on the playlist's DOM element.
|
||||
*
|
||||
* @param {Object} e The dragleave event.
|
||||
*/
|
||||
removeDroppableState(e) {
|
||||
$(e.target).removeClass('droppable');
|
||||
},
|
||||
|
||||
/**
|
||||
* Add a "droppable" class and set the drop effect when an item is dragged over the playlist's
|
||||
* DOM element.
|
||||
*
|
||||
* @param object e The dragover event.
|
||||
*/
|
||||
allowDrop(e) {
|
||||
$(e.target).addClass('droppable');
|
||||
e.dataTransfer.dropEffect = 'move';
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle songs dropped to our favorite or playlist menu item.
|
||||
*
|
||||
* @param {Object} e The event
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
handleDrop(e) {
|
||||
this.removeDroppableState(e);
|
||||
|
||||
if (!e.dataTransfer.getData('text/plain')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var songs = songStore.byIds(e.dataTransfer.getData('text/plain').split(','));
|
||||
|
||||
if (!songs.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.type === 'favorites') {
|
||||
favoriteStore.like(songs);
|
||||
} else {
|
||||
playlistStore.addSongs(this.playlist, songs);
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
},
|
||||
|
||||
events: {
|
||||
/**
|
||||
* Listen to 'playlist:load' event to determine if the current item should be highlighted.
|
||||
*
|
||||
* @param {Object} playlist The playlist being loaded.
|
||||
*/
|
||||
'playlist:load': function (playlist) {
|
||||
this.active = this.playlist === playlist;
|
||||
},
|
||||
|
||||
/**
|
||||
* Listen to 'favorites:load' event to highlight the Favorites item if need be.
|
||||
*/
|
||||
'favorites:load': function () {
|
||||
this.active = this.isFavorites;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="sass" scoped>
|
||||
@import "resources/assets/sass/partials/_vars.scss";
|
||||
@import "resources/assets/sass/partials/_mixins.scss";
|
||||
|
||||
.playlist {
|
||||
user-select: none;
|
||||
|
||||
a {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
span {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: "\f0f6";
|
||||
}
|
||||
}
|
||||
|
||||
&.favorites a::before {
|
||||
content: "\f004";
|
||||
color: $colorHeart;
|
||||
}
|
||||
|
||||
input {
|
||||
display: none;
|
||||
|
||||
width: calc(100% - 32px);
|
||||
margin: 5px 16px;
|
||||
}
|
||||
|
||||
&.editing {
|
||||
a {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
input {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -13,80 +13,38 @@
|
|||
</form>
|
||||
|
||||
<ul class="menu">
|
||||
<li>
|
||||
<a class="favorites"
|
||||
@click.prevent="loadFavorites"
|
||||
:class="[currentView == 'favorites' ? 'active' : '']"
|
||||
@dragleave="removeDroppableState"
|
||||
@dragover.prevent="allowDrop"
|
||||
@drop.stop="handleDrop(null, $event)">Favorites</a>
|
||||
</li>
|
||||
|
||||
<li v-for="p in state.playlists"
|
||||
@dblclick.prevent="edit(p)"
|
||||
class="playlist"
|
||||
:class="{ editing: p == editedPlaylist }"
|
||||
>
|
||||
<a @click.prevent="load(p)"
|
||||
@dragleave="removeDroppableState"
|
||||
@dragover.prevent="allowDrop"
|
||||
@drop.stop="handleDrop(p, $event)"
|
||||
|
||||
:class="[(currentView == 'playlist' && currentPlaylist == p) ? 'active' : '']"
|
||||
>
|
||||
{{ p.name }}
|
||||
</a>
|
||||
|
||||
<input type="text"
|
||||
@keyup.esc="cancelEdit(p)"
|
||||
@keyup.enter="update(p)"
|
||||
@blur="update(p)"
|
||||
v-model="p.name"
|
||||
v-koel-focus="p == editedPlaylist"
|
||||
required
|
||||
>
|
||||
</li>
|
||||
<playlist-item
|
||||
type="favorites"
|
||||
:playlist="{ name: 'Favorites', songs: favoriteState.songs }"></playlist-item>
|
||||
<playlist-item
|
||||
v-for="playlist in playlistState.playlists"
|
||||
type="playlist"
|
||||
:playlist="playlist"></playlist-item>
|
||||
</ul>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import songStore from '../../../stores/song';
|
||||
import playlistStore from '../../../stores/playlist';
|
||||
import favoriteStore from '../../../stores/favorite';
|
||||
import $ from 'jquery';
|
||||
|
||||
import playlistItem from './playlist-item.vue';
|
||||
|
||||
export default {
|
||||
props: ['currentView'],
|
||||
|
||||
components: { playlistItem },
|
||||
|
||||
data() {
|
||||
return {
|
||||
state: playlistStore.state,
|
||||
playlistState: playlistStore.state,
|
||||
favoriteState: favoriteStore.state,
|
||||
creating: false,
|
||||
newName: '',
|
||||
currentPlaylist: null,
|
||||
editedPlaylist: playlistStore.stub,
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
/**
|
||||
* Load a playlist.
|
||||
*
|
||||
* @param {Object} p The playlist.
|
||||
*/
|
||||
load(p) {
|
||||
this.$root.loadPlaylist(p);
|
||||
},
|
||||
|
||||
/**
|
||||
* Load the Favorite playlist.
|
||||
*/
|
||||
loadFavorites() {
|
||||
this.currentPlaylist = null;
|
||||
this.$root.loadFavorites();
|
||||
},
|
||||
|
||||
/**
|
||||
* Store/create a new playlist.
|
||||
*/
|
||||
|
@ -95,101 +53,6 @@
|
|||
|
||||
playlistStore.store(this.newName, [], () => this.newName = '');
|
||||
},
|
||||
|
||||
/**
|
||||
* Show the form to edit a playlist.
|
||||
*
|
||||
* @param {Object} p The playlist
|
||||
*/
|
||||
edit(p) {
|
||||
this.beforeEditCache = p.name;
|
||||
this.editedPlaylist = p;
|
||||
},
|
||||
|
||||
/**
|
||||
* Update a playlist's name.
|
||||
*
|
||||
* @param {Object} p The playlist
|
||||
*/
|
||||
update(p) {
|
||||
if (!this.editedPlaylist) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.editedPlaylist = null;
|
||||
|
||||
p.name = p.name.trim();
|
||||
if (!p.name) {
|
||||
p.name = this.beforeEditCache;
|
||||
return;
|
||||
}
|
||||
|
||||
playlistStore.update(p);
|
||||
},
|
||||
|
||||
/**
|
||||
* Cancel editing the currently edited playlist.
|
||||
*
|
||||
* @param {Object} p The playlist.
|
||||
*/
|
||||
cancelEdit(p) {
|
||||
this.editedPlaylist = null;
|
||||
p.name = this.beforeEditCache;
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove the droppable state when a dragleave event occurs on the playlist's DOM element.
|
||||
*
|
||||
* @param {Object} e The dragleave event.
|
||||
*/
|
||||
removeDroppableState(e) {
|
||||
$(e.target).removeClass('droppable');
|
||||
},
|
||||
|
||||
/**
|
||||
* Add a "droppable" class and set the drop effect when an item is dragged over the playlist's
|
||||
* DOM element.
|
||||
*
|
||||
* @param object e The dragover event.
|
||||
*/
|
||||
allowDrop(e) {
|
||||
$(e.target).addClass('droppable');
|
||||
e.dataTransfer.dropEffect = 'move';
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle songs dropped to our favorite or playlist menu item.
|
||||
*
|
||||
* @param {?Object} playlist The playlist object, or null if dropping to Favorites.
|
||||
* @param {Object} e The event
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
handleDrop(playlist, e) {
|
||||
this.removeDroppableState(e);
|
||||
|
||||
var songs = songStore.byIds(e.dataTransfer.getData('text/plain').split(','));
|
||||
|
||||
if (!songs.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (playlist) {
|
||||
playlistStore.addSongs(playlist, songs);
|
||||
} else {
|
||||
favoriteStore.like(songs);
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
},
|
||||
|
||||
events: {
|
||||
'playlist:load': function (p) {
|
||||
this.currentPlaylist = p;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -199,44 +62,6 @@
|
|||
@import "resources/assets/sass/partials/_mixins.scss";
|
||||
|
||||
#playlists {
|
||||
.menu {
|
||||
a {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
a::before {
|
||||
content: "\f0f6";
|
||||
}
|
||||
|
||||
a.favorites::before {
|
||||
content: "\f004";
|
||||
color: $colorHeart;
|
||||
}
|
||||
|
||||
.playlist {
|
||||
user-select: none;
|
||||
|
||||
input {
|
||||
display: none;
|
||||
|
||||
width: calc(100% - 32px);
|
||||
margin: 5px 16px;
|
||||
}
|
||||
|
||||
&.editing {
|
||||
a {
|
||||
display: none;
|
||||
}
|
||||
|
||||
input {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
form.create {
|
||||
padding: 8px 16px;
|
||||
|
||||
|
|
|
@ -79,12 +79,19 @@
|
|||
|
||||
watch: {
|
||||
/**
|
||||
* Watch the items, so that we can always make sure a queue is not sorted in any ways.
|
||||
* Watch the items.
|
||||
*/
|
||||
items() {
|
||||
// Make sure a queue is not sorted in any ways.
|
||||
if (this.type === 'queue') {
|
||||
this.sortKey = '';
|
||||
}
|
||||
|
||||
// Dispatch this event for the parent to update the song count and duration status.
|
||||
this.$dispatch('songlist:changed', {
|
||||
songCount: this.items.length,
|
||||
totalLength: songStore.getLength(this.items, true),
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
/**
|
||||
* Add necessary functionalities into a (song-list type) component.
|
||||
*/
|
||||
|
||||
import addToMenu from '../components/shared/add-to-menu.vue';
|
||||
|
||||
export default {
|
||||
components: { addToMenu },
|
||||
|
||||
data() {
|
||||
return {
|
||||
showingAddToMenu: false,
|
||||
};
|
||||
},
|
||||
|
||||
events: {
|
||||
'add-to-menu:close': function () {
|
||||
this.showingAddToMenu = false;
|
||||
},
|
||||
},
|
||||
};
|
62
resources/assets/js/mixins/has-song-list.js
Normal file
62
resources/assets/js/mixins/has-song-list.js
Normal file
|
@ -0,0 +1,62 @@
|
|||
/**
|
||||
* Add necessary functionalities into a view that contains a song-list component.
|
||||
*/
|
||||
|
||||
import _ from 'lodash';
|
||||
|
||||
import playback from '../services/playback';
|
||||
import addToMenu from '../components/shared/add-to-menu.vue';
|
||||
import songList from '../components/shared/song-list.vue';
|
||||
|
||||
export default {
|
||||
components: { addToMenu, songList },
|
||||
|
||||
data() {
|
||||
return {
|
||||
/**
|
||||
* Whether or not to show the "Add To" button in the header.
|
||||
*
|
||||
* @type {Boolean}
|
||||
*/
|
||||
showingAddToMenu: false,
|
||||
|
||||
/**
|
||||
* An array of selected songs in the list.
|
||||
*
|
||||
* @type {Array}
|
||||
*/
|
||||
selectedSongs: [],
|
||||
|
||||
meta: {
|
||||
songCount: 0,
|
||||
totalLength: '00:00',
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
/**
|
||||
* Shuffles the currently selected songs.
|
||||
*/
|
||||
shuffleSelected() {
|
||||
if (this.selectedSongs.length < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
playback.queueAndPlay(this.selectedSongs, true);
|
||||
},
|
||||
},
|
||||
|
||||
events: {
|
||||
/**
|
||||
* Listen to add-to-menu:close event to set showingAddToMenu to false (and subsequently close the menu).
|
||||
*/
|
||||
'add-to-menu:close': function () {
|
||||
this.showingAddToMenu = false;
|
||||
},
|
||||
|
||||
'songlist:changed': function (meta) {
|
||||
this.meta = _.assign(this.meta, meta);
|
||||
},
|
||||
},
|
||||
};
|
|
@ -1,28 +0,0 @@
|
|||
import $ from 'jquery';
|
||||
|
||||
import playback from '../services/playback';
|
||||
|
||||
/**
|
||||
* Add a "shuffle selected" functionality to any component containing a song-list component using this mixin.
|
||||
* Such a component should:
|
||||
* - pass "selectedSongs" SYNC prop into the song-list component, e.g.
|
||||
* <song-list :items="state.songs" :selected-songs.sync="selectedSongs" type="queue"></song-list>
|
||||
* - trigger shuffling with shuffleSelected() method
|
||||
*/
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
selectedSongs: [],
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
shuffleSelected() {
|
||||
if (this.selectedSongs.length < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
playback.queueAndPlay(this.selectedSongs, true);
|
||||
},
|
||||
},
|
||||
};
|
|
@ -6,6 +6,8 @@ import utils from '../services/utils';
|
|||
export default {
|
||||
state: {
|
||||
songs: [],
|
||||
length: 0,
|
||||
fmtLength: '',
|
||||
},
|
||||
|
||||
all() {
|
||||
|
|
|
@ -58,6 +58,24 @@ export default {
|
|||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the total duration of some songs.
|
||||
*
|
||||
* @param {Array} songs
|
||||
* @param {boolean} toHis Wheter to convert the duration into H:i:s format
|
||||
*
|
||||
* @return {float|string}
|
||||
*/
|
||||
getLength(songs, toHis) {
|
||||
var duration = _.reduce(songs, (length, song) => length + song.length, 0);
|
||||
|
||||
if (toHis) {
|
||||
return utils.secondsToHis(duration);
|
||||
}
|
||||
|
||||
return duration;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get all songs.
|
||||
*
|
||||
|
|
Loading…
Reference in a new issue