1 # for localized messages
4 from AutoTimerComponent import AutoTimerComponent
5 from RecordTimer import AFTEREVENT
6 from Tools.XMLTools import stringToXML
7 from ServiceReference import ServiceReference
9 CURRENT_CONFIG_VERSION = "5"
11 def getValue(definitions, default):
15 # How many definitions are present
17 childNodes = definitions.childNodes
19 Len = len(definitions)
21 childNodes = definitions[Len-1].childNodes
25 # Iterate through nodes of last one
26 for node in childNodes:
27 # Append text if we have a text node
28 if node.nodeType == node.TEXT_NODE:
31 # Return stripped output or (if empty) default
32 return ret.strip() or default
34 def parseConfig(configuration, list, version = None, uniqueTimerId = 0, defaultTimer = None):
35 if version != CURRENT_CONFIG_VERSION:
36 parseConfigOld(configuration, list, uniqueTimerId)
39 if defaultTimer is not None:
40 # Read in defaults for a new timer
41 for defaults in configuration.getElementsByTagName("defaults"):
42 parseEntry(defaults, defaultTimer, True)
44 for timer in configuration.getElementsByTagName("timer"):
46 baseTimer = AutoTimerComponent(
53 if parseEntry(timer, baseTimer):
54 list.append(baseTimer)
56 def parseEntry(element, baseTimer, defaults = False):
59 baseTimer.match = element.getAttribute("match").encode("UTF-8")
60 if not baseTimer.match:
61 print '[AutoTimer] Erroneous config is missing attribute "match", skipping entry'
65 baseTimer.name = element.getAttribute("name").encode("UTF-8")
66 if not baseTimer.name:
67 print '[AutoTimer] Timer is missing attribute "name", defaulting to match'
68 baseTimer.name = baseTimer.match
71 enabled = element.getAttribute("enabled") or "yes"
73 baseTimer.enabled = False
74 elif enabled == "yes":
75 baseTimer.enabled = True
77 print '[AutoTimer] Erroneous config contains invalid value for "enabled":', enabled,', disabling'
78 baseTimer.enabled = False
81 start = element.getAttribute("from")
82 end = element.getAttribute("to")
84 start = [int(x) for x in start.split(':')]
85 end = [int(x) for x in end.split(':')]
86 baseTimer.timespan = (start, end)
89 maxduration = element.getAttribute("maxduration") or None
91 baseTimer.maxduration = int(maxlen)*60
93 # Read out recording path
94 baseTimer.destination = element.getAttribute("location").encode("UTF-8") or None
97 offset = element.getAttribute("offset") or None
99 offset = offset.split(",")
101 before = after = int(offset[0] or 0) * 60
103 before = int(offset[0] or 0) * 60
104 after = int(offset[1] or 0) * 60
105 baseTimer.offset = (before, after)
108 baseTimer.matchCount = int(element.getAttribute("counter") or '0')
109 baseTimer.matchFormatString = element.getAttribute("counterFormat")
111 baseTimer.counterLimit = element.getAttribute("lastActivation")
112 baseTimer.counterFormat = element.getAttribute("counterFormat")
113 baseTimer.lastBegin = int(element.getAttribute("lastBegin") or 0)
116 justplay = int(element.getAttribute("justplay") or '0')
118 # Read out avoidDuplicateDescription
119 baseTimer.avoidDuplicateDescription = bool(element.getAttribute("avoidDuplicateDescription") or False)
121 # Read out allowed services
122 servicelist = baseTimer.services
123 for service in element.getElementsByTagName("serviceref"):
124 value = getValue(service, None)
126 # strip all after last :
127 pos = value.rfind(':')
129 value = value[:pos+1]
131 servicelist.append(value)
132 baseTimer.services = servicelist
134 # Read out allowed bouquets
135 bouquets = baseTimer.bouquets
136 for bouquet in element.getElementsByTagName("bouquet"):
137 value = getValue(bouquet, None)
139 bouquets.append(value)
140 baseTimer.bouquets = bouquets
142 # Read out afterevent
143 idx = {"none": AFTEREVENT.NONE, "standby": AFTEREVENT.STANDBY, "shutdown": AFTEREVENT.DEEPSTANDBY, "deepstandby": AFTEREVENT.DEEPSTANDBY}
144 afterevent = baseTimer.afterevent
145 for element in element.getElementsByTagName("afterevent"):
146 value = getValue(element, None)
150 start = element.getAttribute("from")
151 end = element.getAttribute("to")
153 start = [int(x) for x in start.split(':')]
154 end = [int(x) for x in end.split(':')]
155 afterevent.append((value, (start, end)))
157 afterevent.append((value, None))
159 print '[AutoTimer] Erroneous config contains invalid value for "afterevent":', afterevent,', ignoring definition'
161 baseTimer.afterevent = afterevent
164 idx = {"title": 0, "shortdescription": 1, "description": 2, "dayofweek": 3}
165 excludes = (baseTimer.getExcludedTitle(), baseTimer.getExcludedShort(), baseTimer.getExcludedDescription(), baseTimer.getExcludedDays())
166 for exclude in element.getElementsByTagName("exclude"):
167 where = exclude.getAttribute("where")
168 value = getValue(exclude, None)
169 if not (value and where):
173 excludes[idx[where]].append(value.encode("UTF-8"))
176 baseTimer.excludes = excludes
178 # Read out includes (use same idx)
179 includes = (baseTimer.getIncludedTitle(), baseTimer.getIncludedShort(), baseTimer.getIncludedDescription(), baseTimer.getIncludedDays())
180 for include in element.getElementsByTagName("include"):
181 where = include.getAttribute("where")
182 value = getValue(include, None)
183 if not (value and where):
187 includes[idx[where]].append(value.encode("UTF-8"))
190 baseTimer.includes = includes
192 # Read out recording tags (needs my enhanced tag support patch)
193 tags = baseTimer.tags
194 for tag in element.getElementsByTagName("tag"):
195 value = getValue(tag, None)
199 tags.append(value.encode("UTF-8"))
200 baseTimer.tags = tags
204 def parseConfigOld(configuration, list, uniqueTimerId = 0):
205 print "[AutoTimer] Trying to parse old config"
208 for timer in configuration.getElementsByTagName("timer"):
209 # Increment uniqueTimerId
213 if timer.hasAttribute("name"):
214 name = timer.getAttribute("name").encode("UTF-8")
215 # Get name (= match) (V1)
218 name = getValue(timer.getElementsByTagName("name"), "").encode("UTF-8")
221 print '[AutoTimer] Erroneous config is missing attribute "name", skipping entry'
224 # Read out match (V3+)
225 if timer.hasAttribute("match"):
227 match = timer.getAttribute("match").encode("UTF-8")
229 print '[AutoTimer] Erroneous config contains empty attribute "match", skipping entry'
233 # Setting name to match
237 # See if Timer is ensabled (V2+)
238 if timer.hasAttribute("enabled"):
239 enabled = timer.getAttribute("enabled") or "yes"
242 elif enabled == "yes":
245 print '[AutoTimer] Erroneous config contains invalid value for "enabled":', enabled,', skipping entry'
249 elements = timer.getElementsByTagName("enabled")
251 if getValue(elements, "yes") == "no":
259 # Read out timespan (V4+; Falling back on missing definition should be OK)
260 if timer.hasAttribute("from") and timer.hasAttribute("to"):
261 start = timer.getAttribute("from")
262 end = timer.getAttribute("to")
264 start = [int(x) for x in start.split(':')]
265 end = [int(x) for x in end.split(':')]
266 timetuple = (start, end)
271 elements = timer.getElementsByTagName("timespan")
274 # Read out last definition
275 start = elements[Len-1].getAttribute("from")
276 end = elements[Len-1].getAttribute("to")
278 start = [int(x) for x in start.split(':')]
279 end = [int(x) for x in end.split(':')]
280 timetuple = (start, end)
282 print '[AutoTimer] Erroneous config contains invalid definition of "timespan", ignoring definition'
287 # Read out allowed services (V*)
288 elements = timer.getElementsByTagName("serviceref")
291 for service in elements:
292 value = getValue(service, None)
294 # strip all after last :
295 pos = value.rfind(':')
297 value = value[:pos+1]
299 servicelist.append(value)
303 # Read out allowed bouquets (V* though officially supported since V4)
305 for bouquet in timer.getElementsByTagName("bouquet"):
306 value = getValue(bouquet, None)
308 bouquets.append(value)
310 # Read out offset (V4+)
311 if timer.hasAttribute("offset"):
312 offset = timer.getAttribute("offset") or None
314 offset = offset.split(",")
316 before = after = int(offset[0] or 0) * 60
318 before = int(offset[0] or 0) * 60
319 after = int(offset[1] or 0) * 60
320 offset = (before, after)
323 elements = timer.getElementsByTagName("offset")
326 value = elements[Len-1].getAttribute("both")
328 before = int(elements[Len-1].getAttribute("before") or 0) * 60
329 after = int(elements[Len-1].getAttribute("after") or 0) * 60
331 before = after = int(value) * 60
332 offset = (before, after)
337 counter = int(timer.getAttribute("counter") or '0')
338 counterLeft = int(timer.getAttribute("left") or counter)
339 counterLimit = timer.getAttribute("lastActivation")
340 counterFormat = timer.getAttribute("counterFormat")
341 lastBegin = int(timer.getAttribute("lastBegin") or 0)
344 justplay = int(timer.getAttribute("justplay") or '0')
346 # Read out avoidDuplicateDescription
347 avoidDuplicateDescription = bool(timer.getAttribute("avoidDuplicateDescription") or False)
349 # Read out afterevent (compatible to V* though behaviour for V3- is different as V4+ allows multiple afterevents while the last definication was chosen before)
350 idx = {"none": AFTEREVENT.NONE, "standby": AFTEREVENT.STANDBY, "shutdown": AFTEREVENT.DEEPSTANDBY, "deepstandby": AFTEREVENT.DEEPSTANDBY}
352 for element in timer.getElementsByTagName("afterevent"):
353 value = getValue(element, None)
357 start = element.getAttribute("from")
358 end = element.getAttribute("to")
360 start = [int(x) for x in start.split(':')]
361 end = [int(x) for x in end.split(':')]
362 afterevent.append((value, (start, end)))
364 afterevent.append((value, None))
366 print '[AutoTimer] Erroneous config contains invalid value for "afterevent":', afterevent,', ignoring definition'
369 # Read out exclude (V*)
370 idx = {"title": 0, "shortdescription": 1, "description": 2, "dayofweek": 3}
371 excludes = ([], [], [], [])
372 for exclude in timer.getElementsByTagName("exclude"):
373 where = exclude.getAttribute("where")
374 value = getValue(exclude, None)
375 if not (value and where):
379 excludes[idx[where]].append(value.encode("UTF-8"))
383 # Read out includes (use same idx) (V4+ feature, should not harm V3-)
384 includes = ([], [], [], [])
385 for include in timer.getElementsByTagName("include"):
386 where = include.getAttribute("where")
387 value = getValue(include, None)
388 if not (value and where):
392 includes[idx[where]].append(value.encode("UTF-8"))
396 # Read out max length (V4+)
397 if timer.hasAttribute("maxduration"):
398 maxlen = timer.getAttribute("maxduration") or None
400 maxlen = int(maxlen)*60
403 elements = timer.getElementsByTagName("maxduration")
405 maxlen = getValue(elements, None)
406 if maxlen is not None:
407 maxlen = int(maxlen)*60
411 # Read out recording path
412 destination = timer.getAttribute("destination").encode("UTF-8") or None
414 # Read out recording tags (needs my enhanced tag support patch)
416 for tag in timer.getElementsByTagName("tag"):
417 value = getValue(tag, None)
421 tags.append(value.encode("UTF-8"))
423 # Finally append timer
424 list.append(AutoTimerComponent(
429 timespan = timetuple,
430 services = servicelist,
432 afterevent = afterevent,
435 maxduration = maxlen,
436 destination = destination,
437 matchCount = counter,
438 matchLeft = counterLeft,
439 matchLimit = counterLimit,
440 matchFormatString = counterFormat,
441 lastBegin = lastBegin,
443 avoidDuplicateDescription = avoidDuplicateDescription,
448 def writeConfig(filename, defaultTimer, timers):
449 # Generate List in RAM
450 list = ['<?xml version="1.0" ?>\n<autotimer version="', CURRENT_CONFIG_VERSION, '">\n\n']
452 # XXX: we might want to make sure that we don't save empty default here
453 list.extend([' <defaults'])
456 if defaultTimer.hasTimespan():
457 list.extend([' from="', defaultTimer.getTimespanBegin(), '" to="', defaultTimer.getTimespanEnd(), '"'])
460 if defaultTimer.hasDuration():
461 list.extend([' maxduration="', str(defaultTimer.getDuration()), '"'])
464 if defaultTimer.hasDestination():
465 list.extend([' location="', stringToXML(defaultTimer.destination), '"'])
468 if defaultTimer.hasOffset():
469 if defaultTimer.isOffsetEqual():
470 list.extend([' offset="', str(defaultTimer.getOffsetBegin()), '"'])
472 list.extend([' offset="', str(defaultTimer.getOffsetBegin()), ',', str(defaultTimer.getOffsetEnd()), '"'])
475 if defaultTimer.hasCounter():
476 list.extend([' counter="', str(defaultTimer.getCounter()), '"'])
477 if defaultTimer.hasCounterFormatString():
478 list.extend([' counterFormat="', str(defaultTimer.getCounterFormatString()), '"'])
480 # Duplicate Description
481 if defaultTimer.getAvoidDuplicateDescription():
482 list.append(' avoidDuplicateDescription="1" ')
484 # Only display justplay if true
485 if defaultTimer.justplay:
486 list.extend([' justplay="', str(defaultTimer.getJustplay()), '"'])
488 # Close still opened defaults tag
492 for serviceref in defaultTimer.getServices():
493 list.extend([' <serviceref>', serviceref, '</serviceref>'])
494 ref = ServiceReference(str(serviceref))
495 list.extend([' <!-- ', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), ' -->\n'])
498 for bouquet in defaultTimer.getBouquets():
499 list.extend([' <bouquet>', str(bouquet), '</bouquet>'])
500 ref = ServiceReference(str(bouquet))
501 list.extend([' <!-- ', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), ' -->\n'])
504 if defaultTimer.hasAfterEvent():
505 idx = {AFTEREVENT.NONE: "none", AFTEREVENT.STANDBY: "standby", AFTEREVENT.DEEPSTANDBY: "shutdown"}
506 for afterevent in defaultTimer.getCompleteAfterEvent():
507 action, timespan = afterevent
508 list.append(' <afterevent')
509 if timespan[0] is not None:
510 list.append(' from="%02d:%02d" to="%02d:%02d"' % (timespan[0][0], timespan[0][1], timespan[1][0], timespan[1][1]))
511 list.extend(['>', idx[action], '</afterevent>\n'])
514 for title in defaultTimer.getExcludedTitle():
515 list.extend([' <exclude where="title">', stringToXML(title), '</exclude>\n'])
516 for short in defaultTimer.getExcludedShort():
517 list.extend([' <exclude where="shortdescription">', stringToXML(short), '</exclude>\n'])
518 for desc in defaultTimer.getExcludedDescription():
519 list.extend([' <exclude where="description">', stringToXML(desc), '</exclude>\n'])
520 for day in defaultTimer.getExcludedDays():
521 list.extend([' <exclude where="dayofweek">', stringToXML(day), '</exclude>\n'])
524 for title in defaultTimer.getIncludedTitle():
525 list.extend([' <include where="title">', stringToXML(title), '</include>\n'])
526 for short in defaultTimer.getIncludedShort():
527 list.extend([' <include where="shortdescription">', stringToXML(short), '</include>\n'])
528 for desc in defaultTimer.getIncludedDescription():
529 list.extend([' <include where="description">', stringToXML(desc), '</include>\n'])
530 for day in defaultTimer.getIncludedDays():
531 list.extend([' <include where="dayofweek">', stringToXML(day), '</include>\n'])
534 for tag in defaultTimer.tags:
535 list.extend([' <tag>', stringToXML(tag), '</tag>\n'])
538 list.append(' </defaults>\n\n')
542 # Common attributes (match, enabled)
543 list.extend([' <timer name="', stringToXML(timer.name), '" match="', stringToXML(timer.match), '" enabled="', timer.getEnabled(), '"'])
546 if timer.hasTimespan():
547 list.extend([' from="', timer.getTimespanBegin(), '" to="', timer.getTimespanEnd(), '"'])
550 if timer.hasDuration():
551 list.extend([' maxduration="', str(timer.getDuration()), '"'])
554 if timer.hasDestination():
555 list.extend([' location="', stringToXML(timer.destination), '"'])
558 if timer.hasOffset():
559 if timer.isOffsetEqual():
560 list.extend([' offset="', str(timer.getOffsetBegin()), '"'])
562 list.extend([' offset="', str(timer.getOffsetBegin()), ',', str(timer.getOffsetEnd()), '"'])
565 if timer.hasCounter():
566 list.extend([' lastBegin="', str(timer.getLastBegin()), '" counter="', str(timer.getCounter()), '" left="', str(timer.getCounterLeft()) ,'"'])
567 if timer.hasCounterFormatString():
568 list.extend([' lastActivation="', str(timer.getCounterLimit()), '"'])
569 list.extend([' counterFormat="', str(timer.getCounterFormatString()), '"'])
571 # Duplicate Description
572 if timer.getAvoidDuplicateDescription():
573 list.append(' avoidDuplicateDescription="1" ')
575 # Only display justplay if true
577 list.extend([' justplay="', str(timer.getJustplay()), '"'])
579 # Close still opened timer tag
583 for serviceref in timer.getServices():
584 list.extend([' <serviceref>', serviceref, '</serviceref>'])
585 ref = ServiceReference(str(serviceref))
586 list.extend([' <!-- ', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), ' -->\n'])
589 for bouquet in timer.getBouquets():
590 list.extend([' <bouquet>', str(bouquet), '</bouquet>'])
591 ref = ServiceReference(str(bouquet))
592 list.extend([' <!-- ', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), ' -->\n'])
595 if timer.hasAfterEvent():
596 idx = {AFTEREVENT.NONE: "none", AFTEREVENT.STANDBY: "standby", AFTEREVENT.DEEPSTANDBY: "shutdown"}
597 for afterevent in timer.getCompleteAfterEvent():
598 action, timespan = afterevent
599 list.append(' <afterevent')
600 if timespan[0] is not None:
601 list.append(' from="%02d:%02d" to="%02d:%02d"' % (timespan[0][0], timespan[0][1], timespan[1][0], timespan[1][1]))
602 list.extend(['>', idx[action], '</afterevent>\n'])
605 for title in timer.getExcludedTitle():
606 list.extend([' <exclude where="title">', stringToXML(title), '</exclude>\n'])
607 for short in timer.getExcludedShort():
608 list.extend([' <exclude where="shortdescription">', stringToXML(short), '</exclude>\n'])
609 for desc in timer.getExcludedDescription():
610 list.extend([' <exclude where="description">', stringToXML(desc), '</exclude>\n'])
611 for day in timer.getExcludedDays():
612 list.extend([' <exclude where="dayofweek">', stringToXML(day), '</exclude>\n'])
615 for title in timer.getIncludedTitle():
616 list.extend([' <include where="title">', stringToXML(title), '</include>\n'])
617 for short in timer.getIncludedShort():
618 list.extend([' <include where="shortdescription">', stringToXML(short), '</include>\n'])
619 for desc in timer.getIncludedDescription():
620 list.extend([' <include where="description">', stringToXML(desc), '</include>\n'])
621 for day in timer.getIncludedDays():
622 list.extend([' <include where="dayofweek">', stringToXML(day), '</include>\n'])
625 for tag in timer.tags:
626 list.extend([' <tag>', stringToXML(tag), '</tag>\n'])
629 list.append(' </timer>\n\n')
631 # End of Configuration
632 list.append('</autotimer>\n')
635 file = open(filename, 'w')
636 file.writelines(list)