AutoPoller.py: use a thread for background parsing
[enigma2-plugins.git] / autotimer / src / AutoPoller.py
1 from __future__ import print_function
2
3 # Core functionality
4 from enigma import eTimer, ePythonMessagePump
5
6 # Config
7 from Components.config import config
8
9 # Notifications
10 from Tools.FuzzyDate import FuzzyTime
11 from Tools.Notifications import AddPopup
12 from Screens.MessageBox import MessageBox
13
14 NOTIFICATIONID = 'AutoTimerConflictEncounteredNotification'
15 SIMILARNOTIFICATIONID = 'AutoTimerSimilarUsedNotification'
16
17 from threading import Thread, Semaphore
18 try:
19         from Queue import Queue
20 except ImportError as ie:
21         from queue import Queue
22
23 class AutoPollerThread(Thread):
24         """Background thread where the EPG is parsed (unless initiated by the user)."""
25         def __init__(self):
26                 Thread.__init__(self)
27                 self.__semaphore = Semaphore(0)
28                 self.__queue = Queue(1)
29                 self.__pump = ePythonMessagePump()
30                 self.__pump.recv_msg.get().append(self.gotThreadMsg)
31                 self.__timer = eTimer()
32                 self.__timer.callback.append(self.timeout)
33                 self.running = False
34
35         def timeout(self):
36                 self.__semaphore.release()
37
38         def gotThreadMsg(self, msg):
39                 """Create Notifications if there is anything to display."""
40                 ret = self.__queue.get()
41                 conflicts = ret[4]
42                 if conflicts and config.plugins.autotimer.notifconflict.value:
43                         AddPopup(
44                                 _("%d conflict(s) encountered when trying to add new timers:\n%s") % (len(conflicts), '\n'.join([_("%s: %s at %s") % (x[4], x[0], FuzzyTime(x[2])) for x in conflicts])),
45                                 MessageBox.TYPE_INFO,
46                                 5,
47                                 NOTIFICATIONID
48                         )
49                 similars = ret[5]
50                 if similars and config.plugins.autotimer.notifsimilar.value:
51                         AddPopup(
52                                 _("%d conflict(s) solved with similar timer(s):\n%s") % (len(similars), '\n'.join([_("%s: %s at %s") % (x[4], x[0], FuzzyTime(x[2])) for x in similars])),
53                                 MessageBox.TYPE_INFO,
54                                 5,
55                                 SIMILARNOTIFICATIONID
56                         )
57
58         def start(self, initial=True):
59                 # NOTE: we wait for 10 seconds on initial launch to not delay enigma2 startup time
60                 if initial: delay = 10
61                 else: delay = config.plugins.autotimer.interval.value*3600
62
63                 self.__timer.startLongTimer(delay)
64                 Thread.start(self)
65
66         def stop(self):
67                 self.__timer.stop()
68                 self.running = False
69                 self.__semaphore.release()
70
71         def run(self):
72                 self.running = True
73                 while True:
74                         self.__semaphore.acquire()
75                         # NOTE: we have to check this here and not using the while to prevent the parser to be started on shutdown
76                         if not self.running: break
77
78                         from plugin import autotimer
79                         # Ignore any program errors
80                         try:
81                                 self.__queue.put(autotimer.parseEPG())
82                                 self.__pump.send(0)
83                         except Exception:
84                                 # Dump error to stdout
85                                 import traceback, sys
86                                 traceback.print_exc(file=sys.stdout)
87
88                         self.__timer.startLongTimer(config.plugins.autotimer.interval.value*3600)
89
90 class AutoPoller:
91         """Manages actual thread which does the polling. Used for convenience."""
92
93         def __init__(self):
94                 self.thread = AutoPollerThread()
95
96         def start(self, initial=True):
97                 self.thread.start(initial=initial)
98
99         def stop(self):
100                 self.thread.stop()
101                 # NOTE: while we don't need to join the thread, we should do so in case it's currently parsining
102                 self.thread.join()
103