]> kaliko git repositories - mpd-sima.git/blob - sima/core.py
Move artist blacklist filter in player client
[mpd-sima.git] / sima / core.py
1 # -*- coding: utf-8 -*-
2 """Core Object dealing with plugins and player client
3 """
4
5 __version__ = '0.12.0pr1'
6 __author__ = 'kaliko jack'
7 __url__ = 'git://git.kaliko.me/sima.git'
8
9 import sys
10 import time
11
12 from collections import deque
13 from logging import getLogger
14
15 from .client import PlayerClient
16 from .client import PlayerError, PlayerUnHandledError
17 from .lib.simadb import SimaDB
18 from .lib.daemon import Daemon
19
20 class Sima(Daemon):
21     """Main class, plugin and player management
22     """
23
24     def __init__(self, conf):
25         ## Set daemon
26         Daemon.__init__(self, conf.get('daemon', 'pidfile'))
27         self.enabled = True
28         self.config = conf
29         self.sdb = SimaDB(db_path=conf.get('sima', 'db_file'))
30         PlayerClient.database = self.sdb
31         self.log = getLogger('sima')
32         self.plugins = list()
33         self.player = self.__get_player()  # Player client
34         try:
35             self.player.connect()
36         except (PlayerError, PlayerUnHandledError) as err:
37             self.log.error('Fails to connect player: {}'.format(err))
38             self.shutdown()
39         self.short_history = deque(maxlen=60)
40
41     def __get_player(self):
42         """Instanciate the player"""
43         host = self.config.get('MPD', 'host')
44         port = self.config.get('MPD', 'port')
45         pswd = self.config.get('MPD', 'password', fallback=None)
46         return PlayerClient(host, port, pswd)
47
48     def add_history(self):
49         self.short_history.appendleft(self.player.current)
50
51     def register_plugin(self, plugin_class):
52         """Registers plubin in Sima instance..."""
53         self.plugins.append(plugin_class(self))
54
55     def foreach_plugin(self, method, *args, **kwds):
56         """Plugin's callbacks dispatcher"""
57         for plugin in self.plugins:
58             #self.log.debug('dispatching {0} to {1}'.format(method, plugin))
59             getattr(plugin, method)(*args, **kwds)
60
61     def need_tracks(self):
62         if not self.enabled:
63             self.log.debug('Queueing disabled!')
64             return False
65         queue = self.player.queue
66         queue_trigger = self.config.getint('sima', 'queue_length')
67         self.log.debug('Currently {0} track(s) ahead. (target {1})'.format(
68                        len(queue), queue_trigger))
69         if len(queue) < queue_trigger:
70             return True
71         return False
72
73     def queue(self):
74         to_add = list()
75         for plugin in self.plugins:
76             pl_callback =  getattr(plugin, 'callback_need_track')()
77             if pl_callback:
78                 to_add.extend(pl_callback)
79         if not to_add:
80             self.log.warning('Queue plugins returned nothing!')
81             for plugin in self.plugins:
82                 pl_callback =  getattr(plugin, 'callback_need_track_fb')()
83                 if pl_callback:
84                     to_add.extend(pl_callback)
85         for track in to_add:
86             self.player.add(track)
87
88     def reconnect_player(self):
89         """Trying to reconnect cycling through longer timeout
90         cycle : 5s 10s 1m 5m 20m 1h
91         """
92         sleepfor = [5, 10, 60, 300, 1200, 3600]
93         while True:
94             tmp = sleepfor.pop(0)
95             sleepfor.append(tmp)
96             self.log.info('Trying to reconnect in {:>4d} seconds'.format(tmp))
97             time.sleep(tmp)
98             try:
99                 self.player.connect()
100             except PlayerError:
101                 continue
102             except PlayerUnHandledError as err:
103                 #TODO: unhandled Player exceptions
104                 self.log.warning('Unhandled player exception: %s' % err)
105             self.log.info('Got reconnected')
106             break
107
108     def shutdown(self):
109         """General shutdown method
110         """
111         self.log.warning('Starting shutdown.')
112         self.player.disconnect()
113         self.foreach_plugin('shutdown')
114
115         self.log.info('The way is shut, it was made by those who are dead. '
116                       'And the dead keep it…')
117         self.log.info('bye...')
118
119     def run(self):
120         """
121         """
122         while 42:
123             try:
124                 self.loop()
125             except PlayerUnHandledError as err:
126                 #TODO: unhandled Player exceptions
127                 self.log.warning('Unhandled player exception: {}'.format(err))
128                 del(self.player)
129                 self.player = PlayerClient()
130                 time.sleep(10)
131             except PlayerError as err:
132                 self.log.warning('Player error: %s' % err)
133                 self.reconnect_player()
134
135     def loop(self):
136         """Dispatching callbacks to plugins
137         """
138         # hanging here untill a monitored event is raised in the player
139         if getattr(self, 'changed', False): # first iteration exception
140             self.changed = self.player.monitor()
141         else:  # first iteration goes through else
142             self.changed = ['playlist', 'player', 'skipped']
143         self.log.debug('changed: {}'.format(', '.join(self.changed)))
144         if 'playlist' in self.changed:
145             self.foreach_plugin('callback_playlist')
146         if ('player' in self.changed
147             or 'options' in self.changed):
148             self.foreach_plugin('callback_player')
149         if 'database' in self.changed:
150             self.foreach_plugin('callback_player_database')
151         if 'skipped' in self.changed:
152             if self.player.state == 'play':
153                 self.log.info('Playing: {}'.format(self.player.current))
154                 self.add_history()
155                 self.foreach_plugin('callback_next_song')
156         if self.need_tracks():
157             self.queue()
158
159 # VIM MODLINE
160 # vim: ai ts=4 sw=4 sts=4 expandtab