forked from platypush/platypush
music.mpd plugin vue.js refactoring - WIP
This commit is contained in:
parent
19162a3b8d
commit
7a74b83c76
18 changed files with 724 additions and 128 deletions
|
@ -26,6 +26,7 @@ $widths: (
|
|||
float: left;
|
||||
box-sizing: border-box;
|
||||
width: ((100%/12)*$i);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@if $i < 12 {
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
bottom: 0;
|
||||
right: 0;
|
||||
width: 25em;
|
||||
z-index: 1000;
|
||||
|
||||
.notification {
|
||||
background: $notification-bg;
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
//// Common defaults
|
||||
$default-bg: #f4f5f6 !default;
|
||||
$default-bg: white !default;
|
||||
$default-bg-2: #f4f5f6 !default;
|
||||
$default-bg-3: #f1f3f2 !default;
|
||||
$default-bg-4: #edf0ee !default;
|
||||
$default-fg: black !default;
|
||||
$default-fg-2: #333333 !default;
|
||||
$default-font-size: 1.5rem !default;
|
||||
|
||||
$default-font-family: "Raleway", "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif !default;
|
||||
$default-border: 1px solid #e1e4e8 !default;
|
||||
|
@ -9,6 +13,7 @@ $default-border-2: 1px solid #dddddd !default;
|
|||
$default-bottom: $default-border !default;
|
||||
$default-link-fg: #5f7869 !default;
|
||||
|
||||
$font-size: $default-font-size !default;
|
||||
$selected-bg: #c8ffd0 !default;
|
||||
$hover-bg: #def6ea !default;
|
||||
$header-bg: $default_bg !default;
|
||||
|
@ -26,12 +31,12 @@ $fade-in-transition-duration: $fade-transition-duration !default;
|
|||
$fade-out-transition-duration: $fade-transition-duration !default;
|
||||
|
||||
//// Notifications
|
||||
$notification-bg: rgba(185, 255, 193, 0.85) !default;
|
||||
$notification-hover-bg: rgba(160,245,178,0.9) !default;
|
||||
$notification-warning-bg: rgba(228, 255, 78, 0.85) !default;
|
||||
$notification-warning-hover-bg: rgba(218, 245, 68, 0.9) !default;
|
||||
$notification-error-bg: rgba(255, 100, 100, 0.85) !default;
|
||||
$notification-error-hover-bg: rgba(245, 90, 90, 0.9) !default;
|
||||
$notification-bg: rgba(185, 255, 193, 0.9) !default;
|
||||
$notification-hover-bg: rgba(160,245,178,0.95) !default;
|
||||
$notification-warning-bg: rgba(228, 255, 78, 0.9) !default;
|
||||
$notification-warning-hover-bg: rgba(218, 245, 68, 0.95) !default;
|
||||
$notification-error-bg: rgba(255, 100, 100, 0.9) !default;
|
||||
$notification-error-hover-bg: rgba(245, 90, 90, 0.95) !default;
|
||||
$notification-border: 1px solid rgba(109, 205, 134, 0.62) !default;
|
||||
$notification-warning-border: 1px solid rgba(205, 205, 109, 0.62) !default;
|
||||
$notification-error-border: 1px solid rgba(205, 109, 109, 0.62) !default;
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
header {
|
||||
.row {
|
||||
width: 100%;
|
||||
background: $header-bg;
|
||||
padding: 1rem 2.5rem;
|
||||
// margin: 0 1rem 3.5rem -1rem;
|
||||
border-bottom: $header-bottom;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.logo {
|
||||
font-size: 25px;
|
||||
|
||||
.logo-1 {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.date-time {
|
||||
text-align: right;
|
||||
padding-right: 3rem;
|
||||
|
||||
.date {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.time {
|
||||
font-weight: bold;
|
||||
font-size: 25px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,27 +7,34 @@
|
|||
@import 'common/modal';
|
||||
@import 'common/notifications';
|
||||
|
||||
@import 'header';
|
||||
@import 'nav';
|
||||
|
||||
body {
|
||||
width: 100%;
|
||||
overflow-x: hidden;
|
||||
font-family: $default-font-family;
|
||||
font-size: $default-font-size;
|
||||
}
|
||||
|
||||
body::-webkit-scrollbar {
|
||||
width: 1em;
|
||||
}
|
||||
|
||||
body::-webkit-scrollbar-track {
|
||||
-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
|
||||
}
|
||||
|
||||
body::-webkit-scrollbar-thumb {
|
||||
background-color: darkgrey;
|
||||
outline: 1px solid slategrey;
|
||||
}
|
||||
|
||||
main {
|
||||
margin: 6rem auto;
|
||||
padding: 4.9rem 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
color: $default-link-fg;
|
||||
}
|
||||
|
||||
.plugin-container {
|
||||
border: $default-border-2;
|
||||
border-radius: 1rem;
|
||||
margin: 1.5rem;
|
||||
box-shadow: 8px 8px 6px -1px rgba(187,187,187,0.75);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ nav {
|
|||
top: 0;
|
||||
width: 100%;
|
||||
z-index: 10;
|
||||
opacity: 0.9;
|
||||
opacity: 0.95;
|
||||
|
||||
ul {
|
||||
position: relative;
|
||||
|
@ -23,9 +23,9 @@ nav {
|
|||
margin: 0;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .1rem;
|
||||
border-radius: 2rem;
|
||||
|
||||
&:hover {
|
||||
border-radius: 2rem;
|
||||
background: $hover-bg;
|
||||
letter-spacing: .4rem;
|
||||
}
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
font-weight: 400;
|
||||
line-height: 3.8rem;
|
||||
letter-spacing: .1rem;
|
||||
border-bottom: $default-border-2;
|
||||
border-radius: 0 0 1em 1em;
|
||||
|
||||
%panel {
|
||||
margin: 1.5rem auto;
|
||||
|
@ -28,7 +30,7 @@
|
|||
|
||||
.title {
|
||||
padding: .75rem;
|
||||
background: $default-bg;
|
||||
background: $default-bg-2;
|
||||
border-bottom: $default-border-2;
|
||||
|
||||
&:last-child {
|
||||
|
|
|
@ -0,0 +1,162 @@
|
|||
@import 'common/vars';
|
||||
@import 'common/layout';
|
||||
@import 'webpanel/plugins/music.mpd/vars';
|
||||
|
||||
// background-image: linear-gradient(to right bottom, rgb(123, 84, 30), rgb(0, 0, 0)), linear-gradient(transparent, rgb(0, 0, 0) 70%);
|
||||
|
||||
.music-mpd-container {
|
||||
line-height: 3rem;
|
||||
letter-spacing: .03rem;
|
||||
overflow: hidden;
|
||||
|
||||
* > .item {
|
||||
cursor: pointer;
|
||||
border-radius: 1rem;
|
||||
padding: .5rem;
|
||||
|
||||
&:nth-child(odd) { background: rgba(255, 255, 255, 0.0); }
|
||||
&:nth-child(even) { background: $default-bg-3; }
|
||||
|
||||
.artist {
|
||||
font-size: $artist-font-size;
|
||||
}
|
||||
}
|
||||
|
||||
* > .duration {
|
||||
color: $duration-color;
|
||||
font-size: $duration-font-size;
|
||||
}
|
||||
|
||||
.panels {
|
||||
display: flex;
|
||||
|
||||
.browser, .playlist {
|
||||
height: 100vh - 16rem;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.browser {
|
||||
background: $browser-panel-bg;
|
||||
border-right: $default-border-2;
|
||||
padding: .3rem 1rem 6rem 1rem;
|
||||
font-size: $browser-font-size;
|
||||
|
||||
.item {
|
||||
background: none;
|
||||
}
|
||||
|
||||
.fa {
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
.playlist {
|
||||
padding: .5rem 1rem 6rem 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.controls {
|
||||
@extend .vertical-center;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
min-height: 6rem;
|
||||
bottom: 0;
|
||||
border-top: $default-border-2;
|
||||
padding: 1rem;
|
||||
background: $control-panel-bg;
|
||||
box-shadow: $control-panel-shadow;
|
||||
z-index: 2;
|
||||
|
||||
.track-container {
|
||||
@extend .vertical-center;
|
||||
line-height: 2.6rem;
|
||||
|
||||
.track-info {
|
||||
.artist {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.playback-controls {
|
||||
.row {
|
||||
@extend .vertical-center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
* > button {
|
||||
border: 0;
|
||||
padding: 0 1.5rem;
|
||||
|
||||
&.enabled {
|
||||
color: $button-enabled-color;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.fa {
|
||||
color: $button-hover-color;
|
||||
}
|
||||
}
|
||||
|
||||
.fa-play, .fa-pause {
|
||||
color: $button-hover-color;
|
||||
font-size: $font-size * 2;
|
||||
margin-top: .3rem;
|
||||
|
||||
&:hover {
|
||||
color: $play-button-hover-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pull-right {
|
||||
padding-right: 2.5rem;
|
||||
button {
|
||||
&:not(last-child) {
|
||||
padding: 0 .7rem;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.volume-container {
|
||||
button {
|
||||
padding: 0 .3rem 0 0;
|
||||
background: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
* > .seek-slider {
|
||||
width: 75%;
|
||||
}
|
||||
|
||||
* > .volume-slider {
|
||||
width: 75%;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
* > .elapsed-time,
|
||||
* > .total-time {
|
||||
font-size: $control-time-font-size;
|
||||
color: $control-time-color;
|
||||
}
|
||||
|
||||
* > .elapsed-time {
|
||||
margin-right: 1.5rem;
|
||||
}
|
||||
|
||||
* > .total-time {
|
||||
margin-left: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
* > .item:hover {
|
||||
background: $hover-bg !important;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
$button-enabled-color: #59df3e;
|
||||
$button-hover-color: $button-enabled-color;
|
||||
$play-button-hover-color: #64ef4a;
|
||||
|
||||
$artist-font-size: $font-size * 0.9333;
|
||||
|
||||
$duration-color: #666;
|
||||
$duration-font-size: $font-size * 0.86666;
|
||||
|
||||
$control-panel-bg: rgba(245,245,245,0.95);
|
||||
$control-panel-shadow: 0 -2.5px 4px 0 #c0c0c0;
|
||||
$control-time-color: #666;
|
||||
$control-time-font-size: $font-size * 0.666666;
|
||||
|
||||
$browser-panel-bg: rgba(248,250,250,0.95);
|
||||
$browser-font-size: $font-size * 0.8666;
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
Vue.component('music-mpd-browser-item', {
|
||||
template: '#tmpl-music-mpd-browser-item',
|
||||
props: ['type','name'],
|
||||
|
||||
methods: {
|
||||
},
|
||||
});
|
||||
|
331
platypush/backend/http/static/js/plugins/music.mpd/index.js
Normal file
331
platypush/backend/http/static/js/plugins/music.mpd/index.js
Normal file
|
@ -0,0 +1,331 @@
|
|||
Vue.component('music-mpd', {
|
||||
template: '#tmpl-music-mpd',
|
||||
props: ['config'],
|
||||
data: function() {
|
||||
return {
|
||||
track: {},
|
||||
status: {},
|
||||
playlist: [],
|
||||
timer: null,
|
||||
browserPath: [],
|
||||
browserItems: [],
|
||||
syncTime: {
|
||||
timestamp: null,
|
||||
elapsed: null,
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
refresh: async function() {
|
||||
const getStatus = request('music.mpd.status');
|
||||
const getTrack = request('music.mpd.currentsong');
|
||||
const getPlaylist = request('music.mpd.playlistinfo');
|
||||
const getBrowserItems = request('music.mpd.lsinfo');
|
||||
|
||||
let [status, track, playlist, browserItems] = await Promise.all([getStatus, getTrack, getPlaylist, getBrowserItems]);
|
||||
|
||||
this._parseStatus(status);
|
||||
this._parseTrack(track);
|
||||
this._parsePlaylist(playlist);
|
||||
this._parseBrowserItems(browserItems);
|
||||
|
||||
if (this.status.state === 'play') {
|
||||
this.startTimer();
|
||||
}
|
||||
},
|
||||
|
||||
_parseStatus: async function(status) {
|
||||
if (!status || status.length === 0) {
|
||||
status = await request('music.mpd.status');
|
||||
}
|
||||
|
||||
for (const [attr, value] of Object.entries(status)) {
|
||||
if (['consume','random','repeat','single','bitrate'].indexOf(attr) >= 0) {
|
||||
Vue.set(this.status, attr, !!parseInt(value));
|
||||
} else if (['nextsong','nextsongid','playlist','playlistlength',
|
||||
'volume','xfade','song','songid'].indexOf(attr) >= 0) {
|
||||
Vue.set(this.status, attr, parseInt(value));
|
||||
} else if (['elapsed'].indexOf(attr) >= 0) {
|
||||
Vue.set(this.status, attr, parseFloat(value));
|
||||
} else {
|
||||
Vue.set(this.status, attr, value);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_parseTrack: async function(track) {
|
||||
if (!track || track.length === 0) {
|
||||
track = await request('music.mpd.currentsong');
|
||||
}
|
||||
|
||||
for (const [attr, value] of Object.entries(track)) {
|
||||
if (['id','pos','time'].indexOf(attr) >= 0) {
|
||||
Vue.set(this.track, attr, parseInt(value));
|
||||
} else {
|
||||
Vue.set(this.track, attr, value);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_parsePlaylist: function(playlist) {
|
||||
if (!playlist || playlist.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.playlist = [];
|
||||
|
||||
for (var track of playlist) {
|
||||
for (const [attr, value] of Object.entries(track)) {
|
||||
if (['time','pos','id'].indexOf(attr) >= 0) {
|
||||
track[attr] = parseInt(value);
|
||||
} else {
|
||||
track[attr] = value;
|
||||
}
|
||||
}
|
||||
|
||||
this.playlist.push(track);
|
||||
}
|
||||
},
|
||||
|
||||
_parseBrowserItems: function(browserItems) {
|
||||
if (!browserItems || browserItems.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.browserItems = [];
|
||||
|
||||
for (var item of browserItems) {
|
||||
if (item.directory) {
|
||||
this.browserItems.push({
|
||||
type: 'directory',
|
||||
name: item.directory,
|
||||
});
|
||||
} else if (item.playlist) {
|
||||
this.browserItems.push({
|
||||
type: 'playlist',
|
||||
name: item.playlist,
|
||||
'last-modified': item['last-modified'],
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
convertTime: function(time) {
|
||||
time = parseFloat(time); // Normalize strings
|
||||
var t = {};
|
||||
t.h = '' + parseInt(time/3600);
|
||||
t.m = '' + parseInt(time/60 - t.h*60);
|
||||
t.s = '' + parseInt(time - t.m*60);
|
||||
|
||||
for (var attr of ['m','s']) {
|
||||
if (parseInt(t[attr]) < 10) {
|
||||
t[attr] = '0' + t[attr];
|
||||
}
|
||||
}
|
||||
|
||||
var ret = [];
|
||||
if (parseInt(t.h)) {
|
||||
ret.push(t.h);
|
||||
}
|
||||
|
||||
ret.push(t.m, t.s);
|
||||
return ret.join(':');
|
||||
},
|
||||
|
||||
previous: async function() {
|
||||
await request('music.mpd.previous');
|
||||
let track = await request('music.mpd.currentsong');
|
||||
this.onNewPlayingTrack({track: track});
|
||||
},
|
||||
|
||||
repeat: async function() {
|
||||
await request('music.mpd.repeat');
|
||||
let status = await request('music.mpd.status');
|
||||
this._parseStatus(status);
|
||||
},
|
||||
|
||||
playPause: async function() {
|
||||
await request('music.mpd.pause');
|
||||
let status = await request('music.mpd.status');
|
||||
const method = status.state === 'play' ? this.onMusicPlay : this.onMusicPause;
|
||||
method({ status: status });
|
||||
},
|
||||
|
||||
stop: async function() {
|
||||
await request('music.mpd.stop');
|
||||
this.onMusicStop({});
|
||||
},
|
||||
|
||||
random: async function() {
|
||||
await request('music.mpd.random');
|
||||
let status = await request('music.mpd.status');
|
||||
this._parseStatus(status);
|
||||
},
|
||||
|
||||
next: async function() {
|
||||
await request('music.mpd.next');
|
||||
let track = await request('music.mpd.currentsong');
|
||||
this.onNewPlayingTrack({track: track});
|
||||
},
|
||||
|
||||
seek: async function(event) {
|
||||
var value;
|
||||
|
||||
if (event.target) {
|
||||
value = parseFloat(event.target.value);
|
||||
} else if (event.value) {
|
||||
value = parseFloat(event.value);
|
||||
} else {
|
||||
value = parseFloat(event);
|
||||
}
|
||||
|
||||
const status = await request('music.mpd.seekcur', {value: value});
|
||||
this.onSeekChange({status: status});
|
||||
},
|
||||
|
||||
volume: async function(event) {
|
||||
var value;
|
||||
|
||||
if (event.target) {
|
||||
value = parseFloat(event.target.value);
|
||||
} else if (event.value) {
|
||||
value = parseFloat(event.value);
|
||||
} else {
|
||||
value = parseFloat(event);
|
||||
}
|
||||
|
||||
const status = await request('music.mpd.setvol', {vol: value});
|
||||
this.onVolumeChange({status: status});
|
||||
},
|
||||
|
||||
onNewPlayingTrack: async function(event) {
|
||||
var previousTrack = {
|
||||
file: this.track.file,
|
||||
artist: this.track.artist,
|
||||
title: this.track.title,
|
||||
};
|
||||
|
||||
this.status.state = 'play';
|
||||
this.status.elapsed = 0;
|
||||
this.track = {};
|
||||
|
||||
let status = await request('music.mpd.status');
|
||||
this._parseStatus(status);
|
||||
this._parseTrack(event.track);
|
||||
this.startTimer();
|
||||
|
||||
if (this.track.file != previousTrack.file
|
||||
|| this.track.artist != previousTrack.artist
|
||||
|| this.track.title != previousTrack.title) {
|
||||
this.showNewTrackNotification();
|
||||
}
|
||||
},
|
||||
|
||||
showNewTrackNotification: function() {
|
||||
createNotification({
|
||||
html: '<b>' + (this.track.artist || '[No Artist]') + '</b><br>' +
|
||||
(this.track.title || '[No Title]'),
|
||||
image: {
|
||||
icon: 'play',
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
onMusicStop: function(event) {
|
||||
this.status.state = 'stop';
|
||||
this._parseStatus(event.status);
|
||||
this._parseTrack(event.track);
|
||||
this.stopTimer();
|
||||
},
|
||||
|
||||
onMusicPlay: function(event) {
|
||||
this.status.state = 'play';
|
||||
this._parseStatus(event.status);
|
||||
this._parseTrack(event.track);
|
||||
this.startTimer();
|
||||
},
|
||||
|
||||
onMusicPause: function(event) {
|
||||
this.status.state = 'pause';
|
||||
this._parseStatus(event.status);
|
||||
this._parseTrack(event.track);
|
||||
|
||||
this.syncTime.timestamp = new Date();
|
||||
this.syncTime.elapsed = this.status.elapsed;
|
||||
},
|
||||
|
||||
onSeekChange: function(event) {
|
||||
if (event.position != null)
|
||||
this.status.elapsed = parseFloat(event.position);
|
||||
if (event.status)
|
||||
this._parseStatus(event.status);
|
||||
if (event.track)
|
||||
this._parseTrack(event.track);
|
||||
|
||||
this.syncTime.timestamp = new Date();
|
||||
this.syncTime.elapsed = this.status.elapsed;
|
||||
},
|
||||
|
||||
onPlaylistChange: function(event) {
|
||||
console.log(event);
|
||||
},
|
||||
|
||||
onVolumeChange: function(event) {
|
||||
if (event.volume != null)
|
||||
this.status.volume = parseFloat(event.volume);
|
||||
if (event.status)
|
||||
this._parseStatus(event.status);
|
||||
if (event.track)
|
||||
this._parseTrack(event.track);
|
||||
},
|
||||
|
||||
onRepeatChange: function(event) {
|
||||
this.status.repeat = event.state;
|
||||
},
|
||||
|
||||
onRandomChange: function(event) {
|
||||
this.status.random = event.state;
|
||||
},
|
||||
|
||||
startTimer: function() {
|
||||
if (this.timer != null) {
|
||||
this.stopTimer();
|
||||
}
|
||||
|
||||
this.syncTime.timestamp = new Date();
|
||||
this.syncTime.elapsed = this.status.elapsed;
|
||||
this.timer = setInterval(this.timerFunc, 1000);
|
||||
},
|
||||
|
||||
stopTimer: function() {
|
||||
if (this.timer == null) {
|
||||
clearInterval(this.timer);
|
||||
this.timer = null;
|
||||
}
|
||||
},
|
||||
|
||||
timerFunc: function() {
|
||||
if (this.status.state !== 'play' || this.status.elapsed == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.status.elapsed = this.syncTime.elapsed +
|
||||
((new Date()).getTime()/1000) - (this.syncTime.timestamp.getTime()/1000);
|
||||
},
|
||||
},
|
||||
|
||||
created: function() {
|
||||
this.refresh();
|
||||
registerEventHandler(this.onNewPlayingTrack, 'platypush.message.event.music.NewPlayingTrackEvent');
|
||||
registerEventHandler(this.onMusicStop, 'platypush.message.event.music.MusicStopEvent');
|
||||
registerEventHandler(this.onMusicPlay, 'platypush.message.event.music.MusicPlayEvent');
|
||||
registerEventHandler(this.onMusicPause, 'platypush.message.event.music.MusicPauseEvent');
|
||||
registerEventHandler(this.onSeekChange, 'platypush.message.event.music.SeekChangeEvent');
|
||||
registerEventHandler(this.onPlaylistChange, 'platypush.message.event.music.PlaylistChangeEvent');
|
||||
registerEventHandler(this.onVolumeChange, 'platypush.message.event.music.VolumeChangeEvent');
|
||||
registerEventHandler(this.onRepeatChange, 'platypush.message.event.music.PlaybackRepeatModeChangeEvent');
|
||||
registerEventHandler(this.onRandomChange, 'platypush.message.event.music.PlaybackRandomModeChangeEvent');
|
||||
},
|
||||
});
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
<script type="text/x-template" id="tmpl-app-header">
|
||||
<header class="s-hidden m-hidden">
|
||||
<div class="row">
|
||||
<div class="logo col-9">
|
||||
<span class="logo-1">Platypush</span>
|
||||
<span class="logo-2">Web Panel</span>
|
||||
</div>
|
||||
|
||||
<div class="date-time col-3">
|
||||
<div class="date" v-text="now.toDateString().substring(0,10)"></div>
|
||||
<div class="time" v-text="now.toTimeString().substring(0,8)"></div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
</script>
|
||||
|
||||
<app-header></app-header>
|
||||
|
|
@ -40,25 +40,31 @@
|
|||
</script>
|
||||
|
||||
{% include 'elements.html' %}
|
||||
|
||||
{% for plugin, conf in templates.items() %}
|
||||
{% with configuration=templates[plugin] %}
|
||||
{% include conf['_template_file'] %}
|
||||
{% endwith %}
|
||||
{% endfor %}
|
||||
|
||||
{% for script in scripts.values() %}
|
||||
<script type="text/javascript" src="{{ url_for('static', filename=script['_script_file']) }}"></script>
|
||||
{% endfor %}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
{# include 'header.html' #}
|
||||
|
||||
{% with plugins=templates.keys() %}
|
||||
{% include 'nav.html' %}
|
||||
{% endwith %}
|
||||
|
||||
<main>
|
||||
<div class="plugins-container">
|
||||
{% for plugin, conf in templates.items() %}
|
||||
{% with configuration=templates[plugin], utils=utils %}
|
||||
{% include conf['_template_file'] %}
|
||||
<plugin tag="{{ utils.plugin_name_to_tag(plugin) }}"
|
||||
:config="{{ conf }}" :class="{hidden: '{{ plugin }}' != selectedPlugin}"/>
|
||||
{% endwith %}
|
||||
{% endfor %}
|
||||
<plugin v-for="(conf, plugin) in {{ utils.to_json(templates) }}"
|
||||
:tag="plugin.replace('.', '-')"
|
||||
:key="plugin"
|
||||
:config="conf"
|
||||
:class="{hidden: plugin != selectedPlugin}"/>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
|
@ -67,10 +73,6 @@
|
|||
|
||||
{% include 'plugins/template.html' %}
|
||||
|
||||
{% for script in scripts.values() %}
|
||||
<script type="text/javascript" src="{{ url_for('static', filename=script['_script_file']) }}"></script>
|
||||
{% endfor %}
|
||||
|
||||
<script type="text/javascript" src="{{ url_for('static', filename='js/application.js') }}"></script>
|
||||
</body>
|
||||
|
||||
|
|
|
@ -6,18 +6,9 @@
|
|||
{{ plugin }}
|
||||
</a>
|
||||
</li>
|
||||
<span class="decorator" v-if="'{{ plugin }}' == selectedPlugin"></span>
|
||||
{% endfor %}
|
||||
|
||||
<li>
|
||||
<a href="#{{ plugin }}">Test tab 2</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#{{ plugin }}">Test tab 3</a>
|
||||
</li>
|
||||
|
||||
<div class="date-time pull-right">
|
||||
<!--<div class="date" v-text="now.toDateString().substring(0,10)"></div>-->
|
||||
<div class="time" v-text="now.toTimeString().substring(0,8)"></div>
|
||||
</div>
|
||||
</ul>
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
<script type="application/javascript" src="{{ url_for('static', filename='js/plugins/music.mpd/browser.js') }}"></script>
|
||||
|
||||
<script type="text/x-template" id="tmpl-music-mpd-browser-item">
|
||||
<div class="row item">
|
||||
<div class="col-1 icon">
|
||||
<i class="fa fa-folder" v-if="type == 'directory'"></i>
|
||||
<i class="fa fa-list" v-else-if="type == 'playlist'"></i>
|
||||
</div>
|
||||
|
||||
<div class="col-11 name" v-text="name"></div>
|
||||
</div>
|
||||
</script>
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
{% include 'plugins/music.mpd/browser.html' %}
|
||||
|
||||
<script type="text/x-template" id="tmpl-music-mpd">
|
||||
<div class="row music-mpd-container">
|
||||
<div class="row panels">
|
||||
<div class="col-no-margin-l-3 s-hidden m-hidden browser">
|
||||
<music-mpd-browser-item
|
||||
v-for="item in browserItems"
|
||||
:key="item.type + '-' + item.name"
|
||||
:type="item.type"
|
||||
:name="item.name">
|
||||
</music-mpd-browser-item>
|
||||
</div>
|
||||
|
||||
<div class="col-no-margin-s-12 col-no-margin-m-12 col-no-margin-l-9 playlist">
|
||||
<div class="row track item"
|
||||
v-for="track in playlist">
|
||||
<div class="col-5 artist" v-text="track.artist"></div>
|
||||
<div class="col-5 title" v-text="track.title"></div>
|
||||
<div class="col-2 pull-right duration" v-text="convertTime(track.time)"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row controls">
|
||||
<div class="col-3 track-container">
|
||||
<div class="track-info" v-if="status.state == 'play' || status.state == 'pause'">
|
||||
<div class="row artist" v-text="track.artist"></div>
|
||||
<div class="row title" v-text="track.title"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-6 playback-controls">
|
||||
<div class="row">
|
||||
<button @click="previous">
|
||||
<i class="fa fa-step-backward"></i>
|
||||
</button>
|
||||
<button @click="playPause">
|
||||
<i class="fa fa-pause" v-if="status.state == 'play'"></i>
|
||||
<i class="fa fa-play" v-else></i>
|
||||
</button>
|
||||
<button @click="stop" v-if="status.state != 'stop'">
|
||||
<i class="fa fa-stop"></i>
|
||||
</button>
|
||||
<button @click="next">
|
||||
<i class="fa fa-step-forward"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<span class="elapsed-time" v-text="status.elapsed && status.state != 'stop' ? convertTime(status.elapsed) : '-:--'"></span>
|
||||
<input type="range"
|
||||
class="slider seek-slider"
|
||||
v-model="status.elapsed"
|
||||
min="0"
|
||||
:max="track.time"
|
||||
:disabled="!track.time || status.state == 'stop'"
|
||||
@input="seek">
|
||||
<span class="total-time" v-text="track.time && status.state != 'stop' ? convertTime(track.time) : '-:--'"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-3 pull-right">
|
||||
<div class="row">
|
||||
<button @click="random" :class="{enabled: status.random}">
|
||||
<i class="fa fa-random"></i>
|
||||
</button>
|
||||
<button @click="repeat" :class="{enabled: status.repeat}">
|
||||
<i class="fa fa-repeat"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="row volume-container">
|
||||
<button disabled>
|
||||
<i class="fa fa-volume-up"></i>
|
||||
</button>
|
||||
<input type="range"
|
||||
class="slider volume-slider"
|
||||
min="0"
|
||||
max="100"
|
||||
v-model="status.volume"
|
||||
@input="volume">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
|
@ -185,11 +185,11 @@ class MusicMopidyBackend(Backend):
|
|||
new_status = self._get_tracklist_status()
|
||||
if new_status['random'] != self._latest_status.get('random'):
|
||||
self.bus.post(PlaybackRandomModeChangeEvent(state=new_status['random']))
|
||||
if new_status['repeat'] != self._latest_status('repeat'):
|
||||
if new_status['repeat'] != self._latest_status['repeat']:
|
||||
self.bus.post(PlaybackRepeatModeChangeEvent(state=new_status['repeat']))
|
||||
if new_status['single'] != self._latest_status('single'):
|
||||
if new_status['single'] != self._latest_status['single']:
|
||||
self.bus.post(PlaybackSingleModeChangeEvent(state=new_status['single']))
|
||||
if new_status['consume'] != self._latest_status('consume'):
|
||||
if new_status['consume'] != self._latest_status['consume']:
|
||||
self.bus.post(PlaybackConsumeModeChangeEvent(state=new_status['consume']))
|
||||
|
||||
self._latest_status = new_status
|
||||
|
|
|
@ -37,20 +37,37 @@ class MusicMpdPlugin(MusicPlugin):
|
|||
self.port = port
|
||||
self.client = None
|
||||
|
||||
def _connect(self):
|
||||
if not self.client:
|
||||
def _connect(self, n_tries=2):
|
||||
with self._client_lock:
|
||||
if self.client:
|
||||
return
|
||||
|
||||
error = None
|
||||
while n_tries > 0:
|
||||
try:
|
||||
n_tries -= 1
|
||||
self.client = mpd.MPDClient(use_unicode=True)
|
||||
self.client.connect(self.host, self.port)
|
||||
return self.client
|
||||
except Exception as e:
|
||||
error = e
|
||||
self.logger.warning('Connection exception: {}{}'.
|
||||
format(str(e), (': Retrying' if n_tries > 0 else '')))
|
||||
time.sleep(0.5)
|
||||
|
||||
self.client = None
|
||||
raise error
|
||||
|
||||
def _exec(self, method, *args, **kwargs):
|
||||
n_tries = int(kwargs.pop('n_tries')) if 'n_tries' in kwargs else 1
|
||||
error = None
|
||||
n_tries = int(kwargs.pop('n_tries')) if 'n_tries' in kwargs else 2
|
||||
return_status = kwargs.pop('return_status') \
|
||||
if 'return_status' in kwargs else True
|
||||
|
||||
while n_tries > 0:
|
||||
try:
|
||||
self._connect()
|
||||
response = None
|
||||
n_tries -= 1
|
||||
with self._client_lock:
|
||||
response = getattr(self.client, method)(*args, **kwargs)
|
||||
|
||||
|
@ -58,16 +75,12 @@ class MusicMpdPlugin(MusicPlugin):
|
|||
return self.status().output
|
||||
return response
|
||||
except Exception as e:
|
||||
error = str(e)
|
||||
self.logger.warning('Exception while executing MPD method {}: {}'.
|
||||
format(method, str(e)))
|
||||
format(method, error))
|
||||
self.client = None
|
||||
|
||||
if n_tries > 0:
|
||||
kwargs['return_status'] = return_status
|
||||
kwargs['n_tries'] = n_tries-1
|
||||
return self._exec(method, *args, **kwargs)
|
||||
else:
|
||||
return (None, str(e))
|
||||
return None, error
|
||||
|
||||
@action
|
||||
def play(self, resource=None):
|
||||
|
@ -346,14 +359,21 @@ class MusicMpdPlugin(MusicPlugin):
|
|||
}
|
||||
"""
|
||||
|
||||
n_tries = 2
|
||||
error = None
|
||||
|
||||
while n_tries > 0:
|
||||
try:
|
||||
n_tries -= 1
|
||||
self._connect()
|
||||
return self.client.status()
|
||||
except Exception as e:
|
||||
error = e
|
||||
self.logger.warning('Exception while getting MPD status: {}'.
|
||||
format(str(e)))
|
||||
self.client = None
|
||||
return (None, str(e))
|
||||
|
||||
return None, error
|
||||
|
||||
@action
|
||||
def currentsong(self):
|
||||
|
|
Loading…
Reference in a new issue