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