properly escape xml
[enigma2-plugins.git] / epgrefresh / src / EPGRefresh.py
1 # -*- coding: UTF-8 -*-
2 # To check if in Standby
3 import Screens.Standby
4
5 # eServiceReference
6 from enigma import eServiceReference, eServiceCenter
7
8 # ...
9 from ServiceReference import ServiceReference
10
11 # Timer
12 from EPGRefreshTimer import epgrefreshtimer, EPGRefreshTimerEntry, checkTimespan
13
14 # To calculate next timer execution
15 from time import time
16
17 # Plugin Config
18 from xml.etree.cElementTree import parse as cet_parse
19 from Tools.XMLTools import stringToXML
20 from os import path as path
21
22 # We want a list of unique services
23 from EPGRefreshService import EPGRefreshService
24
25 # Configuration
26 from Components.config import config
27
28 # Path to configuration
29 CONFIG = "/etc/enigma2/epgrefresh.xml"
30
31 class EPGRefresh:
32         """Simple Class to refresh EPGData"""
33
34         def __init__(self):
35                 # Initialize
36                 self.services = (set(), set())
37                 self.previousService = None
38                 self.forcedScan = False
39                 self.session = None
40                 self.beginOfTimespan = 0
41
42                 # Mtime of configuration files
43                 self.configMtime = -1
44
45                 # Read in Configuration
46                 self.readConfiguration()
47
48         def readConfiguration(self):
49                 # Check if file exists
50                 if not path.exists(CONFIG):
51                         return
52
53                 # Check if file did not change
54                 mtime = path.getmtime(CONFIG)
55                 if mtime == self.configMtime:
56                         return
57
58                 # Keep mtime
59                 self.configMtime = mtime
60
61                 # Empty out list
62                 self.services[0].clear()
63                 self.services[1].clear()
64
65                 # Open file
66                 configuration= cet_parse(CONFIG).getroot()
67
68                 # Add References
69                 for service in configuration.findall("service"):
70                         value = service.text
71                         if value:
72                                 # strip all after last : (custom name)
73                                 pos = value.rfind(':')
74                                 if pos != -1:
75                                         value = value[:pos+1]
76
77                                 duration = service.get('duration', None)
78                                 duration = duration and int(duration)
79
80                                 self.services[0].add(EPGRefreshService(value, duration))
81                 for bouquet in configuration.findall("bouquet"):
82                         value = bouquet.text
83                         if value:
84                                 duration = bouquet.get('duration', None)
85                                 duration = duration and int(duration)
86                                 self.services[1].add(EPGRefreshService(value, duration))
87
88         def saveConfiguration(self):
89                 # Generate List in RAM
90                 list = ['<?xml version="1.0" ?>\n<epgrefresh>\n\n']
91
92                 for service in self.services[0]:
93                         ref = ServiceReference(service.sref)
94                         list.extend([' <!-- ', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), ' -->\n'])
95                         list.append(' <service')
96                         if service.duration is not None:
97                                 list.extend([' duration="', str(service.duration), '"'])
98                         list.extend(['>', stringToXML(service.sref), '</service>\n'])
99                 for bouquet in self.services[1]:
100                         ref = ServiceReference(bouquet.sref)
101                         list.extend([' <!-- ', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), ' -->\n'])
102                         list.append(' <bouquet')
103                         if bouquet.duration is not None:
104                                 list.extend([' duration="', str(bouquet.duration), '"'])
105                         list.extend(['>', stringToXML(bouquet.sref), '</bouquet>\n'])
106
107                 list.append('\n</epgrefresh>')
108
109                 # Save to Flash
110                 file = open(CONFIG, 'w')
111                 file.writelines(list)
112
113                 file.close()
114
115         def forceRefresh(self, session = None):
116                 print "[EPGRefresh] Forcing start of EPGRefresh"
117                 if session is not None:
118                         self.session = session
119
120                 self.forcedScan = True
121                 self.prepareRefresh()
122
123         def start(self, session = None):
124                 if session is not None:
125                         self.session = session
126
127                 epgrefreshtimer.setRefreshTimer(self.createWaitTimer)
128
129         def stop(self):
130                 print "[EPGRefresh] Stopping Timer"
131                 epgrefreshtimer.clear()
132
133         def prepareRefresh(self):
134                 print "[EPGRefresh] About to start refreshing EPG"
135
136                 # Keep service
137                 self.previousService =  self.session.nav.getCurrentlyPlayingServiceReference()
138
139                 # Maybe read in configuration
140                 try:
141                         self.readConfiguration()
142                 except Exception, e:
143                         print "[EPGRefresh] Error occured while reading in configuration:", e
144
145                 # This will hold services which are not explicitely in our list
146                 additionalServices = []
147                 additionalBouquets = []
148
149                 # See if we are supposed to read in autotimer services
150                 if config.plugins.epgrefresh.inherit_autotimer.value:
151                         removeInstance = False
152                         try:
153                                 # Import Instance
154                                 from Plugins.Extensions.AutoTimer.plugin import autotimer
155
156                                 if autotimer is None:
157                                         removeInstance = True
158                                         # Create an instance
159                                         from Plugins.Extensions.AutoTimer.AutoTimer import AutoTimer
160                                         autotimer = AutoTimer()
161
162                                 # Read in configuration
163                                 autotimer.readXml()
164                         except Exception, e:
165                                 print "[EPGRefresh] Could not inherit AutoTimer Services:", e
166                         else:
167                                 # Fetch services
168                                 for timer in autotimer.getEnabledTimerList():
169                                         additionalServices.extend([EPGRefreshService(x, None) for x in timer.services])
170                                         additionalBouquets.extend([EPGRefreshService(x, None) for x in timer.bouquets])
171                         finally:
172                                 # Remove instance if there wasn't one before
173                                 if removeInstance:
174                                         autotimer = None
175
176                 serviceHandler = eServiceCenter.getInstance()
177                 for bouquet in self.services[1].union(additionalBouquets):
178                         myref = eServiceReference(bouquet.sref)
179                         list = serviceHandler.list(myref)
180                         if list is not None:
181                                 while 1:
182                                         s = list.getNext()
183                                         # TODO: I wonder if its sane to assume we get services here (and not just new lists)
184                                         if s.valid():
185                                                 additionalServices.append(EPGRefreshService(s.toString(), None))
186                                         else:
187                                                 break
188                 del additionalBouquets[:]
189
190                 scanServices = []
191                 channelIdList = []
192                 for scanservice in self.services[0].union(additionalServices):
193                         service = eServiceReference(scanservice.sref)
194                         if not service.valid() \
195                                 or (service.flags & (eServiceReference.isMarker|eServiceReference.isDirectory)):
196
197                                 continue
198
199                         channelID = '%08x%04x%04x' % (
200                                 service.getUnsignedData(4), # NAMESPACE
201                                 service.getUnsignedData(2), # TSID
202                                 service.getUnsignedData(3), # ONID
203                         )
204
205                         if channelID not in channelIdList:
206                                 scanServices.append(scanservice)
207                                 channelIdList.append(channelID)
208                 del additionalServices[:]
209
210                 # Debug
211                 #print "[EPGRefresh] Services we're going to scan:", ', '.join([repr(x) for x in scanServices])
212
213                 self.scanServices = scanServices
214                 self.refresh()
215
216         def cleanUp(self):
217                 config.plugins.epgrefresh.lastscan.value = int(time())
218                 config.plugins.epgrefresh.lastscan.save()
219
220                 # shutdown if we're supposed to go to deepstandby and not recording
221                 if not self.forcedScan and config.plugins.epgrefresh.afterevent.value \
222                         and not Screens.Standby.inTryQuitMainloop:
223
224                         self.session.open(
225                                 Screens.Standby.TryQuitMainloop,
226                                 1
227                         )
228
229                 self.forcedScan = False
230                 epgrefreshtimer.cleanup()
231
232                 # Zap back
233                 if self.previousService is not None or Screens.Standby.inStandby:
234                         self.session.nav.playService(self.previousService)
235
236         def refresh(self):
237                 if self.forcedScan:
238                         self.nextService()
239                 else:
240                         # Abort if a scan finished later than our begin of timespan
241                         if self.beginOfTimespan < config.plugins.epgrefresh.lastscan.value:
242                                 return
243                         if config.plugins.epgrefresh.force.value \
244                                 or (Screens.Standby.inStandby and \
245                                         not self.session.nav.RecordTimer.isRecording()):
246
247                                 self.nextService()
248                         # We don't follow our rules here - If the Box is still in Standby and not recording we won't reach this line
249                         else:
250                                 if not checkTimespan(
251                                         config.plugins.epgrefresh.begin.value,
252                                         config.plugins.epgrefresh.end.value):
253
254                                         print "[EPGRefresh] Gone out of timespan while refreshing, sorry!"
255                                         self.cleanUp()
256                                 else:
257                                         print "[EPGRefresh] Box no longer in Standby or Recording started, rescheduling"
258
259                                         # Recheck later
260                                         epgrefreshtimer.add(EPGRefreshTimerEntry(
261                                                         time() + config.plugins.epgrefresh.delay_standby.value*60,
262                                                         self.refresh,
263                                                         nocheck = True)
264                                         )
265
266         def createWaitTimer(self):
267                 self.beginOfTimespan = time()
268
269                 # Add wait timer to epgrefreshtimer
270                 epgrefreshtimer.add(EPGRefreshTimerEntry(time() + 30, self.prepareRefresh))
271
272         def nextService(self):
273                 # Debug
274                 print "[EPGRefresh] Maybe zap to next service"
275
276                 try:
277                         # Get next reference
278                         service = self.scanServices.pop(0)
279                 except IndexError:
280                         # Debug
281                         print "[EPGRefresh] Done refreshing EPG"
282
283                         # Clean up
284                         self.cleanUp()
285                 else:
286                         # Play next service
287                         self.session.nav.playService(eServiceReference(service.sref))
288
289                         # Start Timer
290                         delay = service.duration or config.plugins.epgrefresh.interval.value
291                         epgrefreshtimer.add(EPGRefreshTimerEntry(
292                                 time() + delay*60,
293                                 self.refresh,
294                                 nocheck = True)
295                         )
296
297 epgrefresh = EPGRefresh()