smaller optimizations
[vuplus_dvbapp-plugin] / autotimer / src / AutoTimerConfiguration.py
1 # -*- coding: UTF-8 -*-
2 # for localized messages
3 from . import _
4
5 from AutoTimerComponent import AutoTimerComponent
6 from RecordTimer import AFTEREVENT
7 from Tools.XMLTools import stringToXML
8 from ServiceReference import ServiceReference
9
10 CURRENT_CONFIG_VERSION = "5"
11
12 def getValue(definitions, default):
13         # Initialize Output
14         ret = ""
15
16         # How many definitions are present
17         if isinstance(definitions, list):
18                 Len = len(definitions)
19                 if Len > 0:
20                         childNodes = definitions[Len-1].text
21                 else:
22                         childNodes = ""
23         else:
24                 ret = definitions.text
25
26         # Return stripped output or (if empty) default
27         return ret.strip() or default
28
29 def parseConfig(configuration, list, version = None, uniqueTimerId = 0, defaultTimer = None):
30         if version != CURRENT_CONFIG_VERSION:
31                 parseConfigOld(configuration, list, uniqueTimerId)
32                 return
33
34         if defaultTimer is not None:
35                 # Read in defaults for a new timer
36                 for defaults in configuration.findall("defaults"):
37                         parseEntry(defaults, defaultTimer, True)
38
39         for timer in configuration.findall("timer"):
40                 uniqueTimerId += 1
41                 baseTimer = AutoTimerComponent(
42                         uniqueTimerId,
43                         '',
44                         '',
45                         True
46                 )
47
48                 if parseEntry(timer, baseTimer):
49                         list.append(baseTimer)
50
51 def parseEntry(element, baseTimer, defaults = False):
52         if not defaults:
53                 # Read out match
54                 baseTimer.match = element.get("match", "").encode("UTF-8")
55                 if not baseTimer.match:
56                         print '[AutoTimer] Erroneous config is missing attribute "match", skipping entry'
57                         return False
58
59                 # Read out name
60                 baseTimer.name = element.get("name", "").encode("UTF-8")
61                 if not baseTimer.name:
62                         print '[AutoTimer] Timer is missing attribute "name", defaulting to match'
63                         baseTimer.name = baseTimer.match
64
65                 # Read out enabled
66                 enabled = element.get("enabled", "yes")
67                 if enabled == "no":
68                         baseTimer.enabled = False
69                 elif enabled == "yes":
70                         baseTimer.enabled = True
71                 else:
72                         print '[AutoTimer] Erroneous config contains invalid value for "enabled":', enabled,', disabling'
73                         baseTimer.enabled = False
74
75         # Read out timespan
76         start = element.get("from")
77         end = element.get("to")
78         if start and end:
79                 start = [int(x) for x in start.split(':')]
80                 end = [int(x) for x in end.split(':')]
81                 baseTimer.timespan = (start, end)
82
83         # Read out max length
84         maxduration = element.get("maxduration")
85         if maxduration:
86                 baseTimer.maxduration = int(maxduration)*60
87
88         # Read out recording path
89         baseTimer.destination = element.get("location", "").encode("UTF-8") or None
90
91         # Read out offset
92         offset = element.get("offset")
93         if offset:
94                 offset = offset.split(",")
95                 if len(offset) == 1:
96                         before = after = int(offset[0] or 0) * 60
97                 else:
98                         before = int(offset[0] or 0) * 60
99                         after = int(offset[1] or 0) * 60
100                 baseTimer.offset = (before, after)
101
102         # Read out counter
103         baseTimer.matchCount = int(element.get("counter", 0))
104         baseTimer.matchFormatString = element.get("counterFormat", "")
105         if not defaults:
106                 baseTimer.matchLeft = int(element.get("left", baseTimer.matchCount))
107                 baseTimer.matchLimit = element.get("lastActivation", "")
108                 baseTimer.lastBegin = int(element.get("lastBegin", 0))
109
110         # Read out justplay
111         justplay = int(element.get("justplay", 0))
112
113         # Read out avoidDuplicateDescription
114         baseTimer.avoidDuplicateDescription = int(element.get("avoidDuplicateDescription", 0))
115
116         # Read out allowed services
117         servicelist = baseTimer.services        
118         for service in element.findall("serviceref"):
119                 value = service.text
120                 if value:
121                         # strip all after last :
122                         pos = value.rfind(':')
123                         if pos != -1:
124                                 value = value[:pos+1]
125
126                         servicelist.append(value)
127         baseTimer.services = servicelist
128
129         # Read out allowed bouquets
130         bouquets = baseTimer.bouquets
131         for bouquet in element.findall("bouquet"):
132                 value = bouquet.text
133                 if value:
134                         bouquets.append(value)
135         baseTimer.bouquets = bouquets
136
137         # Read out afterevent
138         idx = {
139                 "none": AFTEREVENT.NONE,
140                 "deepstandby": AFTEREVENT.DEEPSTANDBY,
141                 "shutdown": AFTEREVENT.DEEPSTANDBY,
142                 "standby": AFTEREVENT.STANDBY,
143                 "auto": AFTEREVENT.AUTO
144         }
145         afterevent = baseTimer.afterevent
146         for element in element.findall("afterevent"):
147                 value = element.text
148
149                 if idx.has_key(value):
150                         value = idx[value]
151                 else:
152                         print '[AutoTimer] Erroneous config contains invalid value for "afterevent":', afterevent,', ignoring definition'
153                         continue
154
155                 start = element.get("from")
156                 end = element.get("to")
157                 if start and end:
158                         start = [int(x) for x in start.split(':')]
159                         end = [int(x) for x in end.split(':')]
160                         afterevent.append((value, (start, end)))
161                 else:
162                         afterevent.append((value, None))
163         baseTimer.afterevent = afterevent
164
165         # Read out exclude
166         idx = {"title": 0, "shortdescription": 1, "description": 2, "dayofweek": 3}
167         excludes = (baseTimer.getExcludedTitle(), baseTimer.getExcludedShort(), baseTimer.getExcludedDescription(), baseTimer.getExcludedDays()) 
168         for exclude in element.findall("exclude"):
169                 where = exclude.get("where")
170                 value = exclude.text
171                 if not (value and where):
172                         continue
173
174                 if idx.has_key(where):
175                         excludes[idx[where]].append(value.encode("UTF-8"))
176         baseTimer.exclude = excludes
177
178         # Read out includes (use same idx)
179         includes = (baseTimer.getIncludedTitle(), baseTimer.getIncludedShort(), baseTimer.getIncludedDescription(), baseTimer.getIncludedDays())
180         for include in element.findall("include"):
181                 where = include.get("where")
182                 value = include.text
183                 if not (value and where):
184                         continue
185
186                 if idx.has_key(where):
187                         includes[idx[where]].append(value.encode("UTF-8"))
188         baseTimer.include = includes
189
190         # Read out recording tags (needs my enhanced tag support patch)
191         tags = baseTimer.tags
192         for tag in element.findall("tag"):
193                 value = tag.text
194                 if not value:
195                         continue
196
197                 tags.append(value.encode("UTF-8"))
198         baseTimer.tags = tags
199         
200         return True
201
202 def parseConfigOld(configuration, list, uniqueTimerId = 0):
203         print "[AutoTimer] Trying to parse old config"
204
205         # Iterate Timers
206         for timer in configuration.findall("timer"):
207                 # Increment uniqueTimerId
208                 uniqueTimerId += 1
209
210                 # Get name (V2+)
211                 name = timer.get("name")
212                 if name:
213                         name = name.encode("UTF-8")
214                 # Get name (= match) (V1)
215                 else:
216                         # Read out name
217                         name = getValue(timer.findall("name"), "").encode("UTF-8")
218
219                 if not name:
220                         print '[AutoTimer] Erroneous config is missing attribute "name", skipping entry'
221                         continue
222
223                 # Read out match (V3+)
224                 match = timer.get("match")
225                 if match:
226                         # Read out match
227                         match = match.encode("UTF-8")
228                         if not match:
229                                 print '[AutoTimer] Erroneous config contains empty attribute "match", skipping entry'
230                                 continue
231                 # V2-
232                 else:
233                         # Setting match to name
234                         match = name
235
236
237                 # See if Timer is ensabled (V2+)
238                 enabled = timer.get("enabled")
239                 if enabled:
240                         if enabled == "no":
241                                 enabled = False
242                         elif enabled == "yes":
243                                 enabled = True
244                         else:
245                                 print '[AutoTimer] Erroneous config contains invalid value for "enabled":', enabled,', skipping entry'
246                                 enabled = False
247                 # V1
248                 else:
249                         elements = timer.findall("enabled")
250                         if len(elements):
251                                 if getValue(elements, "yes") == "no":
252                                         enabled = False
253                                 else:
254                                         enabled = True
255                         else:
256                                 enabled = True
257
258                 # Read out timespan (V4+; Falling back on missing definition should be OK)
259                 start = timer.get("from")
260                 end = timer.get("to")
261                 if start and end:
262                         start = [int(x) for x in start.split(':')]
263                         end = [int(x) for x in end.split(':')]
264                         timetuple = (start, end)
265                 # V3-
266                 else:
267                         elements = timer.findall("timespan")
268                         Len = len(elements)
269                         if Len:
270                                 # Read out last definition
271                                 start = elements[Len-1].get("from")
272                                 end = elements[Len-1].get("to")
273                                 if start and end:
274                                         start = [int(x) for x in start.split(':')]
275                                         end = [int(x) for x in end.split(':')]
276                                         timetuple = (start, end)
277                                 else:
278                                         print '[AutoTimer] Erroneous config contains invalid definition of "timespan", ignoring definition'
279                                         timetuple = None
280                         else:
281                                 timetuple = None
282
283                 # Read out allowed services (V*)
284                 elements = timer.findall("serviceref")
285                 if len(elements):
286                         servicelist = []
287                         for service in elements:
288                                 value = service.text
289                                 if value:
290                                         # strip all after last :
291                                         pos = value.rfind(':')
292                                         if pos != -1:
293                                                 value = value[:pos+1]
294
295                                         servicelist.append(value)
296                 else:
297                         servicelist = None
298
299                 # Read out allowed bouquets (V* though officially supported since V4)
300                 bouquets = []
301                 for bouquet in timer.findall("bouquet"):
302                         value = bouquet.text
303                         if value:
304                                 bouquets.append(value)
305
306                 # Read out offset (V4+)
307                 offset = timer.get("offset")
308                 if offset:
309                         offset = offset.split(",")
310                         if len(offset) == 1:
311                                 before = after = int(offset[0] or 0) * 60
312                         else:
313                                 before = int(offset[0] or 0) * 60
314                                 after = int(offset[1] or 0) * 60
315                         offset = (before, after)
316                 # V3-
317                 else:
318                         elements = timer.findall("offset")
319                         Len = len(elements)
320                         if Len:
321                                 value = elements[Len-1].get("both")
322                                 if value == '':
323                                         before = int(elements[Len-1].get("before", 0)) * 60
324                                         after = int(elements[Len-1].get("after", 0)) * 60
325                                 else:
326                                         before = after = int(value) * 60
327                                 offset = (before, after)
328                         else:
329                                 offset = None
330
331                 # Read out counter
332                 counter = int(timer.get("counter", '0'))
333                 counterLeft = int(timer.get("left", counter))
334                 counterLimit = timer.get("lastActivation")
335                 counterFormat = timer.get("counterFormat", "")
336                 lastBegin = int(timer.get("lastBegin", 0))
337
338                 # Read out justplay
339                 justplay = int(timer.get("justplay", '0'))
340
341                 # Read out avoidDuplicateDescription
342                 avoidDuplicateDescription = int(timer.get("avoidDuplicateDescription", 0))
343
344                 # Read out afterevent (compatible to V* though behaviour for V3- is different as V4+ allows multiple afterevents while the last definication was chosen before)
345                 idx = {
346                         "none": AFTEREVENT.NONE,
347                         "deepstandby": AFTEREVENT.DEEPSTANDBY,
348                         "shutdown": AFTEREVENT.DEEPSTANDBY,
349                         "standby": AFTEREVENT.STANDBY,
350                         "auto": AFTEREVENT.AUTO
351                 }
352                 afterevent = []
353                 for element in timer.findall("afterevent"):
354                         value = element.text
355
356                         if idx.has_key(value):
357                                 value = idx[value]
358                         else:
359                                 print '[AutoTimer] Erroneous config contains invalid value for "afterevent":', afterevent,', ignoring definition'
360                                 continue
361
362                         start = element.get("from")
363                         end = element.get("to")
364                         if start and end:
365                                 start = [int(x) for x in start.split(':')]
366                                 end = [int(x) for x in end.split(':')]
367                                 afterevent.append((value, (start, end)))
368                         else:
369                                 afterevent.append((value, None))
370
371                 # Read out exclude (V*)
372                 idx = {"title": 0, "shortdescription": 1, "description": 2, "dayofweek": 3}
373                 excludes = ([], [], [], []) 
374                 for exclude in timer.findall("exclude"):
375                         where = exclude.get("where")
376                         value = exclude.text
377                         if not (value and where):
378                                 continue
379
380                         if idx.has_key(where):
381                                 excludes[idx[where]].append(value.encode("UTF-8"))
382
383                 # Read out includes (use same idx) (V4+ feature, should not harm V3-)
384                 includes = ([], [], [], []) 
385                 for include in timer.findall("include"):
386                         where = include.get("where")
387                         value = include.text
388                         if not (value and where):
389                                 continue
390
391                         if idx.has_key(where):
392                                 includes[idx[where]].append(value.encode("UTF-8"))
393
394                 # Read out max length (V4+)
395                 maxlen = timer.get("maxduration")
396                 if maxlen:
397                         maxlen = int(maxlen)*60
398                 # V3-
399                 else:
400                         elements = timer.findall("maxduration")
401                         if len(elements):
402                                 maxlen = getValue(elements, None)
403                                 if maxlen is not None:
404                                         maxlen = int(maxlen)*60
405                         else:
406                                 maxlen = None
407
408                 # Read out recording path
409                 destination = timer.get("destination", "").encode("UTF-8") or None
410
411                 # Read out recording tags (needs my enhanced tag support patch)
412                 tags = []
413                 for tag in timer.findall("tag"):
414                         value = tag.text
415                         if not value:
416                                 continue
417
418                         tags.append(value.encode("UTF-8"))
419
420                 # Finally append timer
421                 list.append(AutoTimerComponent(
422                                 uniqueTimerId,
423                                 name,
424                                 match,
425                                 enabled,
426                                 timespan = timetuple,
427                                 services = servicelist,
428                                 offset = offset,
429                                 afterevent = afterevent,
430                                 exclude = excludes,
431                                 include = includes,
432                                 maxduration = maxlen,
433                                 destination = destination,
434                                 matchCount = counter,
435                                 matchLeft = counterLeft,
436                                 matchLimit = counterLimit,
437                                 matchFormatString = counterFormat,
438                                 lastBegin = lastBegin,
439                                 justplay = justplay,
440                                 avoidDuplicateDescription = avoidDuplicateDescription,
441                                 bouquets = bouquets,
442                                 tags = tags
443                 ))
444
445 def writeConfig(filename, defaultTimer, timers):
446         # Generate List in RAM
447         list = ['<?xml version="1.0" ?>\n<autotimer version="', CURRENT_CONFIG_VERSION, '">\n\n']
448
449         # XXX: we might want to make sure that we don't save empty default here
450         list.extend([' <defaults'])
451
452         # Timespan
453         if defaultTimer.hasTimespan():
454                 list.extend([' from="', defaultTimer.getTimespanBegin(), '" to="', defaultTimer.getTimespanEnd(), '"'])
455
456         # Duration
457         if defaultTimer.hasDuration():
458                 list.extend([' maxduration="', str(defaultTimer.getDuration()), '"'])
459
460         # Destination
461         if defaultTimer.hasDestination():
462                 list.extend([' location="', stringToXML(defaultTimer.destination), '"'])
463
464         # Offset
465         if defaultTimer.hasOffset():
466                 if defaultTimer.isOffsetEqual():
467                         list.extend([' offset="', str(defaultTimer.getOffsetBegin()), '"'])
468                 else:
469                         list.extend([' offset="', str(defaultTimer.getOffsetBegin()), ',', str(defaultTimer.getOffsetEnd()), '"'])
470
471         # Counter
472         if defaultTimer.hasCounter():
473                 list.extend([' counter="', str(defaultTimer.getCounter()), '"'])
474                 if defaultTimer.hasCounterFormatString():
475                         list.extend([' counterFormat="', str(defaultTimer.getCounterFormatString()), '"'])
476
477         # Duplicate Description
478         if defaultTimer.getAvoidDuplicateDescription():
479                 list.append(' avoidDuplicateDescription="1" ')
480
481         # Only display justplay if true
482         if defaultTimer.justplay:
483                 list.extend([' justplay="', str(defaultTimer.getJustplay()), '"'])
484
485         # Close still opened defaults tag
486         list.append('>\n')
487
488         # Services
489         for serviceref in defaultTimer.services:
490                 list.extend(['  <serviceref>', serviceref, '</serviceref>'])
491                 ref = ServiceReference(str(serviceref))
492                 list.extend([' <!-- ', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), ' -->\n'])
493
494         # Bouquets
495         for bouquet in defaultTimer.bouquets:
496                 list.extend(['  <bouquet>', str(bouquet), '</bouquet>'])
497                 ref = ServiceReference(str(bouquet))
498                 list.extend([' <!-- ', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), ' -->\n'])
499
500         # AfterEvent
501         if defaultTimer.hasAfterEvent():
502                 idx = {
503                         AFTEREVENT.NONE: "none",
504                         AFTEREVENT.STANDBY: "standby",
505                         AFTEREVENT.DEEPSTANDBY: "shutdown",
506                         AFTEREVENT.AUTO: "auto"
507                 }
508                 for afterevent in defaultTimer.afterevent:
509                         action, timespan = afterevent
510                         list.append('  <afterevent')
511                         if timespan[0] is not None:
512                                 list.append(' from="%02d:%02d" to="%02d:%02d"' % (timespan[0][0], timespan[0][1], timespan[1][0], timespan[1][1]))
513                         list.extend(['>', idx[action], '</afterevent>\n'])
514
515         # Excludes
516         for title in defaultTimer.getExcludedTitle():
517                 list.extend(['  <exclude where="title">', stringToXML(title), '</exclude>\n'])
518         for short in defaultTimer.getExcludedShort():
519                 list.extend(['  <exclude where="shortdescription">', stringToXML(short), '</exclude>\n'])
520         for desc in defaultTimer.getExcludedDescription():
521                 list.extend(['  <exclude where="description">', stringToXML(desc), '</exclude>\n'])
522         for day in defaultTimer.getExcludedDays():
523                 list.extend(['  <exclude where="dayofweek">', stringToXML(day), '</exclude>\n'])
524
525         # Includes
526         for title in defaultTimer.getIncludedTitle():
527                 list.extend(['  <include where="title">', stringToXML(title), '</include>\n'])
528         for short in defaultTimer.getIncludedShort():
529                 list.extend(['  <include where="shortdescription">', stringToXML(short), '</include>\n'])
530         for desc in defaultTimer.getIncludedDescription():
531                 list.extend(['  <include where="description">', stringToXML(desc), '</include>\n'])
532         for day in defaultTimer.getIncludedDays():
533                 list.extend(['  <include where="dayofweek">', stringToXML(day), '</include>\n'])
534
535         # Tags
536         for tag in defaultTimer.tags:
537                 list.extend(['  <tag>', stringToXML(tag), '</tag>\n'])
538
539         # End of Timer
540         list.append(' </defaults>\n\n')
541
542         # Iterate timers
543         for timer in timers:
544                 # Common attributes (match, enabled)
545                 list.extend([' <timer name="', stringToXML(timer.name), '" match="', stringToXML(timer.match), '" enabled="', timer.getEnabled(), '"'])
546
547                 # Timespan
548                 if timer.hasTimespan():
549                         list.extend([' from="', timer.getTimespanBegin(), '" to="', timer.getTimespanEnd(), '"'])
550
551                 # Duration
552                 if timer.hasDuration():
553                         list.extend([' maxduration="', str(timer.getDuration()), '"'])
554
555                 # Destination
556                 if timer.hasDestination():
557                         list.extend([' location="', stringToXML(timer.destination), '"'])
558
559                 # Offset
560                 if timer.hasOffset():
561                         if timer.isOffsetEqual():
562                                 list.extend([' offset="', str(timer.getOffsetBegin()), '"'])
563                         else:
564                                 list.extend([' offset="', str(timer.getOffsetBegin()), ',', str(timer.getOffsetEnd()), '"'])
565
566                 # Counter
567                 if timer.hasCounter():
568                         list.extend([' lastBegin="', str(timer.getLastBegin()), '" counter="', str(timer.getCounter()), '" left="', str(timer.getCounterLeft()) ,'"'])
569                         if timer.hasCounterFormatString():
570                                 list.extend([' lastActivation="', str(timer.getCounterLimit()), '"'])
571                                 list.extend([' counterFormat="', str(timer.getCounterFormatString()), '"'])
572
573                 # Duplicate Description
574                 if timer.getAvoidDuplicateDescription():
575                         list.extend([' avoidDuplicateDescription="', str(timer.getAvoidDuplicateDescription()), '"'])
576
577                 # Only display justplay if true
578                 if timer.justplay:
579                         list.extend([' justplay="', str(timer.getJustplay()), '"'])
580
581                 # Close still opened timer tag
582                 list.append('>\n')
583
584                 # Services
585                 for serviceref in timer.services:
586                         list.extend(['  <serviceref>', serviceref, '</serviceref>'])
587                         ref = ServiceReference(str(serviceref))
588                         list.extend([' <!-- ', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), ' -->\n'])
589
590                 # Bouquets
591                 for bouquet in timer.bouquets:
592                         list.extend(['  <bouquet>', str(bouquet), '</bouquet>'])
593                         ref = ServiceReference(str(bouquet))
594                         list.extend([' <!-- ', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), ' -->\n'])
595
596                 # AfterEvent
597                 if timer.hasAfterEvent():
598                         idx = {
599                                 AFTEREVENT.NONE: "none",
600                                 AFTEREVENT.STANDBY: "standby",
601                                 AFTEREVENT.DEEPSTANDBY: "shutdown",
602                                 AFTEREVENT.AUTO: "auto"
603                         }
604                         for afterevent in timer.afterevent:
605                                 action, timespan = afterevent
606                                 list.append('  <afterevent')
607                                 if timespan[0] is not None:
608                                         list.append(' from="%02d:%02d" to="%02d:%02d"' % (timespan[0][0], timespan[0][1], timespan[1][0], timespan[1][1]))
609                                 list.extend(['>', idx[action], '</afterevent>\n'])
610
611                 # Excludes
612                 for title in timer.getExcludedTitle():
613                         list.extend(['  <exclude where="title">', stringToXML(title), '</exclude>\n'])
614                 for short in timer.getExcludedShort():
615                         list.extend(['  <exclude where="shortdescription">', stringToXML(short), '</exclude>\n'])
616                 for desc in timer.getExcludedDescription():
617                         list.extend(['  <exclude where="description">', stringToXML(desc), '</exclude>\n'])
618                 for day in timer.getExcludedDays():
619                         list.extend(['  <exclude where="dayofweek">', stringToXML(day), '</exclude>\n'])
620
621                 # Includes
622                 for title in timer.getIncludedTitle():
623                         list.extend(['  <include where="title">', stringToXML(title), '</include>\n'])
624                 for short in timer.getIncludedShort():
625                         list.extend(['  <include where="shortdescription">', stringToXML(short), '</include>\n'])
626                 for desc in timer.getIncludedDescription():
627                         list.extend(['  <include where="description">', stringToXML(desc), '</include>\n'])
628                 for day in timer.getIncludedDays():
629                         list.extend(['  <include where="dayofweek">', stringToXML(day), '</include>\n'])
630
631                 # Tags
632                 for tag in timer.tags:
633                         list.extend(['  <tag>', stringToXML(tag), '</tag>\n'])
634
635                 # End of Timer
636                 list.append(' </timer>\n\n')
637
638         # End of Configuration
639         list.append('</autotimer>\n')
640
641         # Save to Flash
642         file = open(filename, 'w')
643         file.writelines(list)
644
645         file.close()
646