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