1 # -*- coding: utf-8 -*-
2 # Copyright (c) 2009, 2010, 2011, 2013, 2014 Jack Kaliko <kaliko@azylum.org>
4 # This file is part of sima
6 # sima is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
11 # sima is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with sima. If not, see <http://www.gnu.org/licenses/>.
20 """Core Object dealing with plugins and player client
26 from collections import deque
27 from logging import getLogger
29 from .client import PlayerClient
30 from .client import PlayerError, PlayerUnHandledError
31 from .lib.simadb import SimaDB
32 from .lib.daemon import Daemon
33 from .utils.utils import SigHup
36 """Main class, plugin and player management
39 def __init__(self, conf):
41 Daemon.__init__(self, conf.get('daemon', 'pidfile'))
44 self.sdb = SimaDB(db_path=conf.get('sima', 'db_file'))
45 PlayerClient.database = self.sdb
46 self.log = getLogger('sima')
48 self.player = self.__get_player() # Player client
51 except (PlayerError, PlayerUnHandledError) as err:
52 self.log.warning('Player: {}'.format(err))
53 self.short_history = deque(maxlen=60)
55 def __get_player(self):
56 """Instanciate the player"""
57 host = self.config.get('MPD', 'host')
58 port = self.config.get('MPD', 'port')
59 pswd = self.config.get('MPD', 'password', fallback=None)
60 return PlayerClient(host, port, pswd)
62 def add_history(self):
63 self.short_history.appendleft(self.player.current)
65 def register_plugin(self, plugin_class):
66 """Registers plubin in Sima instance..."""
67 self.plugins.append(plugin_class(self))
69 def foreach_plugin(self, method, *args, **kwds):
70 """Plugin's callbacks dispatcher"""
71 for plugin in self.plugins:
72 #self.log.debug('dispatching {0} to {1}'.format(method, plugin))
73 getattr(plugin, method)(*args, **kwds)
75 def need_tracks(self):
77 self.log.debug('Queueing disabled!')
79 queue = self.player.queue
80 queue_trigger = self.config.getint('sima', 'queue_length')
81 self.log.debug('Currently {0} track(s) ahead. (target {1})'.format(
82 len(queue), queue_trigger))
83 if len(queue) < queue_trigger:
89 for plugin in self.plugins:
90 pl_callback = getattr(plugin, 'callback_need_track')()
92 to_add.extend(pl_callback)
94 self.log.warning('Queue plugins returned nothing!')
95 for plugin in self.plugins:
96 pl_callback = getattr(plugin, 'callback_need_track_fb')()
98 to_add.extend(pl_callback)
100 self.player.add(track)
102 def reconnect_player(self):
103 """Trying to reconnect cycling through longer timeout
104 cycle : 5s 10s 1m 5m 20m 1h
106 sleepfor = [5, 10, 60, 300, 1200, 3600]
108 tmp = sleepfor.pop(0)
110 self.log.info('Trying to reconnect in {:>4d} seconds'.format(tmp))
113 self.player.connect()
116 except PlayerUnHandledError as err:
117 #TODO: unhandled Player exceptions
118 self.log.warning('Unhandled player exception: %s' % err)
119 self.log.info('Got reconnected')
122 def hup_handler(self, signum, frame):
123 self.log.warning('Caught a sighup!')
124 self.player.disconnect()
125 self.foreach_plugin('shutdown')
126 raise SigHup('SIGHUP caught!')
129 """General shutdown method
131 self.log.warning('Starting shutdown.')
132 self.player.disconnect()
133 self.foreach_plugin('shutdown')
135 self.log.info('The way is shut, it was made by those who are dead. '
136 'And the dead keep it…')
137 self.log.info('bye...')
145 except PlayerUnHandledError as err:
146 #TODO: unhandled Player exceptions
147 self.log.warning('Unhandled player exception: {}'.format(err))
149 self.player = PlayerClient()
151 except PlayerError as err:
152 self.log.warning('Player error: %s' % err)
153 self.reconnect_player()
157 """Dispatching callbacks to plugins
159 # hanging here untill a monitored event is raised in the player
160 if getattr(self, 'changed', False): # first iteration exception
161 self.changed = self.player.monitor()
162 else: # first iteration goes through else
163 self.changed = ['playlist', 'player', 'skipped']
164 self.log.debug('changed: {}'.format(', '.join(self.changed)))
165 if 'playlist' in self.changed:
166 self.foreach_plugin('callback_playlist')
167 if ('player' in self.changed
168 or 'options' in self.changed):
169 self.foreach_plugin('callback_player')
170 if 'database' in self.changed:
171 self.foreach_plugin('callback_player_database')
172 if 'skipped' in self.changed:
173 if self.player.state == 'play':
174 self.log.info('Playing: {}'.format(self.player.current))
176 self.foreach_plugin('callback_next_song')
177 if self.need_tracks():
181 # vim: ai ts=4 sw=4 sts=4 expandtab