1 # -*- coding: utf-8 -*-
2 """Core Object dealing with plugins and player client
5 __version__ = '0.12.0pr1'
6 __author__ = 'kaliko jack'
7 __url__ = 'git://git.kaliko.me/sima.git'
12 from collections import deque
13 from logging import getLogger
15 from .client import PlayerClient
16 from .client import PlayerError, PlayerUnHandledError
17 from .lib.simadb import SimaDB
18 from .lib.daemon import Daemon
19 from .utils.utils import SigHup
22 """Main class, plugin and player management
25 def __init__(self, conf):
27 Daemon.__init__(self, conf.get('daemon', 'pidfile'))
30 self.sdb = SimaDB(db_path=conf.get('sima', 'db_file'))
31 PlayerClient.database = self.sdb
32 self.log = getLogger('sima')
34 self.player = self.__get_player() # Player client
37 except (PlayerError, PlayerUnHandledError) as err:
38 self.log.error('Fails to connect player: {}'.format(err))
40 self.short_history = deque(maxlen=60)
42 def __get_player(self):
43 """Instanciate the player"""
44 host = self.config.get('MPD', 'host')
45 port = self.config.get('MPD', 'port')
46 pswd = self.config.get('MPD', 'password', fallback=None)
47 return PlayerClient(host, port, pswd)
49 def add_history(self):
50 self.short_history.appendleft(self.player.current)
52 def register_plugin(self, plugin_class):
53 """Registers plubin in Sima instance..."""
54 self.plugins.append(plugin_class(self))
56 def foreach_plugin(self, method, *args, **kwds):
57 """Plugin's callbacks dispatcher"""
58 for plugin in self.plugins:
59 #self.log.debug('dispatching {0} to {1}'.format(method, plugin))
60 getattr(plugin, method)(*args, **kwds)
62 def need_tracks(self):
64 self.log.debug('Queueing disabled!')
66 queue = self.player.queue
67 queue_trigger = self.config.getint('sima', 'queue_length')
68 self.log.debug('Currently {0} track(s) ahead. (target {1})'.format(
69 len(queue), queue_trigger))
70 if len(queue) < queue_trigger:
76 for plugin in self.plugins:
77 pl_callback = getattr(plugin, 'callback_need_track')()
79 to_add.extend(pl_callback)
81 self.log.warning('Queue plugins returned nothing!')
82 for plugin in self.plugins:
83 pl_callback = getattr(plugin, 'callback_need_track_fb')()
85 to_add.extend(pl_callback)
87 self.player.add(track)
89 def reconnect_player(self):
90 """Trying to reconnect cycling through longer timeout
91 cycle : 5s 10s 1m 5m 20m 1h
93 sleepfor = [5, 10, 60, 300, 1200, 3600]
97 self.log.info('Trying to reconnect in {:>4d} seconds'.format(tmp))
100 self.player.connect()
103 except PlayerUnHandledError as err:
104 #TODO: unhandled Player exceptions
105 self.log.warning('Unhandled player exception: %s' % err)
106 self.log.info('Got reconnected')
109 def hup_handler(self, signum, frame):
110 self.log.warning('Caught a sighup!')
111 self.player.disconnect()
112 self.foreach_plugin('shutdown')
113 raise SigHup('SIGHUP caught!')
116 """General shutdown method
118 self.log.warning('Starting shutdown.')
119 self.player.disconnect()
120 self.foreach_plugin('shutdown')
122 self.log.info('The way is shut, it was made by those who are dead. '
123 'And the dead keep it…')
124 self.log.info('bye...')
132 except PlayerUnHandledError as err:
133 #TODO: unhandled Player exceptions
134 self.log.warning('Unhandled player exception: {}'.format(err))
136 self.player = PlayerClient()
138 except PlayerError as err:
139 self.log.warning('Player error: %s' % err)
140 self.reconnect_player()
144 """Dispatching callbacks to plugins
146 # hanging here untill a monitored event is raised in the player
147 if getattr(self, 'changed', False): # first iteration exception
148 self.changed = self.player.monitor()
149 else: # first iteration goes through else
150 self.changed = ['playlist', 'player', 'skipped']
151 self.log.debug('changed: {}'.format(', '.join(self.changed)))
152 if 'playlist' in self.changed:
153 self.foreach_plugin('callback_playlist')
154 if ('player' in self.changed
155 or 'options' in self.changed):
156 self.foreach_plugin('callback_player')
157 if 'database' in self.changed:
158 self.foreach_plugin('callback_player_database')
159 if 'skipped' in self.changed:
160 if self.player.state == 'play':
161 self.log.info('Playing: {}'.format(self.player.current))
163 self.foreach_plugin('callback_next_song')
164 if self.need_tracks():
168 # vim: ai ts=4 sw=4 sts=4 expandtab