diff --git a/platypush/backend/http/static/css/source/webpanel/plugins/music.mpd/index.scss b/platypush/backend/http/static/css/source/webpanel/plugins/music.mpd/index.scss
index 7216a9f1..7288e5c8 100644
--- a/platypush/backend/http/static/css/source/webpanel/plugins/music.mpd/index.scss
+++ b/platypush/backend/http/static/css/source/webpanel/plugins/music.mpd/index.scss
@@ -130,6 +130,12 @@
}
.item {
+ .empty {
+ font-size: 1em;
+ display: block;
+ height: auto;
+ }
+
&.active {
height: 4rem;
@include animation(active-track 5s infinite);
@@ -144,6 +150,24 @@
}
}
+ .playlist-add {
+ .playlist-add-controls {
+ background: $playlist-controls-bg;
+ border-bottom: $playlist-controls-border;
+ border-radius: 0;
+ box-shadow: $filter-bar-shadow;
+ margin: -2.5rem -2rem 0 -2rem;
+ padding: .5rem;
+ }
+
+ .playlists-container {
+ max-height: 70vh;
+ overflow: auto;
+ margin: 0 -2rem;
+ padding: 1rem;
+ }
+ }
+
.controls {
@extend .vertical-center;
position: fixed;
@@ -296,10 +320,14 @@
position: relative;
}
}
-}
-.dropdown {
- width: 20rem;
+ .dropdown {
+ width: 20rem;
+ }
+
+ .filter-container {
+ position: relative;
+ }
}
#music-mpd-info {
@@ -333,6 +361,12 @@
}
}
+#music-mpd-playlist-add {
+ .modal {
+ min-width: 50rem;
+ }
+}
+
@media #{map-get($widths, 's')} {
#music-mpd-info {
.modal {
diff --git a/platypush/backend/http/static/css/source/webpanel/plugins/music.mpd/vars.scss b/platypush/backend/http/static/css/source/webpanel/plugins/music.mpd/vars.scss
index 46364945..3424a028 100644
--- a/platypush/backend/http/static/css/source/webpanel/plugins/music.mpd/vars.scss
+++ b/platypush/backend/http/static/css/source/webpanel/plugins/music.mpd/vars.scss
@@ -26,4 +26,5 @@ $search-modal-footer-border: 1px solid #ccc;
$info-modal-row-border: 1px solid #ddd;
$info-modal-attr-color: #777;
$track-info-hover-color: rgb(46,190,110);
+$filter-bar-shadow: 0 2.5px 4px 0 #bbb;
diff --git a/platypush/backend/http/static/js/plugins/music.mpd/index.js b/platypush/backend/http/static/js/plugins/music.mpd/index.js
index 9d037794..910f24c4 100644
--- a/platypush/backend/http/static/js/plugins/music.mpd/index.js
+++ b/platypush/backend/http/static/js/plugins/music.mpd/index.js
@@ -8,8 +8,10 @@ Vue.component('music-mpd', {
status: {},
timer: null,
playlist: [],
+ playlists: [],
playlistFilter: '',
browserFilter: '',
+ playlistAddFilter: '',
browserPath: [],
browserItems: [],
@@ -26,9 +28,12 @@ Vue.component('music-mpd', {
infoItem: {},
modalVisible: {
info: false,
+ playlistAdd: false,
},
+ addToPlaylistItems: [],
selectedPlaylistItems: {},
+ selectedPlaylistAddItems: {},
selectedBrowserItems: {},
syncTime: {
@@ -84,6 +89,12 @@ Vue.component('music-mpd', {
items.push({
text: 'Add to playlist',
icon: 'list',
+ click: async function() {
+ self.addToPlaylistItems = Object.values(self.selectedPlaylistItems).map(_ => _.file);
+ self.selectedPlaylistItems = {};
+ self.modalVisible.playlistAdd = true;
+ await self.listplaylists();
+ },
});
if (Object.keys(this.selectedPlaylistItems).length < this.playlist.length) {
@@ -233,6 +244,21 @@ Vue.component('music-mpd', {
},
);
+ if (Object.values(this.selectedBrowserItems).filter(_ => _.type === 'file').length === Object.values(this.selectedBrowserItems).length) {
+ items.push(
+ {
+ text: 'Add to playlist',
+ icon: 'list',
+ click: async function() {
+ self.addToPlaylistItems = Object.keys(self.selectedBrowserItems);
+ self.modalVisible.playlistAdd = true;
+ await self.listplaylists();
+ self.selectedBrowserItems = {};
+ },
+ },
+ );
+ }
+
if (Object.keys(this.selectedBrowserItems).length === 1
&& Object.values(this.selectedBrowserItems)[0].type === 'playlist') {
items.push({
@@ -263,6 +289,9 @@ Vue.component('music-mpd', {
items.push({
text: 'View info',
icon: 'info',
+ click: async function() {
+ await self.info(Object.values(self.selectedBrowserItems)[0].name);
+ },
});
}
@@ -338,7 +367,7 @@ Vue.component('music-mpd', {
}
for (const [attr, value] of Object.entries(track)) {
- if (['id','pos','time'].indexOf(attr) >= 0) {
+ if (['id','pos','time','track','disc'].indexOf(attr) >= 0) {
Vue.set(this.track, attr, parseInt(value));
} else {
Vue.set(this.track, attr, value);
@@ -355,7 +384,7 @@ Vue.component('music-mpd', {
for (var track of playlist) {
for (const [attr, value] of Object.entries(track)) {
- if (['time','pos','id'].indexOf(attr) >= 0) {
+ if (['time','pos','id','track','disc'].indexOf(attr) >= 0) {
track[attr] = parseInt(value);
} else {
track[attr] = value;
@@ -599,7 +628,8 @@ Vue.component('music-mpd', {
var info = item;
if (typeof(item) === 'string') {
- item = await request('music.mpd.search', {filter: {file: info}});
+ var items = await request('music.mpd.search', {filter: ['file', info]});
+ item = items.length ? items[0] : {file: info};
}
this.infoItem = item;
@@ -630,6 +660,42 @@ Vue.component('music-mpd', {
await this.$refs.search.search();
},
+ listplaylists: async function() {
+ this.playlists = [];
+ let playlists = await request('music.mpd.listplaylists');
+
+ for (const p of playlists) {
+ this.playlists.push(p);
+ }
+ },
+
+ playlistadd: async function(items=[], playlists=[]) {
+ if (!playlists.length) {
+ if (this.modalVisible.playlistAdd) {
+ playlists = Object.keys(this.selectedPlaylistAddItems);
+ }
+ }
+
+ if (!items.length) {
+ items = this.addToPlaylistItems;
+ }
+
+ if (!items.length || !playlists.length) {
+ return;
+ }
+
+ var promises = [];
+ for (const playlist of playlists) {
+ promises.push(request('music.mpd.playlistadd', {
+ name: playlist, uri: items.map(_ => typeof(_) === 'object' ? _.file : _)
+ }));
+ }
+
+ await Promise.all(promises);
+ this.modalVisible.playlistAdd = false;
+ this.addToPlaylistItems = [];
+ },
+
onNewPlayingTrack: async function(event) {
var previousTrack = {
file: this.track.file,
@@ -777,7 +843,15 @@ Vue.component('music-mpd', {
return true;
const filter = this.browserFilter.toLocaleLowerCase().split(' ').filter(_ => _.length > 0).join(' ');
- return item.name.toLocaleLowerCase().indexOf(filter) >= 0;
+ return (item.artist || '').concat(item.name).toLocaleLowerCase().indexOf(filter) >= 0;
+ },
+
+ matchesPlaylistAddFilter: function(item) {
+ if (this.playlistAddFilter.length === 0)
+ return true;
+
+ const filter = this.playlistAddFilter.toLocaleLowerCase().split(' ').filter(_ => _.length > 0).join(' ');
+ return (item.playlist || '').toLocaleLowerCase().indexOf(filter) >= 0;
},
onPlaylistItemClick: async function(track) {
@@ -831,6 +905,14 @@ Vue.component('music-mpd', {
}
},
+ onPlaylistAddItemClick: function(playlist) {
+ if (playlist.playlist in this.selectedPlaylistAddItems) {
+ Vue.delete(this.selectedPlaylistAddItems, playlist.playlist);
+ } else {
+ Vue.set(this.selectedPlaylistAddItems, playlist.playlist, playlist);
+ }
+ },
+
togglePlaylistSelectionMode: function() {
if (this.selectionMode.playlist && Object.keys(this.selectedPlaylistItems).length) {
openDropdown(this.$refs.playlistDropdown.$el);
diff --git a/platypush/backend/http/static/js/plugins/music.mpd/search.js b/platypush/backend/http/static/js/plugins/music.mpd/search.js
index a81ba63e..9ec38c4e 100644
--- a/platypush/backend/http/static/js/plugins/music.mpd/search.js
+++ b/platypush/backend/http/static/js/plugins/music.mpd/search.js
@@ -66,6 +66,21 @@ Vue.component('music-mpd-search', {
},
);
+ if (Object.values(this.selectedItems).filter(_ => _.time != null).length === Object.values(this.selectedItems).length) {
+ items.push(
+ {
+ text: 'Add to playlist',
+ icon: 'list',
+ click: async function() {
+ self.mpd.addToPlaylistItems = Object.values(self.selectedItems).map(_ => _.file);
+ self.mpd.modalVisible.playlistAdd = true;
+ await self.mpd.listplaylists();
+ self.selectedItems = {};
+ },
+ },
+ );
+ }
+
if (Object.keys(this.selectedItems).length === 1) {
const item = Object.values(this.selectedItems)[0];
@@ -118,11 +133,17 @@ Vue.component('music-mpd-search', {
var results = await request('music.mpd.search', {filter: filter});
this.results = results.sort((a,b) => {
- const tokenize = (t) => {
- return ''.concat(t.artist || '', '-', t.album || '', '-', t.disc || '', '-', t.track || '', t.title || '').toLocaleLowerCase();
- };
-
- return tokenize(a).localeCompare(tokenize(b));
+ if (a.artist != b.artist)
+ return (a.artist || '').localeCompare(b.artist || '');
+ if (a.album != b.album)
+ return (a.album || '').localeCompare(b.album || '');
+ if (a.track != b.track)
+ return parseInt(a.track || 0) > parseInt(b.track || 0);
+ if (a.title != b.title)
+ return (a.title || '').localeCompare(b.title || '');
+ if (a.file != b.file)
+ return (a.file || '').localeCompare(b.file || '');
+ return 0;
});
this.showResults = true;
@@ -177,6 +198,16 @@ Vue.component('music-mpd-search', {
this.query[attr] = '';
}
},
+
+ resetForm: function() {
+ this.resetQuery();
+ this.showResults = false;
+ var self = this;
+
+ setTimeout(() => {
+ self.$refs.form.querySelector('input[type=text]:first-child').focus()
+ }, 100)
+ },
},
});
diff --git a/platypush/backend/http/templates/plugins/music.mpd/index.html b/platypush/backend/http/templates/plugins/music.mpd/index.html
index 5675c85c..d46192e2 100644
--- a/platypush/backend/http/templates/plugins/music.mpd/index.html
+++ b/platypush/backend/http/templates/plugins/music.mpd/index.html
@@ -55,6 +55,36 @@
+