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
16 if isinstance(definitions, list):
17 Len = len(definitions)
19 childNodes = definitions[Len-1].childNodes
23 childNodes = definitions.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(maxduration)*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 = int(element.getAttribute("avoidDuplicateDescription") or 0)
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)
151 print '[AutoTimer] Erroneous config contains invalid value for "afterevent":', afterevent,', ignoring definition'
154 start = element.getAttribute("from")
155 end = element.getAttribute("to")
157 start = [int(x) for x in start.split(':')]
158 end = [int(x) for x in end.split(':')]
159 afterevent.append((value, (start, end)))
161 afterevent.append((value, None))
162 baseTimer.afterevent = afterevent
165 idx = {"title": 0, "shortdescription": 1, "description": 2, "dayofweek": 3}
166 excludes = (baseTimer.getExcludedTitle(), baseTimer.getExcludedShort(), baseTimer.getExcludedDescription(), baseTimer.getExcludedDays())
167 for exclude in element.getElementsByTagName("exclude"):
168 where = exclude.getAttribute("where")
169 value = getValue(exclude, None)
170 if not (value and where):
174 excludes[idx[where]].append(value.encode("UTF-8"))
177 baseTimer.exclude = excludes
179 # Read out includes (use same idx)
180 includes = (baseTimer.getIncludedTitle(), baseTimer.getIncludedShort(), baseTimer.getIncludedDescription(), baseTimer.getIncludedDays())
181 for include in element.getElementsByTagName("include"):
182 where = include.getAttribute("where")
183 value = getValue(include, None)
184 if not (value and where):
188 includes[idx[where]].append(value.encode("UTF-8"))
191 baseTimer.include = includes
193 # Read out recording tags (needs my enhanced tag support patch)
194 tags = baseTimer.tags
195 for tag in element.getElementsByTagName("tag"):
196 value = getValue(tag, None)
200 tags.append(value.encode("UTF-8"))
201 baseTimer.tags = tags
205 def parseConfigOld(configuration, list, uniqueTimerId = 0):
206 print "[AutoTimer] Trying to parse old config"
209 for timer in configuration.getElementsByTagName("timer"):
210 # Increment uniqueTimerId
214 if timer.hasAttribute("name"):
215 name = timer.getAttribute("name").encode("UTF-8")
216 # Get name (= match) (V1)
219 name = getValue(timer.getElementsByTagName("name"), "").encode("UTF-8")
222 print '[AutoTimer] Erroneous config is missing attribute "name", skipping entry'
225 # Read out match (V3+)
226 if timer.hasAttribute("match"):
228 match = timer.getAttribute("match").encode("UTF-8")
230 print '[AutoTimer] Erroneous config contains empty attribute "match", skipping entry'
234 # Setting name to match
238 # See if Timer is ensabled (V2+)
239 if timer.hasAttribute("enabled"):
240 enabled = timer.getAttribute("enabled") or "yes"
243 elif enabled == "yes":
246 print '[AutoTimer] Erroneous config contains invalid value for "enabled":', enabled,', skipping entry'
250 elements = timer.getElementsByTagName("enabled")
252 if getValue(elements, "yes") == "no":
260 # Read out timespan (V4+; Falling back on missing definition should be OK)
261 if timer.hasAttribute("from") and timer.hasAttribute("to"):
262 start = timer.getAttribute("from")
263 end = timer.getAttribute("to")
265 start = [int(x) for x in start.split(':')]
266 end = [int(x) for x in end.split(':')]
267 timetuple = (start, end)
272 elements = timer.getElementsByTagName("timespan")
275 # Read out last definition
276 start = elements[Len-1].getAttribute("from")
277 end = elements[Len-1].getAttribute("to")
279 start = [int(x) for x in start.split(':')]
280 end = [int(x) for x in end.split(':')]
281 timetuple = (start, end)
283 print '[AutoTimer] Erroneous config contains invalid definition of "timespan", ignoring definition'
288 # Read out allowed services (V*)
289 elements = timer.getElementsByTagName("serviceref")
292 for service in elements:
293 value = getValue(service, None)
295 # strip all after last :
296 pos = value.rfind(':')
298 value = value[:pos+1]
300 servicelist.append(value)
304 # Read out allowed bouquets (V* though officially supported since V4)
306 for bouquet in timer.getElementsByTagName("bouquet"):
307 value = getValue(bouquet, None)
309 bouquets.append(value)
311 # Read out offset (V4+)
312 if timer.hasAttribute("offset"):
313 offset = timer.getAttribute("offset") or None
315 offset = offset.split(",")
317 before = after = int(offset[0] or 0) * 60
319 before = int(offset[0] or 0) * 60
320 after = int(offset[1] or 0) * 60
321 offset = (before, after)
324 elements = timer.getElementsByTagName("offset")
327 value = elements[Len-1].getAttribute("both")
329 before = int(elements[Len-1].getAttribute("before") or 0) * 60
330 after = int(elements[Len-1].getAttribute("after") or 0) * 60
332 before = after = int(value) * 60
333 offset = (before, after)
338 counter = int(timer.getAttribute("counter") or '0')
339 counterLeft = int(timer.getAttribute("left") or counter)
340 counterLimit = timer.getAttribute("lastActivation")
341 counterFormat = timer.getAttribute("counterFormat")
342 lastBegin = int(timer.getAttribute("lastBegin") or 0)
345 justplay = int(timer.getAttribute("justplay") or '0')
347 # Read out avoidDuplicateDescription
348 avoidDuplicateDescription = int(timer.getAttribute("avoidDuplicateDescription") or 0)
350 # Read out afterevent (compatible to V* though behaviour for V3- is different as V4+ allows multiple afterevents while the last definication was chosen before)
351 idx = {"none": AFTEREVENT.NONE, "standby": AFTEREVENT.STANDBY, "shutdown": AFTEREVENT.DEEPSTANDBY, "deepstandby": AFTEREVENT.DEEPSTANDBY}
353 for element in timer.getElementsByTagName("afterevent"):
354 value = getValue(element, None)
359 print '[AutoTimer] Erroneous config contains invalid value for "afterevent":', afterevent,', ignoring definition'
362 start = element.getAttribute("from")
363 end = element.getAttribute("to")
365 start = [int(x) for x in start.split(':')]
366 end = [int(x) for x in end.split(':')]
367 afterevent.append((value, (start, end)))
369 afterevent.append((value, None))
371 # Read out exclude (V*)
372 idx = {"title": 0, "shortdescription": 1, "description": 2, "dayofweek": 3}
373 excludes = ([], [], [], [])
374 for exclude in timer.getElementsByTagName("exclude"):
375 where = exclude.getAttribute("where")
376 value = getValue(exclude, None)
377 if not (value and where):
381 excludes[idx[where]].append(value.encode("UTF-8"))
385 # Read out includes (use same idx) (V4+ feature, should not harm V3-)
386 includes = ([], [], [], [])
387 for include in timer.getElementsByTagName("include"):
388 where = include.getAttribute("where")
389 value = getValue(include, None)
390 if not (value and where):
394 includes[idx[where]].append(value.encode("UTF-8"))
398 # Read out max length (V4+)
399 if timer.hasAttribute("maxduration"):
400 maxlen = timer.getAttribute("maxduration") or None
402 maxlen = int(maxlen)*60
405 elements = timer.getElementsByTagName("maxduration")
407 maxlen = getValue(elements, None)
408 if maxlen is not None:
409 maxlen = int(maxlen)*60
413 # Read out recording path
414 destination = timer.getAttribute("destination").encode("UTF-8") or None
416 # Read out recording tags (needs my enhanced tag support patch)
418 for tag in timer.getElementsByTagName("tag"):
419 value = getValue(tag, None)
423 tags.append(value.encode("UTF-8"))
425 # Finally append timer
426 list.append(AutoTimerComponent(
431 timespan = timetuple,
432 services = servicelist,
434 afterevent = afterevent,
437 maxduration = maxlen,
438 destination = destination,
439 matchCount = counter,
440 matchLeft = counterLeft,
441 matchLimit = counterLimit,
442 matchFormatString = counterFormat,
443 lastBegin = lastBegin,
445 avoidDuplicateDescription = avoidDuplicateDescription,
450 def writeConfig(filename, defaultTimer, timers):
451 # Generate List in RAM
452 list = ['<?xml version="1.0" ?>\n<autotimer version="', CURRENT_CONFIG_VERSION, '">\n\n']
454 # XXX: we might want to make sure that we don't save empty default here
455 list.extend([' <defaults'])
458 if defaultTimer.hasTimespan():
459 list.extend([' from="', defaultTimer.getTimespanBegin(), '" to="', defaultTimer.getTimespanEnd(), '"'])
462 if defaultTimer.hasDuration():
463 list.extend([' maxduration="', str(defaultTimer.getDuration()), '"'])
466 if defaultTimer.hasDestination():
467 list.extend([' location="', stringToXML(defaultTimer.destination), '"'])
470 if defaultTimer.hasOffset():
471 if defaultTimer.isOffsetEqual():
472 list.extend([' offset="', str(defaultTimer.getOffsetBegin()), '"'])
474 list.extend([' offset="', str(defaultTimer.getOffsetBegin()), ',', str(defaultTimer.getOffsetEnd()), '"'])
477 if defaultTimer.hasCounter():
478 list.extend([' counter="', str(defaultTimer.getCounter()), '"'])
479 if defaultTimer.hasCounterFormatString():
480 list.extend([' counterFormat="', str(defaultTimer.getCounterFormatString()), '"'])
482 # Duplicate Description
483 if defaultTimer.getAvoidDuplicateDescription():
484 list.append(' avoidDuplicateDescription="1" ')
486 # Only display justplay if true
487 if defaultTimer.justplay:
488 list.extend([' justplay="', str(defaultTimer.getJustplay()), '"'])
490 # Close still opened defaults tag
494 for serviceref in defaultTimer.getServices():
495 list.extend([' <serviceref>', serviceref, '</serviceref>'])
496 ref = ServiceReference(str(serviceref))
497 list.extend([' <!-- ', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), ' -->\n'])
500 for bouquet in defaultTimer.getBouquets():
501 list.extend([' <bouquet>', str(bouquet), '</bouquet>'])
502 ref = ServiceReference(str(bouquet))
503 list.extend([' <!-- ', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), ' -->\n'])
506 if defaultTimer.hasAfterEvent():
507 idx = {AFTEREVENT.NONE: "none", AFTEREVENT.STANDBY: "standby", AFTEREVENT.DEEPSTANDBY: "shutdown"}
508 for afterevent in defaultTimer.getCompleteAfterEvent():
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'])
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'])
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'])
536 for tag in defaultTimer.tags:
537 list.extend([' <tag>', stringToXML(tag), '</tag>\n'])
540 list.append(' </defaults>\n\n')
544 # Common attributes (match, enabled)
545 list.extend([' <timer name="', stringToXML(timer.name), '" match="', stringToXML(timer.match), '" enabled="', timer.getEnabled(), '"'])
548 if timer.hasTimespan():
549 list.extend([' from="', timer.getTimespanBegin(), '" to="', timer.getTimespanEnd(), '"'])
552 if timer.hasDuration():
553 list.extend([' maxduration="', str(timer.getDuration()), '"'])
556 if timer.hasDestination():
557 list.extend([' location="', stringToXML(timer.destination), '"'])
560 if timer.hasOffset():
561 if timer.isOffsetEqual():
562 list.extend([' offset="', str(timer.getOffsetBegin()), '"'])
564 list.extend([' offset="', str(timer.getOffsetBegin()), ',', str(timer.getOffsetEnd()), '"'])
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()), '"'])
573 # Duplicate Description
574 if timer.getAvoidDuplicateDescription():
575 list.extend([' avoidDuplicateDescription="', str(timer.getAvoidDuplicateDescription()), '"'])
577 # Only display justplay if true
579 list.extend([' justplay="', str(timer.getJustplay()), '"'])
581 # Close still opened timer tag
585 for serviceref in timer.getServices():
586 list.extend([' <serviceref>', serviceref, '</serviceref>'])
587 ref = ServiceReference(str(serviceref))
588 list.extend([' <!-- ', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), ' -->\n'])
591 for bouquet in timer.getBouquets():
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'])
597 if timer.hasAfterEvent():
598 idx = {AFTEREVENT.NONE: "none", AFTEREVENT.STANDBY: "standby", AFTEREVENT.DEEPSTANDBY: "shutdown"}
599 for afterevent in timer.getCompleteAfterEvent():
600 action, timespan = afterevent
601 list.append(' <afterevent')
602 if timespan[0] is not None:
603 list.append(' from="%02d:%02d" to="%02d:%02d"' % (timespan[0][0], timespan[0][1], timespan[1][0], timespan[1][1]))
604 list.extend(['>', idx[action], '</afterevent>\n'])
607 for title in timer.getExcludedTitle():
608 list.extend([' <exclude where="title">', stringToXML(title), '</exclude>\n'])
609 for short in timer.getExcludedShort():
610 list.extend([' <exclude where="shortdescription">', stringToXML(short), '</exclude>\n'])
611 for desc in timer.getExcludedDescription():
612 list.extend([' <exclude where="description">', stringToXML(desc), '</exclude>\n'])
613 for day in timer.getExcludedDays():
614 list.extend([' <exclude where="dayofweek">', stringToXML(day), '</exclude>\n'])
617 for title in timer.getIncludedTitle():
618 list.extend([' <include where="title">', stringToXML(title), '</include>\n'])
619 for short in timer.getIncludedShort():
620 list.extend([' <include where="shortdescription">', stringToXML(short), '</include>\n'])
621 for desc in timer.getIncludedDescription():
622 list.extend([' <include where="description">', stringToXML(desc), '</include>\n'])
623 for day in timer.getIncludedDays():
624 list.extend([' <include where="dayofweek">', stringToXML(day), '</include>\n'])
627 for tag in timer.tags:
628 list.extend([' <tag>', stringToXML(tag), '</tag>\n'])
631 list.append(' </timer>\n\n')
633 # End of Configuration
634 list.append('</autotimer>\n')
637 file = open(filename, 'w')
638 file.writelines(list)