Comments added and methods of class MCGClient reordered

This commit is contained in:
coderkun 2012-06-22 15:33:48 +02:00
parent c2eb40c317
commit cd711efa4a
2 changed files with 145 additions and 98 deletions

235
mcg.py
View file

@ -1,6 +1,9 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Author: coderkun <olli@coderkun.de>
import mpd
@ -8,17 +11,26 @@ import os
from hashlib import md5
from threading import Thread
class MCGClient:
"""Client library for handling the connection to the Music Player Daemon.
This class implements an album-based MPD client.
It offers a non-blocking threaded worker model for use in graphical
environments and is based on python-mpd2.
"""
# Signal: connect/disconnect event
SIGNAL_CONNECT = 'connect'
# Signal: general idle event
SIGNAL_IDLE = 'idle'
# Signal: player idle event
SIGNAL_IDLE_PLAYER = 'idle_player'
# Signal: update event
SIGNAL_UPDATE = 'update'
def __init__(self):
"""Sets class variables and instantiates the MPDClient.
"""
self._connected = False
self._albums = {}
self._callbacks = {}
@ -29,6 +41,9 @@ class MCGClient:
def connect(self, host="localhost", port="6600", password=None):
"""Connects to MPD with the given host, port and password or
with standard values.
"""
# TODO als Parameter an _add_action() übergeben, nicht speichern
self._host = host
self._port = port
@ -36,42 +51,22 @@ class MCGClient:
self._add_action(self._connect)
def _connect(self):
try:
self._client.connect(self._host, self._port)
if self._password:
self._client.password(self._password)
# TODO Verbindung testen
self._connected = True
self._callback(self.SIGNAL_CONNECT, self._connected, None)
self.update()
self.idle_player()
except IOError as e:
self._connected = False
self._callback(self.SIGNAL_CONNECT, self._connected, e)
def is_connected(self):
"""Returns the connection status.
"""
return self._connected
def disconnect(self):
"""Disconnects from the connected MPD.
"""
self._add_action(self._disconnect)
def _disconnect(self):
if not self.is_connected():
return
try:
#self._client.close()
self._client.disconnect()
except:
self._client = mpd.MPDClient()
self._connected = False
self._callback(self.SIGNAL_CONNECT, self._connected, None)
def close(self):
"""Closes the connection and stops properly the worker thread.
This method is to stop the whole appliction.
"""
if not self.is_connected():
return
try:
@ -83,32 +78,81 @@ class MCGClient:
def update(self):
"""Updates the album list.
"""
self._add_action(self._update)
def _update(self):
for song in self._client.listallinfo():
try:
if song['album'] not in self._albums:
album = MCGAlbum(song['artist'], song['album'], song['date'], os.path.dirname(song['file']))
self._albums[album.get_hash()] = album
else:
album = self._albums[MCGAlbum.hash(song['artist'], song['album'])]
track = MCGTrack(song['title'], song['track'], song['time'], song['file'])
album.add_track(track)
except KeyError:
pass
# TODO Alben sortieren
self._callback(self.SIGNAL_UPDATE, self._albums)
def play(self, album):
"""Plays the given album.
"""
# TODO play()
# mpd-Befehle: add, play
# https://github.com/Mic92/python-mpd2/blob/master/mpd.py
print("play: ", self._albums[album].get_title())
def connect_signal(self, signal, callback):
"""Connects a callback function to a signal (event).
"""
self._callbacks[signal] = callback
def _has_callback(self, signal):
"""Checks if there is a registered callback function for a
signal.
"""
return signal in self._callbacks
def _callback(self, signal, *args):
"""Calls the callback function for a signal.
"""
if self._has_callback(signal):
callback = self._callbacks[signal]
callback(*args)
def _add_action(self, method):
"""Adds an action to the action list.
"""
self._actions.append(method)
self._start_worker()
def _start_worker(self):
"""Starts the worker thread which waits for action to be
performed.
"""
if self._worker is None or not self._worker.is_alive():
self._worker = Thread(target=self._work, name='worker', args=())
self._worker.start()
else:
try:
self._client.noidle()
except TypeError as e:
pass
def _work(self):
"""Performs the next action or waits for an idle event.
"""
while True:
if self._actions:
action = self._actions.pop(0)
action()
else:
if not self.is_connected():
break
modules = self._client.idle()
if not self._go:
break
self._idle(modules)
def _idle(self, modules):
"""Reacts to idle events from MPD.
"""
if not modules:
return
@ -125,11 +169,59 @@ class MCGClient:
pass
def idle_player(self):
self._add_action(self._idle_player)
def _connect(self):
"""Action: Performs the real connect to MPD.
"""
try:
self._client.connect(self._host, self._port)
if self._password:
self._client.password(self._password)
# TODO Verbindung testen
self._connected = True
self._callback(self.SIGNAL_CONNECT, self._connected, None)
self.update()
self._add_action(self._idle_player)
except IOError as e:
self._connected = False
self._callback(self.SIGNAL_CONNECT, self._connected, e)
def _disconnect(self):
"""Action: Performs the real disconnect from MPD.
"""
if not self.is_connected():
return
try:
#self._client.close()
self._client.disconnect()
except:
self._client = mpd.MPDClient()
self._connected = False
self._callback(self.SIGNAL_CONNECT, self._connected, None)
def _update(self):
"""Action: Performs the real update.
"""
for song in self._client.listallinfo():
try:
if song['album'] not in self._albums:
album = MCGAlbum(song['artist'], song['album'], song['date'], os.path.dirname(song['file']))
self._albums[album.get_hash()] = album
else:
album = self._albums[MCGAlbum.hash(song['artist'], song['album'])]
track = MCGTrack(song['title'], song['track'], song['time'], song['file'])
album.add_track(track)
except KeyError:
pass
# TODO Alben sortieren
self._callback(self.SIGNAL_UPDATE, self._albums)
def _idle_player(self):
"""Reacts on the player idle event.
"""
if not self._has_callback(self.SIGNAL_IDLE_PLAYER):
return
status = self._client.status()
@ -140,51 +232,6 @@ class MCGClient:
def connect_signal(self, signal, callback):
self._callbacks[signal] = callback
def _has_callback(self, signal):
return signal in self._callbacks
def _callback(self, signal, *args):
if self._has_callback(signal):
callback = self._callbacks[signal]
callback(*args)
def _add_action(self, method):
self._actions.append(method)
self._start_worker()
def _start_worker(self):
if self._worker is None or not self._worker.is_alive():
self._worker = Thread(target=self._work, name='worker', args=())
self._worker.start()
else:
try:
self._client.noidle()
except TypeError as e:
pass
def _work(self):
while True:
if self._actions:
action = self._actions.pop(0)
action()
else:
if not self.is_connected():
break
modules = self._client.idle()
if not self._go:
break
self._idle(modules)
class MCGAlbum:
_file_names = ['folder', 'cover']

View file

@ -1,14 +1,14 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Author: coderkun <olli@coderkun.de>
from gi.repository import Gtk, Gdk, GdkPixbuf, GObject
import mcg
class MCGGtk(Gtk.Window):
def __init__(self):
@ -292,7 +292,6 @@ class ConnectionPanel(Gtk.Box):
from threading import Thread
class CoverPanel(Gtk.HPaned):
SIGNAL_UPDATE_START = 'update-start'
SIGNAL_UPDATE_END = 'update-end'
@ -443,6 +442,7 @@ class CoverPanel(Gtk.HPaned):
import os
import configparser