music.mpd plugin vue.js refactoring - WIP

This commit is contained in:
Fabio Manganiello 2019-05-30 02:07:28 +02:00
parent 19162a3b8d
commit 7a74b83c76
18 changed files with 724 additions and 128 deletions

View file

@ -26,6 +26,7 @@ $widths: (
float: left;
box-sizing: border-box;
width: ((100%/12)*$i);
margin: 0;
}
@if $i < 12 {

View file

@ -3,6 +3,7 @@
bottom: 0;
right: 0;
width: 25em;
z-index: 1000;
.notification {
background: $notification-bg;

View file

@ -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;

View file

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

View file

@ -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);
}

View file

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

View file

@ -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 {

View file

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

View file

@ -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;

View file

@ -0,0 +1,8 @@
Vue.component('music-mpd-browser-item', {
template: '#tmpl-music-mpd-browser-item',
props: ['type','name'],
methods: {
},
});

View 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');
},
});

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -37,37 +37,50 @@ class MusicMpdPlugin(MusicPlugin):
self.port = port
self.client = None
def _connect(self):
if not self.client:
self.client = mpd.MPDClient(use_unicode=True)
self.client.connect(self.host, self.port)
return 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
try:
self._connect()
response = None
with self._client_lock:
response = getattr(self.client, method)(*args, **kwargs)
while n_tries > 0:
try:
self._connect()
n_tries -= 1
with self._client_lock:
response = getattr(self.client, method)(*args, **kwargs)
if return_status:
return self.status().output
return response
except Exception as e:
self.logger.warning('Exception while executing MPD method {}: {}'.
format(method, str(e)))
self.client = None
if return_status:
return self.status().output
return response
except Exception as e:
error = str(e)
self.logger.warning('Exception while executing MPD method {}: {}'.
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):
}
"""
try:
self._connect()
return self.client.status()
except Exception as e:
self.logger.warning('Exception while getting MPD status: {}'.
format(str(e)))
self.client = None
return (None, str(e))
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, error
@action
def currentsong(self):