1 # -*- coding: UTF-8 -*-
2 # for localized messages
5 from AutoTimerComponent import AutoTimerComponent
6 from RecordTimer import AFTEREVENT
7 from Tools.XMLTools import stringToXML
8 from ServiceReference import ServiceReference
10 CURRENT_CONFIG_VERSION = "5"
12 def getValue(definitions, default):
16 # How many definitions are present
17 if isinstance(definitions, list):
18 Len = len(definitions)
20 childNodes = definitions[Len-1].text
24 ret = definitions.text
26 # Return stripped output or (if empty) default
27 return ret.strip() or default
29 def parseConfig(configuration, list, version = None, uniqueTimerId = 0, defaultTimer = None):
30 if version != CURRENT_CONFIG_VERSION:
31 parseConfigOld(configuration, list, uniqueTimerId)
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)
39 for timer in configuration.findall("timer"):
41 baseTimer = AutoTimerComponent(
48 if parseEntry(timer, baseTimer):
49 list.append(baseTimer)
51 def parseEntry(element, baseTimer, defaults = False):
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'
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
66 enabled = element.get("enabled", "yes")
68 baseTimer.enabled = False
69 elif enabled == "yes":
70 baseTimer.enabled = True
72 print '[AutoTimer] Erroneous config contains invalid value for "enabled":', enabled,', disabling'
73 baseTimer.enabled = False
76 start = element.get("from")
77 end = element.get("to")
79 start = [int(x) for x in start.split(':')]
80 end = [int(x) for x in end.split(':')]
81 baseTimer.timespan = (start, end)
84 maxduration = element.get("maxduration")
86 baseTimer.maxduration = int(maxduration)*60
88 # Read out recording path
89 baseTimer.destination = element.get("location", "").encode("UTF-8") or None
92 offset = element.get("offset")
94 offset = offset.split(",")
96 before = after = int(offset[0] or 0) * 60
98 before = int(offset[0] or 0) * 60
99 after = int(offset[1] or 0) * 60
100 baseTimer.offset = (before, after)
103 baseTimer.matchCount = int(element.get("counter", 0))
104 baseTimer.matchFormatString = element.get("counterFormat", "")
106 baseTimer.matchLeft = int(element.get("left", baseTimer.matchCount))
107 baseTimer.matchLimit = element.get("lastActivation", "")
108 baseTimer.lastBegin = int(element.get("lastBegin", 0))
111 justplay = int(element.get("justplay", 0))
113 # Read out avoidDuplicateDescription
114 baseTimer.avoidDuplicateDescription = int(element.get("avoidDuplicateDescription", 0))
116 # Read out allowed services
117 servicelist = baseTimer.services
118 for service in element.findall("serviceref"):
121 # strip all after last :
122 pos = value.rfind(':')
124 value = value[:pos+1]
126 servicelist.append(value)
127 baseTimer.services = servicelist
129 # Read out allowed bouquets
130 bouquets = baseTimer.bouquets
131 for bouquet in element.findall("bouquet"):
134 bouquets.append(value)
135 baseTimer.bouquets = bouquets
137 # Read out afterevent
139 "none": AFTEREVENT.NONE,
140 "deepstandby": AFTEREVENT.DEEPSTANDBY,
141 "shutdown": AFTEREVENT.DEEPSTANDBY,
142 "standby": AFTEREVENT.STANDBY,
143 "auto": AFTEREVENT.AUTO
145 afterevent = baseTimer.afterevent
146 for element in element.findall("afterevent"):
149 if idx.has_key(value):
152 print '[AutoTimer] Erroneous config contains invalid value for "afterevent":', afterevent,', ignoring definition'
155 start = element.get("from")
156 end = element.get("to")
158 start = [int(x) for x in start.split(':')]
159 end = [int(x) for x in end.split(':')]
160 afterevent.append((value, (start, end)))
162 afterevent.append((value, None))
163 baseTimer.afterevent = afterevent
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")
171 if not (value and where):
174 if idx.has_key(where):
175 excludes[idx[where]].append(value.encode("UTF-8"))
176 baseTimer.exclude = excludes
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")
183 if not (value and where):
186 if idx.has_key(where):
187 includes[idx[where]].append(value.encode("UTF-8"))
188 baseTimer.include = includes
190 # Read out recording tags (needs my enhanced tag support patch)
191 tags = baseTimer.tags
192 for tag in element.findall("tag"):
197 tags.append(value.encode("UTF-8"))
198 baseTimer.tags = tags
202 def parseConfigOld(configuration, list, uniqueTimerId = 0):
203 print "[AutoTimer] Trying to parse old config"
206 for timer in configuration.findall("timer"):
207 # Increment uniqueTimerId
211 name = timer.get("name")
213 name = name.encode("UTF-8")
214 # Get name (= match) (V1)
217 name = getValue(timer.findall("name"), "").encode("UTF-8")
220 print '[AutoTimer] Erroneous config is missing attribute "name", skipping entry'
223 # Read out match (V3+)
224 match = timer.get("match")
227 match = match.encode("UTF-8")
229 print '[AutoTimer] Erroneous config contains empty attribute "match", skipping entry'
233 # Setting match to name
237 # See if Timer is ensabled (V2+)
238 enabled = timer.get("enabled")
242 elif enabled == "yes":
245 print '[AutoTimer] Erroneous config contains invalid value for "enabled":', enabled,', skipping entry'
249 elements = timer.findall("enabled")
251 if getValue(elements, "yes") == "no":
258 # Read out timespan (V4+; Falling back on missing definition should be OK)
259 start = timer.get("from")
260 end = timer.get("to")
262 start = [int(x) for x in start.split(':')]
263 end = [int(x) for x in end.split(':')]
264 timetuple = (start, end)
267 elements = timer.findall("timespan")
270 # Read out last definition
271 start = elements[Len-1].get("from")
272 end = elements[Len-1].get("to")
274 start = [int(x) for x in start.split(':')]
275 end = [int(x) for x in end.split(':')]
276 timetuple = (start, end)
278 print '[AutoTimer] Erroneous config contains invalid definition of "timespan", ignoring definition'
283 # Read out allowed services (V*)
284 elements = timer.findall("serviceref")
287 for service in elements:
290 # strip all after last :
291 pos = value.rfind(':')
293 value = value[:pos+1]
295 servicelist.append(value)
299 # Read out allowed bouquets (V* though officially supported since V4)
301 for bouquet in timer.findall("bouquet"):
304 bouquets.append(value)
306 # Read out offset (V4+)
307 offset = timer.get("offset")
309 offset = offset.split(",")
311 before = after = int(offset[0] or 0) * 60
313 before = int(offset[0] or 0) * 60
314 after = int(offset[1] or 0) * 60
315 offset = (before, after)
318 elements = timer.findall("offset")
321 value = elements[Len-1].get("both")
323 before = int(elements[Len-1].get("before", 0)) * 60
324 after = int(elements[Len-1].get("after", 0)) * 60
326 before = after = int(value) * 60
327 offset = (before, after)
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))
339 justplay = int(timer.get("justplay", '0'))
341 # Read out avoidDuplicateDescription
342 avoidDuplicateDescription = int(timer.get("avoidDuplicateDescription", 0))
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)
346 "none": AFTEREVENT.NONE,
347 "deepstandby": AFTEREVENT.DEEPSTANDBY,
348 "shutdown": AFTEREVENT.DEEPSTANDBY,
349 "standby": AFTEREVENT.STANDBY,
350 "auto": AFTEREVENT.AUTO
353 for element in timer.findall("afterevent"):
356 if idx.has_key(value):
359 print '[AutoTimer] Erroneous config contains invalid value for "afterevent":', afterevent,', ignoring definition'
362 start = element.get("from")
363 end = element.get("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.findall("exclude"):
375 where = exclude.get("where")
377 if not (value and where):
380 if idx.has_key(where):
381 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.findall("include"):
386 where = include.get("where")
388 if not (value and where):
391 if idx.has_key(where):
392 includes[idx[where]].append(value.encode("UTF-8"))
394 # Read out max length (V4+)
395 maxlen = timer.get("maxduration")
397 maxlen = int(maxlen)*60
400 elements = timer.findall("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.get("destination", "").encode("UTF-8") or None
411 # Read out recording tags (needs my enhanced tag support patch)
413 for tag in timer.findall("tag"):
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.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'])
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'])
501 if defaultTimer.hasAfterEvent():
503 AFTEREVENT.NONE: "none",
504 AFTEREVENT.STANDBY: "standby",
505 AFTEREVENT.DEEPSTANDBY: "shutdown",
506 AFTEREVENT.AUTO: "auto"
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'])
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.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'])
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'])
597 if timer.hasAfterEvent():
599 AFTEREVENT.NONE: "none",
600 AFTEREVENT.STANDBY: "standby",
601 AFTEREVENT.DEEPSTANDBY: "shutdown",
602 AFTEREVENT.AUTO: "auto"
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'])
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'])
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'])
632 for tag in timer.tags:
633 list.extend([' <tag>', stringToXML(tag), '</tag>\n'])
636 list.append(' </timer>\n\n')
638 # End of Configuration
639 list.append('</autotimer>\n')
642 file = open(filename, 'w')
643 file.writelines(list)