EPGRefresh/AutoTimer: another fix for epgrefresh & autotimer, it works for me now...
[enigma2-plugins.git] / epgrefresh / src / EPGRefresh.py
1 # -*- coding: UTF-8 -*-
2 from __future__ import print_function
3
4 # To check if in Standby
5 import Screens.Standby
6
7 # eServiceReference
8 from enigma import eServiceReference, eServiceCenter
9
10 # ...
11 from ServiceReference import ServiceReference
12
13 # Timer
14 from EPGRefreshTimer import epgrefreshtimer, EPGRefreshTimerEntry, checkTimespan
15
16 # To calculate next timer execution
17 from time import time
18
19 # Plugin Config
20 from xml.etree.cElementTree import parse as cet_parse
21 from Tools.XMLTools import stringToXML
22 from os import path as path
23
24 # We want a list of unique services
25 from EPGRefreshService import EPGRefreshService
26
27 from OrderedSet import OrderedSet
28
29 # Configuration
30 from Components.config import config
31
32 # MessageBox
33 from Screens.MessageBox import MessageBox
34 from Tools import Notifications
35 from Tools.BoundFunction import boundFunction
36
37 # ... II
38 from . import _, NOTIFICATIONID
39 from MainPictureAdapter import MainPictureAdapter
40 from PipAdapter import PipAdapter
41 from RecordAdapter import RecordAdapter
42
43 # Path to configuration
44 CONFIG = "/etc/enigma2/epgrefresh.xml"
45 XML_VERSION = "1"
46
47 class EPGRefresh:
48         """Simple Class to refresh EPGData"""
49
50         def __init__(self):
51                 # Initialize
52                 self.services = (OrderedSet(), OrderedSet())
53                 self.forcedScan = False
54                 self.session = None
55                 self.beginOfTimespan = 0
56                 self.refreshAdapter = None
57
58                 # Mtime of configuration files
59                 self.configMtime = -1
60
61                 # Read in Configuration
62                 self.readConfiguration()
63
64         def readConfiguration(self):
65                 # Check if file exists
66                 if not path.exists(CONFIG):
67                         return
68
69                 # Check if file did not change
70                 mtime = path.getmtime(CONFIG)
71                 if mtime == self.configMtime:
72                         return
73
74                 # Keep mtime
75                 self.configMtime = mtime
76
77                 # Empty out list
78                 self.services[0].clear()
79                 self.services[1].clear()
80
81                 # Open file
82                 configuration = cet_parse(CONFIG).getroot()
83                 version = configuration.get("version", None)
84                 if version is None:
85                         factor = 60
86                 else: #if version == "1"
87                         factor = 1
88
89                 # Add References
90                 for service in configuration.findall("service"):
91                         value = service.text
92                         if value:
93                                 # strip all after last : (custom name)
94                                 pos = value.rfind(':')
95                                 if pos != -1:
96                                         value = value[:pos+1]
97
98                                 duration = service.get('duration', None)
99                                 duration = duration and int(duration)*factor
100
101                                 self.services[0].add(EPGRefreshService(value, duration))
102                 for bouquet in configuration.findall("bouquet"):
103                         value = bouquet.text
104                         if value:
105                                 duration = bouquet.get('duration', None)
106                                 duration = duration and int(duration)
107                                 self.services[1].add(EPGRefreshService(value, duration))
108
109         def buildConfiguration(self, webif = False):
110                 list = ['<?xml version="1.0" ?>\n<epgrefresh version="', XML_VERSION, '">\n\n']
111
112                 if webif:
113                         for serviceref in self.services[0].union(self.services[1]):
114                                 ref = ServiceReference(str(serviceref))
115                                 list.extend((
116                                         ' <e2service>\n',
117                                         '  <e2servicereference>', str(serviceref), '</e2servicereference>\n',
118                                         '  <e2servicename>', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), '</e2servicename>\n',
119                                         ' </e2service>\n',
120                                 ))
121                 else:
122                         for service in self.services[0]:
123                                 ref = ServiceReference(service.sref)
124                                 list.extend((' <!--', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), '-->\n', ' <service'))
125                                 if service.duration is not None:
126                                         list.extend((' duration="', str(service.duration), '"'))
127                                 list.extend(('>', stringToXML(service.sref), '</service>\n'))
128                         for bouquet in self.services[1]:
129                                 ref = ServiceReference(bouquet.sref)
130                                 list.extend((' <!--', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), '-->\n', ' <bouquet'))
131                                 if bouquet.duration is not None:
132                                         list.extend((' duration="', str(bouquet.duration), '"'))
133                                 list.extend(('>', stringToXML(bouquet.sref), '</bouquet>\n'))
134
135                 list.append('\n</epgrefresh>')
136
137                 return list
138
139         def saveConfiguration(self):
140                 file = open(CONFIG, 'w')
141                 file.writelines(self.buildConfiguration())
142
143                 file.close()
144
145         def maybeStopAdapter(self):
146                 if self.refreshAdapter:
147                         self.refreshAdapter.stop()
148                         self.refreshAdapter = None
149
150         def forceRefresh(self, session = None):
151                 print("[EPGRefresh] Forcing start of EPGRefresh")
152                 if self.session is None:
153                         if session is not None:
154                                 self.session = session
155                         else:
156                                 return False
157
158                 self.forcedScan = True
159                 self.prepareRefresh()
160                 return True
161
162         def start(self, session = None):
163                 if session is not None:
164                         self.session = session
165
166                 epgrefreshtimer.setRefreshTimer(self.createWaitTimer)
167
168         def stop(self):
169                 print("[EPGRefresh] Stopping Timer")
170                 self.maybeStopAdapter()
171                 epgrefreshtimer.clear()
172
173         def addServices(self, fromList, toList, channelIds):
174                 for scanservice in fromList:
175                         service = eServiceReference(scanservice.sref)
176                         if not service.valid() \
177                                 or (service.flags & (eServiceReference.isMarker|eServiceReference.isDirectory)):
178
179                                 continue
180
181                         channelID = '%08x%04x%04x' % (
182                                 service.getUnsignedData(4), # NAMESPACE
183                                 service.getUnsignedData(2), # TSID
184                                 service.getUnsignedData(3), # ONID
185                         )
186
187                         if channelID not in channelIds:
188                                 toList.append(scanservice)
189                                 channelIds.append(channelID)
190
191         def generateServicelist(self, services, bouquets):
192                 # This will hold services which are not explicitely in our list
193                 additionalServices = []
194                 additionalBouquets = []
195
196                 # See if we are supposed to read in autotimer services
197                 if config.plugins.epgrefresh.inherit_autotimer.value:
198                         removeInstance = False
199                         try:
200                                 # Import Instance
201                                 from Plugins.Extensions.AutoTimer.plugin import autotimer
202
203                                 if autotimer is None:
204                                         removeInstance = True
205                                         # Create an instance
206                                         from Plugins.Extensions.AutoTimer.AutoTimer import AutoTimer
207                                         autotimer = AutoTimer()
208
209                                 # Read in configuration
210                                 autotimer.readXml()
211                         except Exception as e:
212                                 print("[EPGRefresh] Could not inherit AutoTimer Services:", e)
213                         else:
214                                 # Fetch services
215                                 for timer in autotimer.getEnabledTimerList():
216                                         additionalServices.extend([EPGRefreshService(x, None) for x in timer.services])
217                                         additionalBouquets.extend([EPGRefreshService(x, None) for x in timer.bouquets])
218                         finally:
219                                 # Remove instance if there wasn't one before
220                                 if removeInstance:
221                                         autotimer = None
222
223                 scanServices = []
224                 channelIdList = []
225                 self.addServices(services, scanServices, channelIdList)
226
227                 serviceHandler = eServiceCenter.getInstance()
228                 for bouquet in bouquets.union(additionalBouquets):
229                         myref = eServiceReference(bouquet.sref)
230                         list = serviceHandler.list(myref)
231                         if list is not None:
232                                 while 1:
233                                         s = list.getNext()
234                                         # TODO: I wonder if its sane to assume we get services here (and not just new lists)
235                                         if s.valid():
236                                                 additionalServices.append(EPGRefreshService(s.toString(), None))
237                                         else:
238                                                 break
239                 del additionalBouquets[:]
240
241                 self.addServices(additionalServices, scanServices, channelIdList)
242                 del additionalServices[:]
243
244                 return scanServices
245
246         def prepareRefresh(self):
247                 print("[EPGRefresh] About to start refreshing EPG")
248
249                 # Maybe read in configuration
250                 try:
251                         self.readConfiguration()
252                 except Exception as e:
253                         print("[EPGRefresh] Error occured while reading in configuration:", e)
254
255                 self.scanServices = self.generateServicelist(self.services[0], self.services[1])
256
257                 # Debug
258                 print("[EPGRefresh] Services we're going to scan:", ', '.join([repr(x) for x in self.scanServices]))
259
260                 self.maybeStopAdapter()
261                 # NOTE: start notification is handled in adapter initializer
262                 if config.plugins.epgrefresh.adapter.value.startswith("pip"):
263                         hidden = config.plugins.epgrefresh.adapter.value == "pip_hidden"
264                         refreshAdapter = PipAdapter(self.session, hide=hidden)
265                 elif config.plugins.epgrefresh.adapter.value.startswith("record"):
266                         refreshAdapter = RecordAdapter(self.session)
267                 else:
268                         refreshAdapter = MainPictureAdapter(self.session)
269
270                 if (not refreshAdapter.backgroundCapable and Screens.Standby.inStandby) or not refreshAdapter.prepare():
271                         print("[EPGRefresh] Adapter is not able to run in background or not available, falling back to MainPictureAdapter")
272                         refreshAdapter = MainPictureAdapter(self.session)
273                         refreshAdapter.prepare()
274                 self.refreshAdapter = refreshAdapter
275
276                 self.refresh()
277
278         def cleanUp(self):
279                 config.plugins.epgrefresh.lastscan.value = int(time())
280                 config.plugins.epgrefresh.lastscan.save()
281
282                 # Eventually force autotimer to parse epg
283                 if config.plugins.epgrefresh.parse_autotimer.value:
284                         removeInstance = False
285                         try:
286                                 # Import Instance
287                                 from Plugins.Extensions.AutoTimer.plugin import autotimer
288
289                                 if autotimer is None:
290                                         removeInstance = True
291                                         # Create an instance
292                                         from Plugins.Extensions.AutoTimer.AutoTimer import AutoTimer
293                                         autotimer = AutoTimer()
294
295                                 # Parse EPG
296                                 autotimer.parseEPGAsync(simulateOnly=False).addCallback( boundFunction(self.finish, removeInstance=removeInstance) )
297                                 return
298                         except Exception as e:
299                                 print("[EPGRefresh] Could not start AutoTimer:", e)
300
301                 self.finish()
302
303         def finish(self, *args, **kwargs):
304                 removeInstance=kwargs.get("removeInstance")
305                 if not Screens.Standby.inStandby and not config.plugins.epgrefresh.background and config.plugins.epgrefresh.enablemessage.value:
306                         Notifications.AddPopup(_("EPG refresh finished."), MessageBox.TYPE_INFO, 4, NOTIFICATIONID)
307                 self.forcedScan = False
308                 epgrefreshtimer.cleanup()
309                 self.maybeStopAdapter()
310
311                 # Remove instance if there wasn't one before
312                 if removeInstance:
313                         try:
314                                 # Import Instance
315                                 from Plugins.Extensions.AutoTimer.plugin import autotimer
316                                 autotimer = None
317                         except:
318                                 pass
319                 # shutdown if we're supposed to go to deepstandby and not recording
320                 if not self.forcedScan and config.plugins.epgrefresh.afterevent.value \
321                         and not Screens.Standby.inTryQuitMainloop:
322
323                         self.session.open(
324                                 Screens.Standby.TryQuitMainloop,
325                                 1
326                         )
327
328         def refresh(self):
329                 if self.forcedScan:
330                         self.nextService()
331                 else:
332                         # Abort if a scan finished later than our begin of timespan
333                         if self.beginOfTimespan < config.plugins.epgrefresh.lastscan.value:
334                                 return
335                         if config.plugins.epgrefresh.force.value \
336                                 or (Screens.Standby.inStandby and \
337                                         not self.session.nav.RecordTimer.isRecording()):
338
339                                 self.nextService()
340                         # We don't follow our rules here - If the Box is still in Standby and not recording we won't reach this line
341                         else:
342                                 if not checkTimespan(
343                                         config.plugins.epgrefresh.begin.value,
344                                         config.plugins.epgrefresh.end.value):
345
346                                         print("[EPGRefresh] Gone out of timespan while refreshing, sorry!")
347                                         self.cleanUp()
348                                 else:
349                                         print("[EPGRefresh] Box no longer in Standby or Recording started, rescheduling")
350
351                                         # Recheck later
352                                         epgrefreshtimer.add(EPGRefreshTimerEntry(
353                                                         time() + config.plugins.epgrefresh.delay_standby.value*60,
354                                                         self.refresh,
355                                                         nocheck = True)
356                                         )
357
358         def createWaitTimer(self):
359                 self.beginOfTimespan = time()
360
361                 # Add wait timer to epgrefreshtimer
362                 epgrefreshtimer.add(EPGRefreshTimerEntry(time() + 30, self.prepareRefresh))
363
364         def nextService(self):
365                 # Debug
366                 print("[EPGRefresh] Maybe zap to next service")
367
368                 try:
369                         # Get next reference
370                         service = self.scanServices.pop(0)
371                 except IndexError:
372                         # Debug
373                         print("[EPGRefresh] Done refreshing EPG")
374
375                         # Clean up
376                         self.cleanUp()
377                 else:
378                         # If the current adapter is unable to run in background and we are in fact in background now,
379                         # fall back to main picture
380                         if (not self.refreshAdapter.backgroundCapable and Screens.Standby.inStandby):
381                                 print("[EPGRefresh] Adapter is not able to run in background or not available, falling back to MainPictureAdapter")
382                                 self.maybeStopAdapter()
383                                 self.refreshAdapter = MainPictureAdapter(self.session)
384                                 self.refreshAdapter.prepare()
385
386                         # Play next service
387                         # XXX: we might want to check the return value
388                         self.refreshAdapter.play(eServiceReference(service.sref))
389
390                         # Start Timer
391                         delay = service.duration or config.plugins.epgrefresh.interval_seconds.value
392                         epgrefreshtimer.add(EPGRefreshTimerEntry(
393                                 time() + delay,
394                                 self.refresh,
395                                 nocheck = True)
396                         )
397
398 epgrefresh = EPGRefresh()