1 from AutoTimerComponent import AutoTimerComponent
2 from RecordTimer import AFTEREVENT
3 from Tools.XMLTools import stringToXML
4 from ServiceReference import ServiceReference
6 CURRENT_CONFIG_VERSION = "5"
8 def getValue(definitions, default):
12 # How many definitions are present
14 childNodes = definitions.childNodes
16 Len = len(definitions)
18 childNodes = definitions[Len-1].childNodes
22 # Iterate through nodes of last one
23 for node in childNodes:
24 # Append text if we have a text node
25 if node.nodeType == node.TEXT_NODE:
28 # Return stripped output or (if empty) default
29 return ret.strip() or default
31 def parseConfig(configuration, list, version = None, uniqueTimerId = 0, defaultTimer = None):
32 if version != CURRENT_CONFIG_VERSION:
33 parseConfigOld(configuration, list, uniqueTimerId)
36 if defaultTimer is not None:
37 # Read in defaults for a new timer
38 for defaults in configuration.getElementsByTagName("defaults"):
39 parseEntry(defaults, defaultTimer, True)
41 for timer in configuration.getElementsByTagName("timer"):
43 baseTimer = AutoTimerComponent(
50 if parseEntry(timer, baseTimer):
51 list.append(baseTimer)
53 def parseEntry(element, baseTimer, defaults = False):
56 baseTimer.match = element.getAttribute("match").encode("UTF-8")
57 if not baseTimer.match:
58 print '[AutoTimer] Erroneous config is missing attribute "match", skipping entry'
62 baseTimer.name = element.getAttribute("name").encode("UTF-8")
63 if not baseTimer.name:
64 print '[AutoTimer] Timer is missing attribute "name", defaulting to match'
65 baseTimer.name = baseTimer.match
68 enabled = element.getAttribute("enabled") or "yes"
70 baseTimer.enabled = False
71 elif enabled == "yes":
72 baseTimer.enabled = True
74 print '[AutoTimer] Erroneous config contains invalid value for "enabled":', enabled,', disabling'
75 baseTimer.enabled = False
78 start = element.getAttribute("from")
79 end = element.getAttribute("to")
81 start = [int(x) for x in start.split(':')]
82 end = [int(x) for x in end.split(':')]
83 baseTimer.timespan = (start, end)
86 maxduration = element.getAttribute("maxduration") or None
88 baseTimer.maxduration = int(maxlen)*60
90 # Read out recording path
91 baseTimer.destination = element.getAttribute("location").encode("UTF-8") or None
94 offset = element.getAttribute("offset") or None
96 offset = offset.split(",")
98 before = after = int(offset[0] or 0) * 60
100 before = int(offset[0] or 0) * 60
101 after = int(offset[1] or 0) * 60
102 baseTimer.offset = (before, after)
105 baseTimer.matchCount = int(element.getAttribute("counter") or '0')
106 baseTimer.matchFormatString = element.getAttribute("counterFormat")
108 baseTimer.counterLimit = element.getAttribute("lastActivation")
109 baseTimer.counterFormat = element.getAttribute("counterFormat")
110 baseTimer.lastBegin = int(element.getAttribute("lastBegin") or 0)
113 justplay = int(element.getAttribute("justplay") or '0')
115 # Read out avoidDuplicateDescription
116 baseTimer.avoidDuplicateDescription = bool(element.getAttribute("avoidDuplicateDescription") or False)
118 # Read out allowed services
119 servicelist = baseTimer.services
120 for service in element.getElementsByTagName("serviceref"):
121 value = getValue(service, None)
123 # strip all after last :
124 pos = value.rfind(':')
126 value = value[:pos+1]
128 servicelist.append(value)
129 baseTimer.services = servicelist
131 # Read out allowed bouquets
132 bouquets = baseTimer.bouquets
133 for bouquet in element.getElementsByTagName("bouquet"):
134 value = getValue(bouquet, None)
136 bouquets.append(value)
137 baseTimer.bouquets = bouquets
139 # Read out afterevent
140 idx = {"none": AFTEREVENT.NONE, "standby": AFTEREVENT.STANDBY, "shutdown": AFTEREVENT.DEEPSTANDBY, "deepstandby": AFTEREVENT.DEEPSTANDBY}
141 afterevent = baseTimer.afterevent
142 for element in element.getElementsByTagName("afterevent"):
143 value = getValue(element, None)
147 start = element.getAttribute("from")
148 end = element.getAttribute("to")
150 start = [int(x) for x in start.split(':')]
151 end = [int(x) for x in end.split(':')]
152 afterevent.append((value, (start, end)))
154 afterevent.append((value, None))
156 print '[AutoTimer] Erroneous config contains invalid value for "afterevent":', afterevent,', ignoring definition'
158 baseTimer.afterevent = afterevent
161 idx = {"title": 0, "shortdescription": 1, "description": 2, "dayofweek": 3}
162 excludes = (baseTimer.getExcludedTitle(), baseTimer.getExcludedShort(), baseTimer.getExcludedDescription(), baseTimer.getExcludedDays())
163 for exclude in element.getElementsByTagName("exclude"):
164 where = exclude.getAttribute("where")
165 value = getValue(exclude, None)
166 if not (value and where):
170 excludes[idx[where]].append(value.encode("UTF-8"))
173 baseTimer.excludes = excludes
175 # Read out includes (use same idx)
176 includes = (baseTimer.getIncludedTitle(), baseTimer.getIncludedShort(), baseTimer.getIncludedDescription(), baseTimer.getIncludedDays())
177 for include in element.getElementsByTagName("include"):
178 where = include.getAttribute("where")
179 value = getValue(include, None)
180 if not (value and where):
184 includes[idx[where]].append(value.encode("UTF-8"))
187 baseTimer.includes = includes
189 # Read out recording tags (needs my enhanced tag support patch)
190 tags = baseTimer.tags
191 for tag in element.getElementsByTagName("tag"):
192 value = getValue(tag, None)
196 tags.append(value.encode("UTF-8"))
197 baseTimer.tags = tags
201 def parseConfigOld(configuration, list, uniqueTimerId = 0):
202 print "[AutoTimer] Trying to parse old config"
205 for timer in configuration.getElementsByTagName("timer"):
206 # Increment uniqueTimerId
210 if timer.hasAttribute("name"):
211 name = timer.getAttribute("name").encode("UTF-8")
212 # Get name (= match) (V1)
215 name = getValue(timer.getElementsByTagName("name"), "").encode("UTF-8")
218 print '[AutoTimer] Erroneous config is missing attribute "name", skipping entry'
221 # Read out match (V3+)
222 if timer.hasAttribute("match"):
224 match = timer.getAttribute("match").encode("UTF-8")
226 print '[AutoTimer] Erroneous config contains empty attribute "match", skipping entry'
230 # Setting name to match
234 # See if Timer is ensabled (V2+)
235 if timer.hasAttribute("enabled"):
236 enabled = timer.getAttribute("enabled") or "yes"
239 elif enabled == "yes":
242 print '[AutoTimer] Erroneous config contains invalid value for "enabled":', enabled,', skipping entry'
246 elements = timer.getElementsByTagName("enabled")
248 if getValue(elements, "yes") == "no":
256 # Read out timespan (V4+; Falling back on missing definition should be OK)
257 if timer.hasAttribute("from") and timer.hasAttribute("to"):
258 start = timer.getAttribute("from")
259 end = timer.getAttribute("to")
261 start = [int(x) for x in start.split(':')]
262 end = [int(x) for x in end.split(':')]
263 timetuple = (start, end)
268 elements = timer.getElementsByTagName("timespan")
271 # Read out last definition
272 start = elements[Len-1].getAttribute("from")
273 end = elements[Len-1].getAttribute("to")
275 start = [int(x) for x in start.split(':')]
276 end = [int(x) for x in end.split(':')]
277 timetuple = (start, end)
279 print '[AutoTimer] Erroneous config contains invalid definition of "timespan", ignoring definition'
284 # Read out allowed services (V*)
285 elements = timer.getElementsByTagName("serviceref")
288 for service in elements:
289 value = getValue(service, None)
291 # strip all after last :
292 pos = value.rfind(':')
294 value = value[:pos+1]
296 servicelist.append(value)
300 # Read out allowed bouquets (V* though officially supported since V4)
302 for bouquet in timer.getElementsByTagName("bouquet"):
303 value = getValue(bouquet, None)
305 bouquets.append(value)
307 # Read out offset (V4+)
308 if timer.hasAttribute("offset"):
309 offset = timer.getAttribute("offset") or None
311 offset = offset.split(",")
313 before = after = int(offset[0] or 0) * 60
315 before = int(offset[0] or 0) * 60
316 after = int(offset[1] or 0) * 60
317 offset = (before, after)
320 elements = timer.getElementsByTagName("offset")
323 value = elements[Len-1].getAttribute("both")
325 before = int(elements[Len-1].getAttribute("before") or 0) * 60
326 after = int(elements[Len-1].getAttribute("after") or 0) * 60
328 before = after = int(value) * 60
329 offset = (before, after)
334 counter = int(timer.getAttribute("counter") or '0')
335 counterLeft = int(timer.getAttribute("left") or counter)
336 counterLimit = timer.getAttribute("lastActivation")
337 counterFormat = timer.getAttribute("counterFormat")
338 lastBegin = int(timer.getAttribute("lastBegin") or 0)
341 justplay = int(timer.getAttribute("justplay") or '0')
343 # Read out avoidDuplicateDescription
344 avoidDuplicateDescription = bool(timer.getAttribute("avoidDuplicateDescription") or False)
346 # Read out afterevent (compatible to V* though behaviour for V3- is different as V4+ allows multiple afterevents while the last definication was chosen before)
347 idx = {"none": AFTEREVENT.NONE, "standby": AFTEREVENT.STANDBY, "shutdown": AFTEREVENT.DEEPSTANDBY, "deepstandby": AFTEREVENT.DEEPSTANDBY}
349 for element in timer.getElementsByTagName("afterevent"):
350 value = getValue(element, None)
354 start = element.getAttribute("from")
355 end = element.getAttribute("to")
357 start = [int(x) for x in start.split(':')]
358 end = [int(x) for x in end.split(':')]
359 afterevent.append((value, (start, end)))
361 afterevent.append((value, None))
363 print '[AutoTimer] Erroneous config contains invalid value for "afterevent":', afterevent,', ignoring definition'
366 # Read out exclude (V*)
367 idx = {"title": 0, "shortdescription": 1, "description": 2, "dayofweek": 3}
368 excludes = ([], [], [], [])
369 for exclude in timer.getElementsByTagName("exclude"):
370 where = exclude.getAttribute("where")
371 value = getValue(exclude, None)
372 if not (value and where):
376 excludes[idx[where]].append(value.encode("UTF-8"))
380 # Read out includes (use same idx) (V4+ feature, should not harm V3-)
381 includes = ([], [], [], [])
382 for include in timer.getElementsByTagName("include"):
383 where = include.getAttribute("where")
384 value = getValue(include, None)
385 if not (value and where):
389 includes[idx[where]].append(value.encode("UTF-8"))
393 # Read out max length (V4+)
394 if timer.hasAttribute("maxduration"):
395 maxlen = timer.getAttribute("maxduration") or None
397 maxlen = int(maxlen)*60
400 elements = timer.getElementsByTagName("maxduration")
402 maxlen = getValue(elements, None)
403 if maxlen is not None:
404 maxlen = int(maxlen)*60
408 # Read out recording path
409 destination = timer.getAttribute("destination").encode("UTF-8") or None
411 # Read out recording tags (needs my enhanced tag support patch)
413 for tag in timer.getElementsByTagName("tag"):
414 value = getValue(tag, None)
418 tags.append(value.encode("UTF-8"))
420 # Finally append timer
421 list.append(AutoTimerComponent(
426 timespan = timetuple,
427 services = servicelist,
429 afterevent = afterevent,
432 maxduration = maxlen,
433 destination = destination,
434 matchCount = counter,
435 matchLeft = counterLeft,
436 matchLimit = counterLimit,
437 matchFormatString = counterFormat,
438 lastBegin = lastBegin,
440 avoidDuplicateDescription = avoidDuplicateDescription,
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']
449 # XXX: we might want to make sure that we don't save empty default here
450 list.extend([' <defaults'])
453 if defaultTimer.hasTimespan():
454 list.extend([' from="', defaultTimer.getTimespanBegin(), '" to="', defaultTimer.getTimespanEnd(), '"'])
457 if defaultTimer.hasDuration():
458 list.extend([' maxduration="', str(defaultTimer.getDuration()), '"'])
461 if defaultTimer.hasDestination():
462 list.extend([' location="', stringToXML(defaultTimer.destination), '"'])
465 if defaultTimer.hasOffset():
466 if defaultTimer.isOffsetEqual():
467 list.extend([' offset="', str(defaultTimer.getOffsetBegin()), '"'])
469 list.extend([' offset="', str(defaultTimer.getOffsetBegin()), ',', str(defaultTimer.getOffsetEnd()), '"'])
472 if defaultTimer.hasCounter():
473 list.extend([' counter="', str(defaultTimer.getCounter()), '"'])
474 if defaultTimer.hasCounterFormatString():
475 list.extend([' counterFormat="', str(defaultTimer.getCounterFormatString()), '"'])
477 # Duplicate Description
478 if defaultTimer.getAvoidDuplicateDescription():
479 list.append(' avoidDuplicateDescription="1" ')
481 # Only display justplay if true
482 if defaultTimer.justplay:
483 list.extend([' justplay="', str(defaultTimer.getJustplay()), '"'])
485 # Close still opened defaults tag
489 for serviceref in defaultTimer.getServices():
490 list.extend([' <serviceref>', serviceref, '</serviceref>'])
491 ref = ServiceReference(str(serviceref))
492 list.extend([' <!-- ', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), ' -->\n'])
495 for bouquet in defaultTimer.getBouquets():
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'])
501 if defaultTimer.hasAfterEvent():
502 idx = {AFTEREVENT.NONE: "none", AFTEREVENT.STANDBY: "standby", AFTEREVENT.DEEPSTANDBY: "shutdown"}
503 for afterevent in defaultTimer.getCompleteAfterEvent():
504 action, timespan = afterevent
505 list.append(' <afterevent')
506 if timespan[0] is not None:
507 list.append(' from="%02d:%02d" to="%02d:%02d"' % (timespan[0][0], timespan[0][1], timespan[1][0], timespan[1][1]))
508 list.extend(['>', idx[action], '</afterevent>\n'])
511 for title in defaultTimer.getExcludedTitle():
512 list.extend([' <exclude where="title">', stringToXML(title), '</exclude>\n'])
513 for short in defaultTimer.getExcludedShort():
514 list.extend([' <exclude where="shortdescription">', stringToXML(short), '</exclude>\n'])
515 for desc in defaultTimer.getExcludedDescription():
516 list.extend([' <exclude where="description">', stringToXML(desc), '</exclude>\n'])
517 for day in defaultTimer.getExcludedDays():
518 list.extend([' <exclude where="dayofweek">', stringToXML(day), '</exclude>\n'])
521 for title in defaultTimer.getIncludedTitle():
522 list.extend([' <include where="title">', stringToXML(title), '</include>\n'])
523 for short in defaultTimer.getIncludedShort():
524 list.extend([' <include where="shortdescription">', stringToXML(short), '</include>\n'])
525 for desc in defaultTimer.getIncludedDescription():
526 list.extend([' <include where="description">', stringToXML(desc), '</include>\n'])
527 for day in defaultTimer.getIncludedDays():
528 list.extend([' <include where="dayofweek">', stringToXML(day), '</include>\n'])
531 for tag in defaultTimer.tags:
532 list.extend([' <tag>', stringToXML(tag), '</tag>\n'])
535 list.append(' </defaults>\n\n')
539 # Common attributes (match, enabled)
540 list.extend([' <timer name="', stringToXML(timer.name), '" match="', stringToXML(timer.match), '" enabled="', timer.getEnabled(), '"'])
543 if timer.hasTimespan():
544 list.extend([' from="', timer.getTimespanBegin(), '" to="', timer.getTimespanEnd(), '"'])
547 if timer.hasDuration():
548 list.extend([' maxduration="', str(timer.getDuration()), '"'])
551 if timer.hasDestination():
552 list.extend([' location="', stringToXML(timer.destination), '"'])
555 if timer.hasOffset():
556 if timer.isOffsetEqual():
557 list.extend([' offset="', str(timer.getOffsetBegin()), '"'])
559 list.extend([' offset="', str(timer.getOffsetBegin()), ',', str(timer.getOffsetEnd()), '"'])
562 if timer.hasCounter():
563 list.extend([' lastBegin="', str(timer.getLastBegin()), '" counter="', str(timer.getCounter()), '" left="', str(timer.getCounterLeft()) ,'"'])
564 if timer.hasCounterFormatString():
565 list.extend([' lastActivation="', str(timer.getCounterLimit()), '"'])
566 list.extend([' counterFormat="', str(timer.getCounterFormatString()), '"'])
568 # Duplicate Description
569 if timer.getAvoidDuplicateDescription():
570 list.append(' avoidDuplicateDescription="1" ')
572 # Only display justplay if true
574 list.extend([' justplay="', str(timer.getJustplay()), '"'])
576 # Close still opened timer tag
580 for serviceref in timer.getServices():
581 list.extend([' <serviceref>', serviceref, '</serviceref>'])
582 ref = ServiceReference(str(serviceref))
583 list.extend([' <!-- ', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), ' -->\n'])
586 for bouquet in timer.getBouquets():
587 list.extend([' <bouquet>', str(bouquet), '</bouquet>'])
588 ref = ServiceReference(str(bouquet))
589 list.extend([' <!-- ', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), ' -->\n'])
592 if timer.hasAfterEvent():
593 idx = {AFTEREVENT.NONE: "none", AFTEREVENT.STANDBY: "standby", AFTEREVENT.DEEPSTANDBY: "shutdown"}
594 for afterevent in timer.getCompleteAfterEvent():
595 action, timespan = afterevent
596 list.append(' <afterevent')
597 if timespan[0] is not None:
598 list.append(' from="%02d:%02d" to="%02d:%02d"' % (timespan[0][0], timespan[0][1], timespan[1][0], timespan[1][1]))
599 list.extend(['>', idx[action], '</afterevent>\n'])
602 for title in timer.getExcludedTitle():
603 list.extend([' <exclude where="title">', stringToXML(title), '</exclude>\n'])
604 for short in timer.getExcludedShort():
605 list.extend([' <exclude where="shortdescription">', stringToXML(short), '</exclude>\n'])
606 for desc in timer.getExcludedDescription():
607 list.extend([' <exclude where="description">', stringToXML(desc), '</exclude>\n'])
608 for day in timer.getExcludedDays():
609 list.extend([' <exclude where="dayofweek">', stringToXML(day), '</exclude>\n'])
612 for title in timer.getIncludedTitle():
613 list.extend([' <include where="title">', stringToXML(title), '</include>\n'])
614 for short in timer.getIncludedShort():
615 list.extend([' <include where="shortdescription">', stringToXML(short), '</include>\n'])
616 for desc in timer.getIncludedDescription():
617 list.extend([' <include where="description">', stringToXML(desc), '</include>\n'])
618 for day in timer.getIncludedDays():
619 list.extend([' <include where="dayofweek">', stringToXML(day), '</include>\n'])
622 for tag in timer.tags:
623 list.extend([' <tag>', stringToXML(tag), '</tag>\n'])
626 list.append(' </timer>\n\n')
628 # End of Configuration
629 list.append('</autotimer>\n')
632 file = open(filename, 'w')
633 file.writelines(list)