[#295] Merged music.mpd
plugin and backend.
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
Closes: #295
This commit is contained in:
parent
e96eae73ec
commit
2066db463b
8 changed files with 301 additions and 232 deletions
|
@ -274,6 +274,10 @@ class Application:
|
|||
backend.stop()
|
||||
|
||||
for plugin in runnable_plugins:
|
||||
# This is required because some plugins may redefine the `stop` method.
|
||||
# In that case, at the very least the _should_stop event should be
|
||||
# set to notify the plugin to stop.
|
||||
plugin._should_stop.set() # pylint: disable=protected-access
|
||||
plugin.stop()
|
||||
|
||||
for backend in backends:
|
||||
|
|
|
@ -1,174 +0,0 @@
|
|||
import time
|
||||
|
||||
from platypush.backend import Backend
|
||||
from platypush.context import get_plugin
|
||||
from platypush.message.event.music import (
|
||||
MusicPlayEvent,
|
||||
MusicPauseEvent,
|
||||
MusicStopEvent,
|
||||
NewPlayingTrackEvent,
|
||||
PlaylistChangeEvent,
|
||||
VolumeChangeEvent,
|
||||
PlaybackConsumeModeChangeEvent,
|
||||
PlaybackSingleModeChangeEvent,
|
||||
PlaybackRepeatModeChangeEvent,
|
||||
PlaybackRandomModeChangeEvent,
|
||||
)
|
||||
|
||||
|
||||
class MusicMpdBackend(Backend):
|
||||
"""
|
||||
This backend listens for events on a MPD/Mopidy music server.
|
||||
|
||||
Requires:
|
||||
|
||||
* :class:`platypush.plugins.music.mpd.MusicMpdPlugin` configured
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, server='localhost', port=6600, poll_seconds=3, **kwargs):
|
||||
"""
|
||||
:param poll_seconds: Interval between queries to the server (default: 3 seconds)
|
||||
:type poll_seconds: float
|
||||
"""
|
||||
|
||||
super().__init__(**kwargs)
|
||||
|
||||
self.server = server
|
||||
self.port = port
|
||||
self.poll_seconds = poll_seconds
|
||||
|
||||
def run(self):
|
||||
super().run()
|
||||
|
||||
last_status = {}
|
||||
last_state = None
|
||||
last_track = None
|
||||
last_playlist = None
|
||||
|
||||
while not self.should_stop():
|
||||
success = False
|
||||
state = None
|
||||
status = None
|
||||
playlist = None
|
||||
track = None
|
||||
|
||||
while not success:
|
||||
try:
|
||||
plugin = get_plugin('music.mpd')
|
||||
if not plugin:
|
||||
raise StopIteration
|
||||
|
||||
status = plugin.status().output
|
||||
if not status or status.get('state') is None:
|
||||
raise StopIteration
|
||||
|
||||
track = plugin.currentsong().output
|
||||
state = status['state'].lower()
|
||||
playlist = status['playlist']
|
||||
success = True
|
||||
except Exception as e:
|
||||
self.logger.debug(e)
|
||||
get_plugin('music.mpd', reload=True)
|
||||
if not state:
|
||||
state = last_state
|
||||
if not playlist:
|
||||
playlist = last_playlist
|
||||
if not track:
|
||||
track = last_track
|
||||
finally:
|
||||
time.sleep(self.poll_seconds)
|
||||
|
||||
if state != last_state:
|
||||
if state == 'stop':
|
||||
self.bus.post(
|
||||
MusicStopEvent(
|
||||
status=status, track=track, plugin_name='music.mpd'
|
||||
)
|
||||
)
|
||||
elif state == 'pause':
|
||||
self.bus.post(
|
||||
MusicPauseEvent(
|
||||
status=status, track=track, plugin_name='music.mpd'
|
||||
)
|
||||
)
|
||||
elif state == 'play':
|
||||
self.bus.post(
|
||||
MusicPlayEvent(
|
||||
status=status, track=track, plugin_name='music.mpd'
|
||||
)
|
||||
)
|
||||
|
||||
if playlist != last_playlist:
|
||||
if last_playlist:
|
||||
# XXX plchanges can become heavy with big playlists,
|
||||
# PlaylistChangeEvent temporarily disabled
|
||||
# changes = plugin.plchanges(last_playlist).output
|
||||
# self.bus.post(PlaylistChangeEvent(changes=changes))
|
||||
self.bus.post(PlaylistChangeEvent(plugin_name='music.mpd'))
|
||||
last_playlist = playlist
|
||||
|
||||
if state == 'play' and track != last_track:
|
||||
self.bus.post(
|
||||
NewPlayingTrackEvent(
|
||||
status=status, track=track, plugin_name='music.mpd'
|
||||
)
|
||||
)
|
||||
|
||||
if last_status.get('volume') != status['volume']:
|
||||
self.bus.post(
|
||||
VolumeChangeEvent(
|
||||
volume=int(status['volume']),
|
||||
status=status,
|
||||
track=track,
|
||||
plugin_name='music.mpd',
|
||||
)
|
||||
)
|
||||
|
||||
if last_status.get('random') != status['random']:
|
||||
self.bus.post(
|
||||
PlaybackRandomModeChangeEvent(
|
||||
state=bool(int(status['random'])),
|
||||
status=status,
|
||||
track=track,
|
||||
plugin_name='music.mpd',
|
||||
)
|
||||
)
|
||||
|
||||
if last_status.get('repeat') != status['repeat']:
|
||||
self.bus.post(
|
||||
PlaybackRepeatModeChangeEvent(
|
||||
state=bool(int(status['repeat'])),
|
||||
status=status,
|
||||
track=track,
|
||||
plugin_name='music.mpd',
|
||||
)
|
||||
)
|
||||
|
||||
if last_status.get('consume') != status['consume']:
|
||||
self.bus.post(
|
||||
PlaybackConsumeModeChangeEvent(
|
||||
state=bool(int(status['consume'])),
|
||||
status=status,
|
||||
track=track,
|
||||
plugin_name='music.mpd',
|
||||
)
|
||||
)
|
||||
|
||||
if last_status.get('single') != status['single']:
|
||||
self.bus.post(
|
||||
PlaybackSingleModeChangeEvent(
|
||||
state=bool(int(status['single'])),
|
||||
status=status,
|
||||
track=track,
|
||||
plugin_name='music.mpd',
|
||||
)
|
||||
)
|
||||
|
||||
last_status = status
|
||||
last_state = state
|
||||
last_track = track
|
||||
time.sleep(self.poll_seconds)
|
||||
|
||||
|
||||
# vim:sw=4:ts=4:et:
|
|
@ -1,22 +0,0 @@
|
|||
manifest:
|
||||
events:
|
||||
platypush.message.event.music.MusicPauseEvent: if the playback state changed to
|
||||
pause
|
||||
platypush.message.event.music.MusicPlayEvent: if the playback state changed to
|
||||
play
|
||||
platypush.message.event.music.MusicStopEvent: if the playback state changed to
|
||||
stop
|
||||
platypush.message.event.music.NewPlayingTrackEvent: if a new track is being played
|
||||
platypush.message.event.music.PlaylistChangeEvent: if the main playlist has changed
|
||||
platypush.message.event.music.VolumeChangeEvent: if the main volume has changed
|
||||
install:
|
||||
apt:
|
||||
- python3-mpd2
|
||||
dnf:
|
||||
- python-mpd2
|
||||
pacman:
|
||||
- python-mpd2
|
||||
pip:
|
||||
- python-mpd2
|
||||
package: platypush.backend.music.mpd
|
||||
type: backend
|
|
@ -320,6 +320,18 @@ backend.http:
|
|||
# port: 6600
|
||||
###
|
||||
|
||||
###
|
||||
# # Example last.fm scrobbler configuration, to synchronize your music
|
||||
# # activities to your Last.fm profile. You'll need to register an application
|
||||
# # with your account at https://www.last.fm/api.
|
||||
#
|
||||
# lastfm:
|
||||
# api_key: <API_KEY>
|
||||
# api_secret: <API_SECRET>
|
||||
# username: <USERNAME>
|
||||
# password: <PASSWORD>
|
||||
###
|
||||
|
||||
###
|
||||
# # Plugins with empty configuration can also be explicitly enabled by specifying
|
||||
# # `enabled: true` or `disabled: false`. An integration with no items will be
|
||||
|
@ -1100,6 +1112,23 @@ backend.http:
|
|||
# source: ${msg["source"]}
|
||||
###
|
||||
|
||||
###
|
||||
# # The example below is a hook that reacts when a `NewPlayingTrackEvent` event
|
||||
# # is received and synchronize the listening activity to the users' Last.fm
|
||||
# # profile (it requires the `lastfm` plugin and at least a music plugin
|
||||
# # enabled, like `music.mpd`).
|
||||
#
|
||||
# event.hook.OnNewMusicActivity:
|
||||
# if:
|
||||
# type: platypush.message.event.music.NewPlayingTrackEvent
|
||||
# then:
|
||||
# - if ${track.get('artist') and track.get('title')}:
|
||||
# - action: lastfm.scrobble
|
||||
# args:
|
||||
# artist: ${track['artist']}
|
||||
# title: ${track['title']}
|
||||
##
|
||||
|
||||
###
|
||||
# # The example below plays the music on mpd/mopidy when your voice assistant
|
||||
# # triggers a speech recognized event with "play the music" content.
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
import re
|
||||
import threading
|
||||
import time
|
||||
from typing import Collection, Optional, Union
|
||||
|
||||
from platypush.plugins import action
|
||||
from platypush.plugins import RunnablePlugin, action
|
||||
from platypush.plugins.music import MusicPlugin
|
||||
|
||||
from ._conf import MpdConfig
|
||||
from ._listener import MpdListener
|
||||
|
||||
class MusicMpdPlugin(MusicPlugin):
|
||||
|
||||
class MusicMpdPlugin(MusicPlugin, RunnablePlugin):
|
||||
"""
|
||||
This plugin allows you to interact with an MPD/Mopidy music server.
|
||||
|
||||
|
@ -21,22 +23,29 @@ class MusicMpdPlugin(MusicPlugin):
|
|||
|
||||
.. note:: As of Mopidy 3.0 MPD is an optional interface provided by the
|
||||
``mopidy-mpd`` extension. Make sure that you have the extension
|
||||
installed and enabled on your instance to use this plugin with your
|
||||
server.
|
||||
installed and enabled on your instance to use this plugin if you want to
|
||||
use it with Mopidy instead of MPD.
|
||||
|
||||
"""
|
||||
|
||||
_client_lock = threading.RLock()
|
||||
|
||||
def __init__(self, host: str, port: int = 6600):
|
||||
def __init__(
|
||||
self,
|
||||
host: str,
|
||||
port: int = 6600,
|
||||
poll_interval: Optional[float] = 5.0,
|
||||
**kwargs,
|
||||
):
|
||||
"""
|
||||
:param host: MPD IP/hostname
|
||||
:param port: MPD port (default: 6600)
|
||||
:param host: MPD IP/hostname.
|
||||
:param port: MPD port (default: 6600).
|
||||
:param poll_interval: Polling interval in seconds. If set, the plugin
|
||||
will poll the MPD server for status updates and trigger change
|
||||
events when required. Default: 5 seconds.
|
||||
"""
|
||||
|
||||
super().__init__()
|
||||
self.host = host
|
||||
self.port = port
|
||||
super().__init__(poll_interval=poll_interval, **kwargs)
|
||||
self.conf = MpdConfig(host=host, port=port)
|
||||
self.client = None
|
||||
|
||||
def _connect(self, n_tries: int = 2):
|
||||
|
@ -51,7 +60,7 @@ class MusicMpdPlugin(MusicPlugin):
|
|||
try:
|
||||
n_tries -= 1
|
||||
self.client = mpd.MPDClient()
|
||||
self.client.connect(self.host, self.port)
|
||||
self.client.connect(self.conf.host, self.conf.port)
|
||||
return self.client
|
||||
except Exception as e:
|
||||
error = e
|
||||
|
@ -60,7 +69,7 @@ class MusicMpdPlugin(MusicPlugin):
|
|||
e,
|
||||
(': Retrying' if n_tries > 0 else ''),
|
||||
)
|
||||
time.sleep(0.5)
|
||||
self.wait_stop(0.5)
|
||||
|
||||
self.client = None
|
||||
if error:
|
||||
|
@ -83,7 +92,8 @@ class MusicMpdPlugin(MusicPlugin):
|
|||
response = getattr(self.client, method)(*args, **kwargs)
|
||||
|
||||
if return_status:
|
||||
return self.status().output
|
||||
return self._status()
|
||||
|
||||
return response
|
||||
except Exception as e:
|
||||
error = str(e)
|
||||
|
@ -145,7 +155,7 @@ class MusicMpdPlugin(MusicPlugin):
|
|||
return self._exec('play') if status in ('pause', 'stop') else None
|
||||
|
||||
@action
|
||||
def stop(self, *_, **__):
|
||||
def stop(self, *_, **__): # type: ignore
|
||||
"""Stop playback"""
|
||||
return self._exec('stop')
|
||||
|
||||
|
@ -185,6 +195,9 @@ class MusicMpdPlugin(MusicPlugin):
|
|||
|
||||
:param vol: Volume value (range: 0-100).
|
||||
"""
|
||||
self.logger.warning(
|
||||
'music.mpd.setvol is deprecated, use music.mpd.set_volume instead'
|
||||
)
|
||||
return self.set_volume(vol)
|
||||
|
||||
@action
|
||||
|
@ -404,6 +417,9 @@ class MusicMpdPlugin(MusicPlugin):
|
|||
:param value: Seek position in seconds, or delta string (e.g. '+15' or
|
||||
'-15') to indicate a seek relative to the current position
|
||||
"""
|
||||
self.logger.warning(
|
||||
'music.mpd.seekcur is deprecated, use music.mpd.seek instead'
|
||||
)
|
||||
return self.seek(value)
|
||||
|
||||
@action
|
||||
|
@ -472,6 +488,24 @@ class MusicMpdPlugin(MusicPlugin):
|
|||
"""
|
||||
return self._status()
|
||||
|
||||
def _current_track(self):
|
||||
track = self._exec('currentsong', return_status=False)
|
||||
if not isinstance(track, dict):
|
||||
return None
|
||||
|
||||
if 'title' in track and (
|
||||
'artist' not in track
|
||||
or not track['artist']
|
||||
or re.search('^https?://', track['file'])
|
||||
or re.search('^tunein:', track['file'])
|
||||
):
|
||||
m = re.match(r'^\s*(.+?)\s+-\s+(.*)\s*$', track['title'])
|
||||
if m and m.group(1) and m.group(2):
|
||||
track['artist'] = m.group(1)
|
||||
track['title'] = m.group(2)
|
||||
|
||||
return track
|
||||
|
||||
@action
|
||||
def currentsong(self):
|
||||
"""
|
||||
|
@ -500,23 +534,7 @@ class MusicMpdPlugin(MusicPlugin):
|
|||
"x-albumuri": "spotify:album:6q5KhDhf9BZkoob7uAnq19"
|
||||
}
|
||||
"""
|
||||
|
||||
track = self._exec('currentsong', return_status=False)
|
||||
if not isinstance(track, dict):
|
||||
return None
|
||||
|
||||
if 'title' in track and (
|
||||
'artist' not in track
|
||||
or not track['artist']
|
||||
or re.search('^https?://', track['file'])
|
||||
or re.search('^tunein:', track['file'])
|
||||
):
|
||||
m = re.match(r'^\s*(.+?)\s+-\s+(.*)\s*$', track['title'])
|
||||
if m and m.group(1) and m.group(2):
|
||||
track['artist'] = m.group(1)
|
||||
track['title'] = m.group(2)
|
||||
|
||||
return track
|
||||
return self._current_track()
|
||||
|
||||
@action
|
||||
def playlistinfo(self):
|
||||
|
@ -589,6 +607,9 @@ class MusicMpdPlugin(MusicPlugin):
|
|||
"""
|
||||
Deprecated alias for :meth:`.playlists`.
|
||||
"""
|
||||
self.logger.warning(
|
||||
'music.mpd.listplaylists is deprecated, use music.mpd.get_playlists instead'
|
||||
)
|
||||
return self.get_playlists()
|
||||
|
||||
@action
|
||||
|
@ -611,6 +632,9 @@ class MusicMpdPlugin(MusicPlugin):
|
|||
"""
|
||||
Deprecated alias for :meth:`.playlist`.
|
||||
"""
|
||||
self.logger.warning(
|
||||
'music.mpd.listplaylist is deprecated, use music.mpd.get_playlist instead'
|
||||
)
|
||||
return self._exec('listplaylist', name, return_status=False)
|
||||
|
||||
@action
|
||||
|
@ -618,10 +642,15 @@ class MusicMpdPlugin(MusicPlugin):
|
|||
"""
|
||||
Deprecated alias for :meth:`.playlist` with ``with_tracks=True``.
|
||||
"""
|
||||
self.logger.warning(
|
||||
'music.mpd.listplaylistinfo is deprecated, use music.mpd.get_playlist instead'
|
||||
)
|
||||
return self.get_playlist(name, with_tracks=True)
|
||||
|
||||
@action
|
||||
def add_to_playlist(self, playlist: str, resources: Union[str, Collection[str]]):
|
||||
def add_to_playlist(
|
||||
self, playlist: str, resources: Union[str, Collection[str]], **_
|
||||
):
|
||||
"""
|
||||
Add one or multiple resources to a playlist.
|
||||
|
||||
|
@ -640,6 +669,9 @@ class MusicMpdPlugin(MusicPlugin):
|
|||
"""
|
||||
Deprecated alias for :meth:`.add_to_playlist`.
|
||||
"""
|
||||
self.logger.warning(
|
||||
'music.mpd.playlistadd is deprecated, use music.mpd.add_to_playlist instead'
|
||||
)
|
||||
return self.add_to_playlist(name, uri)
|
||||
|
||||
@action
|
||||
|
@ -677,6 +709,9 @@ class MusicMpdPlugin(MusicPlugin):
|
|||
"""
|
||||
Deprecated alias for :meth:`.remove_from_playlist`.
|
||||
"""
|
||||
self.logger.warning(
|
||||
'music.mpd.playlistdelete is deprecated, use music.mpd.remove_from_playlist instead'
|
||||
)
|
||||
return self.remove_from_playlist(name, pos)
|
||||
|
||||
@action
|
||||
|
@ -684,6 +719,9 @@ class MusicMpdPlugin(MusicPlugin):
|
|||
"""
|
||||
Deprecated alias for :meth:`.playlist_move`.
|
||||
"""
|
||||
self.logger.warning(
|
||||
'music.mpd.playlistmove is deprecated, use music.mpd.playlist_move instead'
|
||||
)
|
||||
return self.playlist_move(name, from_pos=from_pos, to_pos=to_pos)
|
||||
|
||||
@action
|
||||
|
@ -816,7 +854,9 @@ class MusicMpdPlugin(MusicPlugin):
|
|||
)
|
||||
|
||||
@action
|
||||
def searchadd(self, filter: dict, *args, **kwargs):
|
||||
def searchadd(
|
||||
self, filter: dict, *args, **kwargs # pylint: disable=redefined-builtin
|
||||
):
|
||||
"""
|
||||
Free search by filter and add the results to the current playlist.
|
||||
|
||||
|
@ -828,5 +868,16 @@ class MusicMpdPlugin(MusicPlugin):
|
|||
'searchadd', *filter_list, *args, return_status=False, **kwargs
|
||||
)
|
||||
|
||||
def main(self):
|
||||
listener = None
|
||||
if self.poll_interval is not None:
|
||||
listener = MpdListener(self)
|
||||
listener.start()
|
||||
|
||||
self.wait_stop()
|
||||
|
||||
if listener:
|
||||
listener.join()
|
||||
|
||||
|
||||
# vim:sw=4:ts=4:et:
|
||||
|
|
12
platypush/plugins/music/mpd/_conf.py
Normal file
12
platypush/plugins/music/mpd/_conf.py
Normal file
|
@ -0,0 +1,12 @@
|
|||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass
|
||||
class MpdConfig:
|
||||
"""
|
||||
MPD configuration
|
||||
"""
|
||||
|
||||
host: str = 'localhost'
|
||||
port: int = 6600
|
||||
password: str = ''
|
163
platypush/plugins/music/mpd/_listener.py
Normal file
163
platypush/plugins/music/mpd/_listener.py
Normal file
|
@ -0,0 +1,163 @@
|
|||
from dataclasses import dataclass, field
|
||||
from threading import Thread
|
||||
from typing import Optional
|
||||
|
||||
from platypush.context import get_bus
|
||||
from platypush.message.event.music import (
|
||||
MusicPlayEvent,
|
||||
MusicPauseEvent,
|
||||
MusicStopEvent,
|
||||
NewPlayingTrackEvent,
|
||||
PlaylistChangeEvent,
|
||||
VolumeChangeEvent,
|
||||
PlaybackConsumeModeChangeEvent,
|
||||
PlaybackSingleModeChangeEvent,
|
||||
PlaybackRepeatModeChangeEvent,
|
||||
PlaybackRandomModeChangeEvent,
|
||||
)
|
||||
from platypush.plugins.music import MusicPlugin
|
||||
|
||||
|
||||
@dataclass
|
||||
class MpdStatus:
|
||||
"""
|
||||
Data class for the MPD status.
|
||||
"""
|
||||
|
||||
state: Optional[str] = None
|
||||
playlist: Optional[int] = None
|
||||
volume: Optional[int] = None
|
||||
random: Optional[bool] = None
|
||||
repeat: Optional[bool] = None
|
||||
consume: Optional[bool] = None
|
||||
single: Optional[bool] = None
|
||||
track: dict = field(default_factory=dict)
|
||||
|
||||
|
||||
class MpdListener(Thread):
|
||||
"""
|
||||
Thread that listens/polls for MPD events and posts them to the bus.
|
||||
"""
|
||||
|
||||
def __init__(self, plugin: MusicPlugin, *_, **__):
|
||||
from . import MusicMpdPlugin
|
||||
|
||||
super().__init__(name='platypush:mpd:listener')
|
||||
assert isinstance(plugin, MusicMpdPlugin)
|
||||
self.plugin: MusicMpdPlugin = plugin
|
||||
self._status = MpdStatus()
|
||||
|
||||
@property
|
||||
def logger(self):
|
||||
return self.plugin.logger
|
||||
|
||||
@property
|
||||
def bus(self):
|
||||
return get_bus()
|
||||
|
||||
def wait_stop(self, timeout=None):
|
||||
self.plugin.wait_stop(timeout=timeout)
|
||||
|
||||
def _process_events(self, status: dict, track: Optional[dict] = None):
|
||||
state = status.get('state', '').lower()
|
||||
evt_args = {'status': status, 'track': track, 'plugin_name': 'music.mpd'}
|
||||
|
||||
if state != self._status.state:
|
||||
if state == 'stop':
|
||||
self.bus.post(MusicStopEvent(**evt_args))
|
||||
elif state == 'pause':
|
||||
self.bus.post(MusicPauseEvent(**evt_args))
|
||||
elif state == 'play':
|
||||
self.bus.post(MusicPlayEvent(**evt_args))
|
||||
|
||||
if status.get('playlist') != self._status.playlist and self._status.playlist:
|
||||
# XXX plchanges can become heavy with big playlists,
|
||||
# PlaylistChangeEvent temporarily disabled
|
||||
# changes = plugin.plchanges(last_playlist).output
|
||||
# self.bus.post(PlaylistChangeEvent(changes=changes))
|
||||
self.bus.post(PlaylistChangeEvent(plugin_name='music.mpd'))
|
||||
|
||||
if state == 'play' and track != self._status.track:
|
||||
self.bus.post(NewPlayingTrackEvent(**evt_args))
|
||||
|
||||
if (
|
||||
status.get('volume') is not None
|
||||
and status.get('volume') != self._status.volume
|
||||
):
|
||||
self.bus.post(VolumeChangeEvent(volume=int(status['volume']), **evt_args))
|
||||
|
||||
if (
|
||||
status.get('random') is not None
|
||||
and status.get('random') != self._status.random
|
||||
):
|
||||
self.bus.post(
|
||||
PlaybackRandomModeChangeEvent(
|
||||
state=bool(int(status['random'])), **evt_args
|
||||
)
|
||||
)
|
||||
|
||||
if (
|
||||
status.get('repeat') is not None
|
||||
and status.get('repeat') != self._status.repeat
|
||||
):
|
||||
self.bus.post(
|
||||
PlaybackRepeatModeChangeEvent(
|
||||
state=bool(int(status['repeat'])), **evt_args
|
||||
)
|
||||
)
|
||||
|
||||
if (
|
||||
status.get('consume') is not None
|
||||
and status.get('consume') != self._status.consume
|
||||
):
|
||||
self.bus.post(
|
||||
PlaybackConsumeModeChangeEvent(
|
||||
state=bool(int(status['consume'])), **evt_args
|
||||
)
|
||||
)
|
||||
|
||||
if (
|
||||
status.get('single') is not None
|
||||
and status.get('single') != self._status.single
|
||||
):
|
||||
self.bus.post(
|
||||
PlaybackSingleModeChangeEvent(
|
||||
state=bool(int(status['single'])), **evt_args
|
||||
)
|
||||
)
|
||||
|
||||
def _update_status(self, status: dict, track: Optional[dict] = None):
|
||||
self._status = MpdStatus(
|
||||
state=status.get('state', '').lower(),
|
||||
playlist=status.get('playlist'),
|
||||
volume=status.get('volume'),
|
||||
random=status.get('random'),
|
||||
repeat=status.get('repeat'),
|
||||
consume=status.get('consume'),
|
||||
single=status.get('single'),
|
||||
track=track or {},
|
||||
)
|
||||
|
||||
def run(self):
|
||||
super().run()
|
||||
|
||||
while not self.plugin.should_stop():
|
||||
try:
|
||||
status = self.plugin._status() # pylint: disable=protected-access
|
||||
assert status and status.get('state'), 'No status returned'
|
||||
if not (status and status.get('state')):
|
||||
self.wait_stop(self.plugin.poll_interval)
|
||||
break
|
||||
|
||||
track = self.plugin._current_track() # pylint: disable=protected-access
|
||||
self._process_events(status, track)
|
||||
self._update_status(status, track)
|
||||
except Exception as e:
|
||||
self.logger.warning(
|
||||
'Could not retrieve the latest status: %s', e, exc_info=True
|
||||
)
|
||||
finally:
|
||||
self.wait_stop(self.plugin.poll_interval)
|
||||
|
||||
|
||||
# vim:sw=4:ts=4:et:
|
|
@ -1,5 +1,11 @@
|
|||
manifest:
|
||||
events: {}
|
||||
events:
|
||||
- platypush.message.event.music.MusicPauseEvent
|
||||
- platypush.message.event.music.MusicPlayEvent
|
||||
- platypush.message.event.music.MusicStopEvent
|
||||
- platypush.message.event.music.NewPlayingTrackEvent
|
||||
- platypush.message.event.music.PlaylistChangeEvent
|
||||
- platypush.message.event.music.VolumeChangeEvent
|
||||
install:
|
||||
apt:
|
||||
- python3-mpd
|
||||
|
|
Loading…
Reference in a new issue