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