initial checkin of MerlinSkinThemes
[enigma2-plugins.git] / simplerss / src / RSSPoller.py
1 from __future__ import print_function
2
3 from Components.config import config
4 from enigma import eTimer
5
6 from Tools.Notifications import AddPopup
7 from Screens.MessageBox import MessageBox
8
9 from RSSFeed import BaseFeed, UniversalFeed
10
11 from twisted.web.client import getPage
12 from xml.etree.cElementTree import fromstring as cElementTree_fromstring
13
14 from GoogleReader import GoogleReader
15
16 NOTIFICATIONID = 'SimpleRSSUpdateNotification'
17
18 update_callbacks = []
19
20 class RSSPoller:
21         """Keeps all Feed and takes care of (automatic) updates"""
22
23         def __init__(self, poll = True):
24                 # Timer
25                 self.poll_timer = eTimer()
26                 self.poll_timer_conn = self.poll_timer.timeout.connect(self.poll)
27                 self.do_poll = poll
28
29                 # this indicates we're reloading the list of feeds
30                 self.reloading = False
31
32                 self.newItemFeed = BaseFeed(
33                         "",
34                         _("New Items"),
35                         _("New Items since last Auto-Update"),
36                 )
37
38                 # Generate Feeds
39                 self.feeds = [
40                         UniversalFeed(
41                                 x.uri.value,
42                                 x.autoupdate.value
43                         )
44                                 for x in config.plugins.simpleRSS.feed
45                 ]
46
47                 if not config.plugins.simpleRSS.enable_google_reader.value:
48                         if poll:
49                                 self.poll_timer.start(0, 1)
50                 else:
51                         self.googleReader = GoogleReader(config.plugins.simpleRSS.google_username.value, config.plugins.simpleRSS.google_password.value)
52                         self.googleReader.login().addCallback(self.googleLoggedIn).addErrback(self.googleLoginFailed)
53
54                 # Initialize Vars
55                 self.current_feed = 0
56
57         def googleLoggedIn(self, sid = None):
58                 self.googleReader.getSubscriptionList().addCallback(self.googleSubscriptionList).addErrback(self.googleSubscriptionFailed)
59
60         def googleLoginFailed(self, res = None):
61                 AddPopup(
62                         _("Failed to login to Google Reader."),
63                         MessageBox.TYPE_ERROR,
64                         5,
65                 )
66
67                 self.reloading = False
68                 if self.do_poll:
69                         self.poll_timer.start(0, 1)
70
71         def googleSubscriptionList(self, subscriptions = None):
72                 self.feeds.extend(subscriptions)
73
74                 self.reloading = False
75                 if self.do_poll:
76                         self.doCallback()
77                         self.poll_timer.start(0, 1)
78
79         def googleSubscriptionFailed(self, res = None):
80                 AddPopup(
81                         _("Failed to get subscriptions from Google Reader."),
82                         MessageBox.TYPE_ERROR,
83                         5,
84                 )
85
86                 self.reloading = False
87                 if self.do_poll:
88                         self.poll_timer.start(0, 1)
89
90         def addCallback(self, callback):
91                 if callback not in update_callbacks:
92                         update_callbacks.append(callback)
93
94         def removeCallback(self, callback):
95                 if callback in update_callbacks:
96                         update_callbacks.remove(callback)
97
98         def doCallback(self, id = None):
99                 for callback in update_callbacks:
100                         try:
101                                 callback(id)
102                         except Exception:
103                                 pass
104
105         def error(self, error = ""):
106                 print("[SimpleRSS] failed to fetch feed:", error)
107
108                 # Assume its just a temporary failure and jump over to next feed
109                 self.next_feed()
110
111         def _gotPage(self, data, id = None, callback = False, errorback = None):
112                 # workaround: exceptions in gotPage-callback were ignored
113                 try:
114                         self.gotPage(data, id)
115                         if callback:
116                                 self.doCallback(id)
117                 except NotImplementedError as errmsg:
118                         # Don't show this error when updating in background
119                         if id is not None:
120                                 AddPopup(
121                                         _("Sorry, this type of feed is unsupported:\n%s") % (str(errmsg)),
122                                         MessageBox.TYPE_INFO,
123                                         5,
124                                 )
125                         else:
126                                 # We don't want to stop updating just because one feed is broken
127                                 self.next_feed()
128                 except Exception:
129                         import traceback, sys
130                         traceback.print_exc(file=sys.stdout)
131                         # Errorback given, call it (asumme we don't need do restart timer!)
132                         if errorback is not None:
133                                 errorback()
134                                 return
135                         # Assume its just a temporary failure and jump over to next feed
136                         self.next_feed()
137
138         def gotPage(self, data, id = None):
139                 feed = cElementTree_fromstring(data)
140
141                 # For Single-Polling
142                 if id is not None:
143                         self.feeds[id].gotFeed(feed)
144                         print("[SimpleRSS] single feed parsed...")
145                         return
146
147                 new_items = self.feeds[self.current_feed].gotFeed(feed)
148
149                 print("[SimpleRSS] feed parsed...")
150
151                 # Append new items to locally bound ones
152                 if new_items is not None:
153                         self.newItemFeed.history.extend(new_items)
154
155                 # Start Timer so we can either fetch next feed or show new_items
156                 self.next_feed()
157
158         def singlePoll(self, id, callback = False, errorback = None):
159                 getPage(self.feeds[id].uri).addCallback(self._gotPage, id, callback, errorback).addErrback(errorback)
160
161         def poll(self):
162                 # Reloading, reschedule
163                 if self.reloading:
164                         print("[SimpleRSS] timer triggered while reloading, rescheduling")
165                         self.poll_timer.start(10000, 1)
166                 # End of List
167                 elif len(self.feeds) <= self.current_feed:
168                         # New Items
169                         if self.newItemFeed.history:
170                                 print("[SimpleRSS] got new items, calling back")
171                                 self.doCallback()
172
173                                 # Inform User
174                                 update_notification_value = config.plugins.simpleRSS.update_notification.value
175                                 if update_notification_value == "preview":
176                                         from RSSScreens import RSSFeedView
177
178                                         from Tools.Notifications import AddNotificationWithID, RemovePopup
179
180                                         RemovePopup(NOTIFICATIONID)
181
182                                         AddNotificationWithID(
183                                                 NOTIFICATIONID,
184                                                 RSSFeedView,
185                                                 self.newItemFeed,
186                                                 newItems = True
187                                         )
188                                 elif update_notification_value == "notification":
189                                         AddPopup(
190                                                 _("Received %d new news item(s).") % (len(self.newItemFeed.history)),
191                                                 MessageBox.TYPE_INFO,
192                                                 5,
193                                                 NOTIFICATIONID
194                                         )
195                                 elif update_notification_value == "ticker":
196                                         from RSSTickerView import tickerView
197                                         if not tickerView:
198                                                 print("[SimpleRSS] missing ticker instance, something with my code is wrong :-/")
199                                         else:
200                                                 tickerView.display(self.newItemFeed)
201                         # No new Items
202                         else:
203                                 print("[SimpleRSS] no new items")
204
205                         self.current_feed = 0
206                         self.poll_timer.startLongTimer(config.plugins.simpleRSS.interval.value*60)
207                 # It's updating-time
208                 else:
209                         # Assume we're cleaning history if current feed is 0
210                         clearHistory = self.current_feed == 0
211                         if config.plugins.simpleRSS.update_notification.value != "none":
212                                 from Tools import Notifications
213                                 if hasattr(Notifications, 'notificationQueue'):
214                                         notifications = Notifications.notificationQueue.queue
215                                         current_notifications = Notifications.notificationQueue.current
216                                         handler = lambda note: (note.fnc, note.screen, note.args, note.kwargs, note.id)
217                                         handler_current = lambda note: (note[0].id,)
218                                 else:
219                                         notifications = Notifications.notifications
220                                         current_notifications = Notifications.current_notifications
221                                         handler_current = handler = lambda note: note
222
223                                 for x in current_notifications:
224                                         if handler_current(x)[0] == NOTIFICATIONID:
225                                                 print("[SimpleRSS] timer triggered while preview on screen, rescheduling")
226                                                 self.poll_timer.start(10000, 1)
227                                                 return
228
229                                 if clearHistory:
230                                         for x in notifications:
231                                                 if handler(x)[4] == NOTIFICATIONID:
232                                                         print("[SimpleRSS] wont wipe history because it was never read")
233                                                         clearHistory = False
234                                                         break
235
236                         if clearHistory:
237                                 del self.newItemFeed.history[:]
238
239                         # Feed supposed to autoupdate
240                         feed = self.feeds[self.current_feed]
241
242                         if feed.autoupdate:
243                                 getPage(feed.uri).addCallback(self._gotPage).addErrback(self.error)
244                         # Go to next feed
245                         else:
246                                 print("[SimpleRSS] passing feed")
247                                 self.next_feed()
248
249         def next_feed(self):
250                 self.current_feed += 1
251                 self.poll_timer.start(1000, 1)
252
253         def shutdown(self):
254                 self.poll_timer_conn = None
255                 self.poll_timer = None
256                 self.do_poll = False
257
258         def triggerReload(self):
259                 self.reloading = True
260
261                 newfeeds = []
262                 oldfeeds = self.feeds
263                 found = False
264                 for x in config.plugins.simpleRSS.feed:
265                         for feed in oldfeeds:
266                                 if x.uri.value == feed.uri:
267                                         # Update possibly different autoupdate value
268                                         feed.autoupdate = x.autoupdate.value
269                                         newfeeds.append(feed) # Append to new Feeds
270                                         oldfeeds.remove(feed) # Remove from old Feeds
271                                         found = True
272                                         break
273                         if not found:
274                                 newfeeds.append(
275                                         UniversalFeed(
276                                                 x.uri.value,
277                                                 x.autoupdate.value
278                                 ))
279                         found = False
280
281                 self.feeds = newfeeds
282
283                 if config.plugins.simpleRSS.enable_google_reader.value:
284                         self.googleReader = GoogleReader(config.plugins.simpleRSS.google_username.value, config.plugins.simpleRSS.google_password.value)
285                         self.googleReader.login().addCallback(self.googleLoggedIn).addErrback(self.googleLoginFailed)
286                 else:
287                         self.reloading = False
288