create initial defaultTimer on __init__ so the plugin won't crash if it lacks a confi...
[enigma2-plugins.git] / autotimer / src / AutoTimer.py
1 # Plugins Config
2 from xml.dom.minidom import parse as minidom_parse
3 from Tools.XMLTools import stringToXML
4 from os import path as os_path
5
6 # Navigation (RecordTimer)
7 import NavigationInstance
8
9 # Timer
10 from ServiceReference import ServiceReference
11 from RecordTimer import RecordTimerEntry, AFTEREVENT
12 from Components.TimerSanityCheck import TimerSanityCheck
13
14 # Timespan
15 from time import localtime, time
16
17 # EPGCache & Event
18 from enigma import eEPGCache, eServiceReference
19
20 # Enigma2 Config
21 from Components.config import config
22
23 # AutoTimer Component
24 from AutoTimerComponent import AutoTimerComponent
25
26 XML_CONFIG = "/etc/enigma2/autotimer.xml"
27 CURRENT_CONFIG_VERSION = "5"
28
29 def getValue(definitions, default):
30         # Initialize Output
31         ret = ""
32
33         # How many definitions are present
34         try:
35                 childNodes = definitions.childNodes
36         except:
37                 Len = len(definitions)
38                 if Len > 0:
39                         childNodes = definitions[Len-1].childNodes
40                 else:
41                         childNodes = []
42
43         # Iterate through nodes of last one
44         for node in childNodes:
45                 # Append text if we have a text node
46                 if node.nodeType == node.TEXT_NODE:
47                         ret = ret + node.data
48
49         # Return stripped output or (if empty) default
50         return ret.strip() or default
51
52 def getTimeDiff(timer, begin, end):
53         if begin <= timer.begin <= end:
54                 return end - timer.begin
55         elif timer.begin <= begin <= timer.end:
56                 return timer.end - begin
57         return 0
58
59 class AutoTimerIgnoreTimerException(Exception):
60         def __init__(self, cause):
61                 self.cause = cause
62
63         def __str__(self):
64                 return "[AutoTimer] " + str(self.cause)
65
66         def __repr__(self):
67                 return str(type(self))
68
69 class AutoTimer:
70         """Read and save xml configuration, query EPGCache"""
71
72         def __init__(self):
73                 # Keep EPGCache
74                 self.epgcache = eEPGCache.getInstance()
75
76                 # Initialize
77                 self.timers = []
78                 self.configMtime = -1
79                 self.uniqueTimerId = 0
80                 self.defaultTimer = AutoTimerComponent(
81                         0,              # Id
82                         "",             # Name
83                         "",             # Match
84                         True    # Enabled
85                 )
86
87         def readXml(self):
88                 # Abort if no config found
89                 if not os_path.exists(XML_CONFIG):
90                         return
91
92                 # Parse if mtime differs from whats saved
93                 mtime = os_path.getmtime(XML_CONFIG)
94                 if mtime == self.configMtime:
95                         print "[AutoTimer] No changes in configuration, won't parse"
96                         return
97
98                 # Save current mtime
99                 self.configMtime = mtime
100
101                 # Parse Config
102                 dom = minidom_parse(XML_CONFIG)
103                 
104                 # Empty out timers and reset Ids
105                 del self.timers[:]
106                 self.uniqueTimerId = 0
107                 self.defaultTimer = AutoTimerComponent(
108                         0,              # Id
109                         "",             # Name
110                         "",             # Match
111                         True    # Enabled
112                 )
113
114                 # Get Config Element
115                 for configuration in dom.getElementsByTagName("autotimer"):
116                         # Parse old configuration files
117                         if configuration.getAttribute("version") != CURRENT_CONFIG_VERSION:
118                                 from OldConfigurationParser import parseConfig
119                                 parseConfig(configuration, self.timers, configuration.getAttribute("version"), self.uniqueTimerId)
120                                 if not self.uniqueTimerId:
121                                         self.uniqueTimerId = len(self.timers)
122                                 continue
123                         # Read in defaults for a new timer
124                         for defaults in configuration.getElementsByTagName("defaults"):
125                                 # Read out timespan
126                                 start = defaults.getAttribute("from")
127                                 end = defaults.getAttribute("to")
128                                 if start and end:
129                                         start = [int(x) for x in start.split(':')]
130                                         end = [int(x) for x in end.split(':')]
131                                         self.defaultTimer.timespan = (start, end)
132
133                                 # Read out max length
134                                 maxduration = defaults.getAttribute("maxduration") or None
135                                 if maxduration:
136                                         self.defaultTimer.maxduration = int(maxlen)*60
137
138                                 # Read out recording path
139                                 self.defaultTimer.destination = defaults.getAttribute("location").encode("UTF-8") or None
140
141                                 # Read out offset
142                                 offset = defaults.getAttribute("offset") or None
143                                 if offset:
144                                         offset = offset.split(",")
145                                         if len(offset) == 1:
146                                                 before = after = int(offset[0] or 0) * 60
147                                         else:
148                                                 before = int(offset[0] or 0) * 60
149                                                 after = int(offset[1] or 0) * 60
150                                         self.defaultTimer.offset = (before, after)
151
152                                 # Read out counter
153                                 self.defaultTimer.matchCount = int(defaults.getAttribute("counter") or '0')
154                                 self.defaultTimer.matchFormatString = defaults.getAttribute("counterFormat")
155
156                                 # Read out justplay
157                                 justplay = int(defaults.getAttribute("justplay") or '0')
158
159                                 # Read out avoidDuplicateDescription
160                                 self.defaultTimer.avoidDuplicateDescription = bool(defaults.getAttribute("avoidDuplicateDescription") or False)
161
162                                 # Read out allowed services
163                                 servicelist = self.defaultTimer.services        
164                                 for service in defaults.getElementsByTagName("serviceref"):
165                                         value = getValue(service, None)
166                                         if value:
167                                                 # strip all after last :
168                                                 pos = value.rfind(':')
169                                                 if pos != -1:
170                                                         value = value[:pos+1]
171
172                                                 servicelist.append(value)
173                                 self.defaultTimer.services = servicelist # We might have got a dummy list above
174
175                                 # Read out allowed bouquets
176                                 bouquets = self.defaultTimer.bouquets
177                                 for bouquet in defaults.getElementsByTagName("bouquet"):
178                                         value = getValue(bouquet, None)
179                                         if value:
180                                                 bouquets.append(value)
181                                 self.defaultTimer.bouquets = bouquets
182
183                                 # Read out afterevent
184                                 idx = {"none": AFTEREVENT.NONE, "standby": AFTEREVENT.STANDBY, "shutdown": AFTEREVENT.DEEPSTANDBY, "deepstandby": AFTEREVENT.DEEPSTANDBY}
185                                 afterevent = self.defaultTimer.afterevent
186                                 for element in defaults.getElementsByTagName("afterevent"):
187                                         value = getValue(element, None)
188
189                                         try:
190                                                 value = idx[value]
191                                                 start = element.getAttribute("from")
192                                                 end = element.getAttribute("to")
193                                                 if start and end:
194                                                         start = [int(x) for x in start.split(':')]
195                                                         end = [int(x) for x in end.split(':')]
196                                                         afterevent.append((value, (start, end)))
197                                                 else:
198                                                         afterevent.append((value, None))
199                                         except KeyError, ke:
200                                                 print '[AutoTimer] Erroneous config contains invalid value for "afterevent":', afterevent,', ignoring definition'
201                                                 continue
202                                 self.defaultTimer.afterevent = afterevent
203
204                                 # Read out exclude
205                                 idx = {"title": 0, "shortdescription": 1, "description": 2, "dayofweek": 3}
206                                 excludes = (self.defaultTimer.getExcludedTitle(), self.defaultTimer.getExcludedShort(), self.defaultTimer.getExcludedDescription(), self.defaultTimer.getExcludedDays()) 
207                                 for exclude in defaults.getElementsByTagName("exclude"):
208                                         where = exclude.getAttribute("where")
209                                         value = getValue(exclude, None)
210                                         if not (value and where):
211                                                 continue
212
213                                         try:
214                                                 excludes[idx[where]].append(value.encode("UTF-8"))
215                                         except KeyError, ke:
216                                                 pass
217                                 self.defaultTimer.excludes = excludes
218
219                                 # Read out includes (use same idx)
220                                 includes = (self.defaultTimer.getIncludedTitle(), self.defaultTimer.getIncludedShort(), self.defaultTimer.getIncludedDescription(), self.defaultTimer.getIncludedDays())
221                                 for include in defaults.getElementsByTagName("include"):
222                                         where = include.getAttribute("where")
223                                         value = getValue(include, None)
224                                         if not (value and where):
225                                                 continue
226
227                                         try:
228                                                 includes[idx[where]].append(value.encode("UTF-8"))
229                                         except KeyError, ke:
230                                                 pass
231                                 self.defaultTimer.includes = includes
232
233                                 # Read out recording tags (needs my enhanced tag support patch)
234                                 tags = self.defaultTimer.tags
235                                 for tag in defaults.getElementsByTagName("tag"):
236                                         value = getValue(tag, None)
237                                         if not value:
238                                                 continue
239
240                                         tags.append(value.encode("UTF-8"))
241
242                         # Iterate Timers
243                         for timer in configuration.getElementsByTagName("timer"):
244                                 # Increment uniqueTimerId
245                                 self.uniqueTimerId += 1
246
247                                 # Read out match
248                                 match = timer.getAttribute("match").encode("UTF-8")
249                                 if not match:
250                                         print '[AutoTimer] Erroneous config is missing attribute "match", skipping entry'
251                                         continue
252
253                                 # Read out name
254                                 name = timer.getAttribute("name").encode("UTF-8")
255                                 if not name:
256                                         print '[AutoTimer] Timer is missing attribute "name", defaulting to match'
257                                         name = match
258
259                                 # Read out enabled
260                                 enabled = timer.getAttribute("enabled") or "yes"
261                                 if enabled == "no":
262                                         enabled = False
263                                 elif enabled == "yes":
264                                         enabled = True
265                                 else:
266                                         print '[AutoTimer] Erroneous config contains invalid value for "enabled":', enabled,', disabling'
267                                         enabled = False
268
269                                 # Read out timespan
270                                 start = timer.getAttribute("from")
271                                 end = timer.getAttribute("to")
272                                 if start and end:
273                                         start = [int(x) for x in start.split(':')]
274                                         end = [int(x) for x in end.split(':')]
275                                         timetuple = (start, end)
276                                 else:
277                                         timetuple = None
278
279                                 # Read out max length
280                                 maxlen = timer.getAttribute("maxduration") or None
281                                 if maxlen:
282                                         maxlen = int(maxlen)*60
283
284                                 # Read out recording path
285                                 destination = timer.getAttribute("location").encode("UTF-8") or None
286
287                                 # Read out offset
288                                 offset = timer.getAttribute("offset") or None
289                                 if offset:
290                                         offset = offset.split(",")
291                                         if len(offset) == 1:
292                                                 before = after = int(offset[0] or 0) * 60
293                                         else:
294                                                 before = int(offset[0] or 0) * 60
295                                                 after = int(offset[1] or 0) * 60
296                                         offset = (before, after)
297
298                                 # Read out counter
299                                 counter = int(timer.getAttribute("counter") or '0')
300                                 counterLeft = int(timer.getAttribute("left") or counter)
301                                 counterLimit = timer.getAttribute("lastActivation")
302                                 counterFormat = timer.getAttribute("counterFormat")
303                                 lastBegin = int(timer.getAttribute("lastBegin") or 0)
304
305                                 # Read out justplay
306                                 justplay = int(timer.getAttribute("justplay") or '0')
307
308                                 # Read out avoidDuplicateDescription
309                                 avoidDuplicateDescription = bool(timer.getAttribute("avoidDuplicateDescription") or False)
310
311                                 # Read out allowed services
312                                 servicelist = []                                        
313                                 for service in timer.getElementsByTagName("serviceref"):
314                                         value = getValue(service, None)
315                                         if value:
316                                                 # strip all after last :
317                                                 pos = value.rfind(':')
318                                                 if pos != -1:
319                                                         value = value[:pos+1]
320
321                                                 servicelist.append(value)
322
323                                 # Read out allowed bouquets
324                                 bouquets = []
325                                 for bouquet in timer.getElementsByTagName("bouquet"):
326                                         value = getValue(bouquet, None)
327                                         if value:
328                                                 bouquets.append(value)
329
330                                 # Read out afterevent
331                                 idx = {"none": AFTEREVENT.NONE, "standby": AFTEREVENT.STANDBY, "shutdown": AFTEREVENT.DEEPSTANDBY, "deepstandby": AFTEREVENT.DEEPSTANDBY}
332                                 afterevent = []
333                                 for element in timer.getElementsByTagName("afterevent"):
334                                         value = getValue(element, None)
335
336                                         try:
337                                                 value = idx[value]
338                                                 start = element.getAttribute("from")
339                                                 end = element.getAttribute("to")
340                                                 if start and end:
341                                                         start = [int(x) for x in start.split(':')]
342                                                         end = [int(x) for x in end.split(':')]
343                                                         afterevent.append((value, (start, end)))
344                                                 else:
345                                                         afterevent.append((value, None))
346                                         except KeyError, ke:
347                                                 print '[AutoTimer] Erroneous config contains invalid value for "afterevent":', afterevent,', ignoring definition'
348                                                 continue
349
350                                 # Read out exclude
351                                 idx = {"title": 0, "shortdescription": 1, "description": 2, "dayofweek": 3}
352                                 excludes = ([], [], [], []) 
353                                 for exclude in timer.getElementsByTagName("exclude"):
354                                         where = exclude.getAttribute("where")
355                                         value = getValue(exclude, None)
356                                         if not (value and where):
357                                                 continue
358
359                                         try:
360                                                 excludes[idx[where]].append(value.encode("UTF-8"))
361                                         except KeyError, ke:
362                                                 pass
363
364                                 # Read out includes (use same idx)
365                                 includes = ([], [], [], []) 
366                                 for include in timer.getElementsByTagName("include"):
367                                         where = include.getAttribute("where")
368                                         value = getValue(include, None)
369                                         if not (value and where):
370                                                 continue
371
372                                         try:
373                                                 includes[idx[where]].append(value.encode("UTF-8"))
374                                         except KeyError, ke:
375                                                 pass
376
377                                 # Read out recording tags (needs my enhanced tag support patch)
378                                 tags = []
379                                 for tag in timer.getElementsByTagName("tag"):
380                                         value = getValue(tag, None)
381                                         if not value:
382                                                 continue
383
384                                         tags.append(value.encode("UTF-8"))
385
386                                 # Finally append tuple
387                                 self.timers.append(AutoTimerComponent(
388                                                 self.uniqueTimerId,
389                                                 name,
390                                                 match,
391                                                 enabled,
392                                                 timespan = timetuple,
393                                                 services = servicelist,
394                                                 offset = offset,
395                                                 afterevent = afterevent,
396                                                 exclude = excludes,
397                                                 include = includes,
398                                                 maxduration = maxlen,
399                                                 destination = destination,
400                                                 matchCount = counter,
401                                                 matchLeft = counterLeft,
402                                                 matchLimit = counterLimit,
403                                                 matchFormatString = counterFormat,
404                                                 lastBegin = lastBegin,
405                                                 justplay = justplay,
406                                                 avoidDuplicateDescription = avoidDuplicateDescription,
407                                                 bouquets = bouquets,
408                                                 tags = tags
409                                 ))
410
411         def getTimerList(self):
412                 return self.timers
413
414         def getEnabledTimerList(self):
415                 return [x for x in self.timers if x.enabled]
416
417         def getTupleTimerList(self):
418                 return [(x,) for x in self.timers]
419
420         def getUniqueId(self):
421                 self.uniqueTimerId += 1
422                 return self.uniqueTimerId
423
424         def add(self, timer):
425                 self.timers.append(timer)
426
427         def set(self, timer):
428                 idx = 0
429                 for stimer in self.timers:
430                         if stimer == timer:
431                                 self.timers[idx] = timer
432                                 return
433                         idx += 1
434                 self.timers.append(timer)
435
436         def remove(self, uniqueId):
437                 idx = 0
438                 for timer in self.timers:
439                         if timer.id == uniqueId:
440                                 self.timers.pop(idx)
441                                 return
442                         idx += 1
443
444         def writeXml(self):
445                 # Generate List in RAM
446                 list = ['<?xml version="1.0" ?>\n<autotimer version="', CURRENT_CONFIG_VERSION, '">\n\n']
447
448                 # XXX: we might want to make sure that we don't save empty default here
449                 list.extend([' <defaults'])
450
451                 # Timespan
452                 if self.defaultTimer.hasTimespan():
453                         list.extend([' from="', self.defaultTimer.getTimespanBegin(), '" to="', self.defaultTimer.getTimespanEnd(), '"'])
454
455                 # Duration
456                 if self.defaultTimer.hasDuration():
457                         list.extend([' maxduration="', str(self.defaultTimer.getDuration()), '"'])
458
459                 # Destination
460                 if self.defaultTimer.hasDestination():
461                         list.extend([' location="', stringToXML(self.defaultTimer.destination), '"'])
462
463                 # Offset
464                 if self.defaultTimer.hasOffset():
465                         if self.defaultTimer.isOffsetEqual():
466                                 list.extend([' offset="', str(self.defaultTimer.getOffsetBegin()), '"'])
467                         else:
468                                 list.extend([' offset="', str(self.defaultTimer.getOffsetBegin()), ',', str(self.defaultTimer.getOffsetEnd()), '"'])
469
470                 # Counter
471                 if self.defaultTimer.hasCounter():
472                         list.extend([' counter="', str(self.defaultTimer.getCounter()), '"'])
473                         if self.defaultTimer.hasCounterFormatString():
474                                 list.extend([' counterFormat="', str(self.defaultTimer.getCounterFormatString()), '"'])
475
476                 # Duplicate Description
477                 if self.defaultTimer.getAvoidDuplicateDescription():
478                         list.append(' avoidDuplicateDescription="1" ')
479
480                 # Only display justplay if true
481                 if self.defaultTimer.justplay:
482                         list.extend([' justplay="', str(self.defaultTimer.getJustplay()), '"'])
483
484                 # Close still opened defaults tag
485                 list.append('>\n')
486
487                 # Services
488                 for serviceref in self.defaultTimer.getServices():
489                         list.extend(['  <serviceref>', serviceref, '</serviceref>'])
490                         ref = ServiceReference(str(serviceref))
491                         list.extend([' <!-- ', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), ' -->\n'])
492
493                 # Bouquets
494                 for bouquet in self.defaultTimer.getBouquets():
495                         list.extend(['  <bouquet>', str(bouquet), '</bouquet>'])
496                         ref = ServiceReference(str(bouquet))
497                         list.extend([' <!-- ', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), ' -->\n'])
498
499                 # AfterEvent
500                 if self.defaultTimer.hasAfterEvent():
501                         idx = {AFTEREVENT.NONE: "none", AFTEREVENT.STANDBY: "standby", AFTEREVENT.DEEPSTANDBY: "shutdown"}
502                         for afterevent in self.defaultTimer.getCompleteAfterEvent():
503                                 action, timespan = afterevent
504                                 list.append('  <afterevent')
505                                 if timespan[0] is not None:
506                                         list.append(' from="%02d:%02d" to="%02d:%02d"' % (timespan[0][0], timespan[0][1], timespan[1][0], timespan[1][1]))
507                                 list.extend(['>', idx[action], '</afterevent>\n'])
508
509                 # Excludes
510                 for title in self.defaultTimer.getExcludedTitle():
511                         list.extend(['  <exclude where="title">', stringToXML(title), '</exclude>\n'])
512                 for short in self.defaultTimer.getExcludedShort():
513                         list.extend(['  <exclude where="shortdescription">', stringToXML(short), '</exclude>\n'])
514                 for desc in self.defaultTimer.getExcludedDescription():
515                         list.extend(['  <exclude where="description">', stringToXML(desc), '</exclude>\n'])
516                 for day in self.defaultTimer.getExcludedDays():
517                         list.extend(['  <exclude where="dayofweek">', stringToXML(day), '</exclude>\n'])
518
519                 # Includes
520                 for title in self.defaultTimer.getIncludedTitle():
521                         list.extend(['  <include where="title">', stringToXML(title), '</include>\n'])
522                 for short in self.defaultTimer.getIncludedShort():
523                         list.extend(['  <include where="shortdescription">', stringToXML(short), '</include>\n'])
524                 for desc in self.defaultTimer.getIncludedDescription():
525                         list.extend(['  <include where="description">', stringToXML(desc), '</include>\n'])
526                 for day in self.defaultTimer.getIncludedDays():
527                         list.extend(['  <include where="dayofweek">', stringToXML(day), '</include>\n'])
528
529                 # Tags
530                 for tag in self.defaultTimer.tags:
531                         list.extend(['  <tag>', stringToXML(tag), '</tag>\n'])
532
533                 # End of Timer
534                 list.append(' </defaults>\n\n')
535
536                 # Iterate timers
537                 for timer in self.timers:
538                         # Common attributes (match, enabled)
539                         list.extend([' <timer name="', stringToXML(timer.name), '" match="', stringToXML(timer.match), '" enabled="', timer.getEnabled(), '"'])
540
541                         # Timespan
542                         if timer.hasTimespan():
543                                 list.extend([' from="', timer.getTimespanBegin(), '" to="', timer.getTimespanEnd(), '"'])
544
545                         # Duration
546                         if timer.hasDuration():
547                                 list.extend([' maxduration="', str(timer.getDuration()), '"'])
548
549                         # Destination
550                         if timer.hasDestination():
551                                 list.extend([' location="', stringToXML(timer.destination), '"'])
552
553                         # Offset
554                         if timer.hasOffset():
555                                 if timer.isOffsetEqual():
556                                         list.extend([' offset="', str(timer.getOffsetBegin()), '"'])
557                                 else:
558                                         list.extend([' offset="', str(timer.getOffsetBegin()), ',', str(timer.getOffsetEnd()), '"'])
559
560                         # Counter
561                         if timer.hasCounter():
562                                 list.extend([' lastBegin="', str(timer.getLastBegin()), '" counter="', str(timer.getCounter()), '" left="', str(timer.getCounterLeft()) ,'"'])
563                                 if timer.hasCounterFormatString():
564                                         list.extend([' lastActivation="', str(timer.getCounterLimit()), '"'])
565                                         list.extend([' counterFormat="', str(timer.getCounterFormatString()), '"'])
566
567                         # Duplicate Description
568                         if timer.getAvoidDuplicateDescription():
569                                 list.append(' avoidDuplicateDescription="1" ')
570
571                         # Only display justplay if true
572                         if timer.justplay:
573                                 list.extend([' justplay="', str(timer.getJustplay()), '"'])
574
575                         # Close still opened timer tag
576                         list.append('>\n')
577
578                         # Services
579                         for serviceref in timer.getServices():
580                                 list.extend(['  <serviceref>', serviceref, '</serviceref>'])
581                                 ref = ServiceReference(str(serviceref))
582                                 list.extend([' <!-- ', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), ' -->\n'])
583
584                         # Bouquets
585                         for bouquet in timer.getBouquets():
586                                 list.extend(['  <bouquet>', str(bouquet), '</bouquet>'])
587                                 ref = ServiceReference(str(bouquet))
588                                 list.extend([' <!-- ', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), ' -->\n'])
589
590                         # AfterEvent
591                         if timer.hasAfterEvent():
592                                 idx = {AFTEREVENT.NONE: "none", AFTEREVENT.STANDBY: "standby", AFTEREVENT.DEEPSTANDBY: "shutdown"}
593                                 for afterevent in timer.getCompleteAfterEvent():
594                                         action, timespan = afterevent
595                                         list.append('  <afterevent')
596                                         if timespan[0] is not None:
597                                                 list.append(' from="%02d:%02d" to="%02d:%02d"' % (timespan[0][0], timespan[0][1], timespan[1][0], timespan[1][1]))
598                                         list.extend(['>', idx[action], '</afterevent>\n'])
599
600                         # Excludes
601                         for title in timer.getExcludedTitle():
602                                 list.extend(['  <exclude where="title">', stringToXML(title), '</exclude>\n'])
603                         for short in timer.getExcludedShort():
604                                 list.extend(['  <exclude where="shortdescription">', stringToXML(short), '</exclude>\n'])
605                         for desc in timer.getExcludedDescription():
606                                 list.extend(['  <exclude where="description">', stringToXML(desc), '</exclude>\n'])
607                         for day in timer.getExcludedDays():
608                                 list.extend(['  <exclude where="dayofweek">', stringToXML(day), '</exclude>\n'])
609
610                         # Includes
611                         for title in timer.getIncludedTitle():
612                                 list.extend(['  <include where="title">', stringToXML(title), '</include>\n'])
613                         for short in timer.getIncludedShort():
614                                 list.extend(['  <include where="shortdescription">', stringToXML(short), '</include>\n'])
615                         for desc in timer.getIncludedDescription():
616                                 list.extend(['  <include where="description">', stringToXML(desc), '</include>\n'])
617                         for day in timer.getIncludedDays():
618                                 list.extend(['  <include where="dayofweek">', stringToXML(day), '</include>\n'])
619
620                         # Tags
621                         for tag in timer.tags:
622                                 list.extend(['  <tag>', stringToXML(tag), '</tag>\n'])
623
624                         # End of Timer
625                         list.append(' </timer>\n\n')
626
627                 # End of Configuration
628                 list.append('</autotimer>\n')
629
630                 # Save to Flash
631                 file = open(XML_CONFIG, 'w')
632                 file.writelines(list)
633
634                 file.close()
635
636         def parseEPG(self, simulateOnly = False):
637                 if NavigationInstance.instance is None:
638                         print "[AutoTimer] Navigation is not available, can't parse EPG"
639                         return (0, 0, 0, [])
640
641                 total = 0
642                 new = 0
643                 modified = 0
644                 timers = []
645
646                 self.readXml()
647
648                 # Save Recordings in a dict to speed things up a little
649                 # We include processed timers as we might search for duplicate descriptions
650                 recorddict = {}
651                 for timer in NavigationInstance.instance.RecordTimer.timer_list + NavigationInstance.instance.RecordTimer.processed_timers:
652                         if not recorddict.has_key(str(timer.service_ref)):
653                                 recorddict[str(timer.service_ref)] = [timer]
654                         else:
655                                 recorddict[str(timer.service_ref)].append(timer)
656
657                 # Iterate Timer
658                 for timer in self.getEnabledTimerList():
659                         # Search EPG, default to empty list
660                         ret = self.epgcache.search(('RI', 100, eEPGCache.PARTIAL_TITLE_SEARCH, timer.match, eEPGCache.NO_CASE_CHECK)) or []
661
662                         for serviceref, eit in ret:
663                                 eserviceref = eServiceReference(serviceref)
664
665                                 evt = self.epgcache.lookupEventId(eserviceref, eit)
666                                 if not evt:
667                                         print "[AutoTimer] Could not create Event!"
668                                         continue
669
670                                 # Try to determine real service (we always choose the last one)
671                                 n = evt.getNumOfLinkageServices()
672                                 if n > 0:
673                                         i = evt.getLinkageService(eserviceref, n-1)
674                                         serviceref = i.toString()
675
676                                 # Gather Information
677                                 name = evt.getEventName()
678                                 description = evt.getShortDescription()
679                                 begin = evt.getBeginTime()
680                                 duration = evt.getDuration()
681                                 end = begin + duration
682
683                                 # If event starts in less than 60 seconds skip it
684                                 if begin < time() + 60:
685                                         continue
686
687                                 # Convert begin time
688                                 timestamp = localtime(begin)
689
690                                 # Update timer
691                                 timer.update(begin, timestamp)
692
693                                 # Check Duration, Timespan and Excludes
694                                 if timer.checkServices(serviceref) \
695                                         or timer.checkDuration(duration) \
696                                         or timer.checkTimespan(timestamp) \
697                                         or timer.checkFilter(name, description,
698                                                 evt.getExtendedDescription(), str(timestamp.tm_wday)):
699                                         continue
700
701                                 if timer.hasOffset():
702                                         # Apply custom Offset
703                                         begin, end = timer.applyOffset(begin, end)
704                                 else:
705                                         # Apply E2 Offset
706                                         begin -= config.recording.margin_before.value * 60
707                                         end += config.recording.margin_after.value * 60
708
709
710                                 total += 1
711
712                                 # Append to timerlist and abort if simulating
713                                 timers.append((name, begin, end, serviceref, timer.name))
714                                 if simulateOnly:
715                                         continue
716
717                                 # Initialize
718                                 newEntry = None
719
720                                 # Check for double Timers
721                                 # We first check eit and if user wants us to guess event based on time
722                                 # we try this as backup. The allowed diff should be configurable though.
723                                 try:
724                                         for rtimer in recorddict.get(serviceref, []):
725                                                 if rtimer.eit == eit or config.plugins.autotimer.try_guessing.value and getTimeDiff(rtimer, begin, end) > ((duration/10)*8):
726                                                         newEntry = rtimer
727
728                                                         # Abort if we don't want to modify timers or timer is repeated
729                                                         if config.plugins.autotimer.refresh.value == "none" or newEntry.repeated:
730                                                                 raise AutoTimerIgnoreTimerException("Won't modify existing timer because either no modification allowed or repeated timer")
731
732                                                         try:
733                                                                 if newEntry.isAutoTimer:
734                                                                         print "[AutoTimer] Modifying existing AutoTimer!"
735                                                         except AttributeError, ae:
736                                                                 if config.plugins.autotimer.refresh.value != "all":
737                                                                         raise AutoTimerIgnoreTimerException("Won't modify existing timer because it's no timer set by us")
738                                                                 print "[AutoTimer] Warning, we're messing with a timer which might not have been set by us"
739
740                                                         func = NavigationInstance.instance.RecordTimer.timeChanged
741                                                         modified += 1
742
743                                                         # Modify values saved in timer
744                                                         newEntry.name = name
745                                                         newEntry.description = description
746                                                         newEntry.begin = int(begin)
747                                                         newEntry.end = int(end)
748                                                         newEntry.service_ref = ServiceReference(serviceref)
749
750                                                         break
751                                                 elif timer.getAvoidDuplicateDescription() and rtimer.description == description:
752                                                         raise AutoTimerIgnoreTimerException("We found a timer with same description, skipping event")
753
754                                 except AutoTimerIgnoreTimerException, etite:
755                                         print etite
756                                         continue
757
758                                 # Event not yet in Timers
759                                 if newEntry is None:
760                                         if timer.checkCounter(timestamp):
761                                                 continue
762
763                                         new += 1
764
765                                         print "[AutoTimer] Adding an event."
766                                         newEntry = RecordTimerEntry(ServiceReference(serviceref), begin, end, name, description, eit)
767                                         func = NavigationInstance.instance.RecordTimer.record
768
769                                         # Mark this entry as AutoTimer (only AutoTimers will have this Attribute set)
770                                         newEntry.isAutoTimer = True
771
772                                 # Apply afterEvent
773                                 if timer.hasAfterEvent():
774                                         afterEvent = timer.getAfterEventTimespan(localtime(end))
775                                         if afterEvent is None:
776                                                 afterEvent = timer.getAfterEvent()
777                                         if afterEvent is not None:
778                                                 newEntry.afterEvent = afterEvent
779
780                                 newEntry.dirname = timer.destination
781                                 newEntry.justplay = timer.justplay
782                                 newEntry.tags = timer.tags # This needs my enhanced tag support patch to work
783  
784                                 # Do a sanity check, although it does not do much right now
785                                 timersanitycheck = TimerSanityCheck(NavigationInstance.instance.RecordTimer.timer_list, newEntry)
786                                 if not timersanitycheck.check():
787                                         print "[Autotimer] Sanity check failed"
788                                 else:
789                                         print "[Autotimer] Sanity check passed"
790
791                                 # Either add to List or change time
792                                 func(newEntry)
793
794                 return (total, new, modified, timers)