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
75 # Read out encoding (won't change if no value is set)
76 baseTimer.encoding = element.get("encoding")
78 # Read out search type/case
79 baseTimer.searchType = element.get("searchType", baseTimer.searchType)
80 baseTimer.searchCase = element.get("searchCase", baseTimer.searchCase)
83 start = element.get("from")
84 end = element.get("to")
86 start = [int(x) for x in start.split(':')]
87 end = [int(x) for x in end.split(':')]
88 baseTimer.timespan = (start, end)
91 maxduration = element.get("maxduration")
93 baseTimer.maxduration = int(maxduration)*60
95 # Read out recording path
96 baseTimer.destination = element.get("location", "").encode("UTF-8") or None
99 offset = element.get("offset")
101 offset = offset.split(",")
103 before = after = int(offset[0] or 0) * 60
105 before = int(offset[0] or 0) * 60
106 after = int(offset[1] or 0) * 60
107 baseTimer.offset = (before, after)
110 baseTimer.matchCount = int(element.get("counter", 0))
111 baseTimer.matchFormatString = element.get("counterFormat", "")
113 baseTimer.matchLeft = int(element.get("left", baseTimer.matchCount))
114 baseTimer.matchLimit = element.get("lastActivation", "")
115 baseTimer.lastBegin = int(element.get("lastBegin", 0))
118 baseTimer.justplay = int(element.get("justplay", 0))
120 # Read out avoidDuplicateDescription
121 baseTimer.avoidDuplicateDescription = int(element.get("avoidDuplicateDescription", 0))
123 # Read out allowed services
124 l = element.findall("serviceref")
131 # strip all after last :
132 pos = value.rfind(':')
134 value = value[:pos+1]
136 servicelist.append(value)
137 baseTimer.services = servicelist
139 # Read out allowed bouquets
140 l = element.findall("bouquet")
146 bouquets.append(value)
147 baseTimer.bouquets = bouquets
149 # Read out afterevent
150 l = element.findall("afterevent")
153 "none": AFTEREVENT.NONE,
154 "deepstandby": AFTEREVENT.DEEPSTANDBY,
155 "shutdown": AFTEREVENT.DEEPSTANDBY,
156 "standby": AFTEREVENT.STANDBY,
157 "auto": AFTEREVENT.AUTO
161 value = afterevent.text
163 if idx.has_key(value):
166 print '[AutoTimer] Erroneous config contains invalid value for "afterevent":', afterevent,', ignoring definition'
169 start = afterevent.get("from")
170 end = afterevent.get("to")
172 start = [int(x) for x in start.split(':')]
173 end = [int(x) for x in end.split(':')]
174 afterevents.append((value, (start, end)))
176 afterevents.append((value, None))
177 baseTimer.afterevent = afterevents
180 l = element.findall("exclude")
181 idx = {"title": 0, "shortdescription": 1, "description": 2, "dayofweek": 3}
183 excludes = ([], [], [], [])
185 where = exclude.get("where")
187 if not (value and where):
190 if idx.has_key(where):
191 excludes[idx[where]].append(value.encode("UTF-8"))
192 baseTimer.exclude = excludes
194 # Read out includes (use same idx)
195 l = element.findall("include")
197 includes = ([], [], [], [])
199 where = include.get("where")
201 if not (value and where):
204 if idx.has_key(where):
205 includes[idx[where]].append(value.encode("UTF-8"))
206 baseTimer.include = includes
208 # Read out recording tags
209 l = element.findall("tag")
217 tags.append(value.encode("UTF-8"))
218 baseTimer.tags = tags
222 def parseConfigOld(configuration, list, uniqueTimerId = 0):
223 print "[AutoTimer] Trying to parse old config"
226 for timer in configuration.findall("timer"):
227 # Increment uniqueTimerId
231 name = timer.get("name")
233 name = name.encode("UTF-8")
234 # Get name (= match) (V1)
237 name = getValue(timer.findall("name"), "").encode("UTF-8")
240 print '[AutoTimer] Erroneous config is missing attribute "name", skipping entry'
243 # Read out match (V3+)
244 match = timer.get("match")
247 match = match.encode("UTF-8")
249 print '[AutoTimer] Erroneous config contains empty attribute "match", skipping entry'
253 # Setting match to name
257 # See if Timer is ensabled (V2+)
258 enabled = timer.get("enabled")
262 elif enabled == "yes":
265 print '[AutoTimer] Erroneous config contains invalid value for "enabled":', enabled,', skipping entry'
269 elements = timer.findall("enabled")
271 if getValue(elements, "yes") == "no":
278 # Read out timespan (V4+; Falling back on missing definition should be OK)
279 start = timer.get("from")
280 end = timer.get("to")
282 start = [int(x) for x in start.split(':')]
283 end = [int(x) for x in end.split(':')]
284 timetuple = (start, end)
287 elements = timer.findall("timespan")
290 # Read out last definition
291 start = elements[Len-1].get("from")
292 end = elements[Len-1].get("to")
294 start = [int(x) for x in start.split(':')]
295 end = [int(x) for x in end.split(':')]
296 timetuple = (start, end)
298 print '[AutoTimer] Erroneous config contains invalid definition of "timespan", ignoring definition'
303 # Read out allowed services (V*)
304 elements = timer.findall("serviceref")
307 for service in elements:
310 # strip all after last :
311 pos = value.rfind(':')
313 value = value[:pos+1]
315 servicelist.append(value)
319 # Read out allowed bouquets (V* though officially supported since V4)
321 for bouquet in timer.findall("bouquet"):
324 bouquets.append(value)
326 # Read out offset (V4+)
327 offset = timer.get("offset")
329 offset = offset.split(",")
331 before = after = int(offset[0] or 0) * 60
333 before = int(offset[0] or 0) * 60
334 after = int(offset[1] or 0) * 60
335 offset = (before, after)
338 elements = timer.findall("offset")
341 value = elements[Len-1].get("both")
343 before = int(elements[Len-1].get("before", 0)) * 60
344 after = int(elements[Len-1].get("after", 0)) * 60
346 before = after = int(value) * 60
347 offset = (before, after)
352 counter = int(timer.get("counter", '0'))
353 counterLeft = int(timer.get("left", counter))
354 counterLimit = timer.get("lastActivation")
355 counterFormat = timer.get("counterFormat", "")
356 lastBegin = int(timer.get("lastBegin", 0))
359 justplay = int(timer.get("justplay", '0'))
361 # Read out avoidDuplicateDescription
362 avoidDuplicateDescription = int(timer.get("avoidDuplicateDescription", 0))
364 # Read out afterevent (compatible to V* though behaviour for V3- is different as V4+ allows multiple afterevents while the last definication was chosen before)
366 "none": AFTEREVENT.NONE,
367 "deepstandby": AFTEREVENT.DEEPSTANDBY,
368 "shutdown": AFTEREVENT.DEEPSTANDBY,
369 "standby": AFTEREVENT.STANDBY,
370 "auto": AFTEREVENT.AUTO
373 for element in timer.findall("afterevent"):
376 if idx.has_key(value):
379 print '[AutoTimer] Erroneous config contains invalid value for "afterevent":', afterevent,', ignoring definition'
382 start = element.get("from")
383 end = element.get("to")
385 start = [int(x) for x in start.split(':')]
386 end = [int(x) for x in end.split(':')]
387 afterevent.append((value, (start, end)))
389 afterevent.append((value, None))
391 # Read out exclude (V*)
392 idx = {"title": 0, "shortdescription": 1, "description": 2, "dayofweek": 3}
393 excludes = ([], [], [], [])
394 for exclude in timer.findall("exclude"):
395 where = exclude.get("where")
397 if not (value and where):
400 if idx.has_key(where):
401 excludes[idx[where]].append(value.encode("UTF-8"))
403 # Read out includes (use same idx) (V4+ feature, should not harm V3-)
404 includes = ([], [], [], [])
405 for include in timer.findall("include"):
406 where = include.get("where")
408 if not (value and where):
411 if idx.has_key(where):
412 includes[idx[where]].append(value.encode("UTF-8"))
414 # Read out max length (V4+)
415 maxlen = timer.get("maxduration")
417 maxlen = int(maxlen)*60
420 elements = timer.findall("maxduration")
422 maxlen = getValue(elements, None)
423 if maxlen is not None:
424 maxlen = int(maxlen)*60
428 # Read out recording path
429 destination = timer.get("destination", "").encode("UTF-8") or None
431 # Read out recording tags
433 for tag in timer.findall("tag"):
438 tags.append(value.encode("UTF-8"))
440 # Finally append timer
441 list.append(AutoTimerComponent(
446 timespan = timetuple,
447 services = servicelist,
449 afterevent = afterevent,
452 maxduration = maxlen,
453 destination = destination,
454 matchCount = counter,
455 matchLeft = counterLeft,
456 matchLimit = counterLimit,
457 matchFormatString = counterFormat,
458 lastBegin = lastBegin,
460 avoidDuplicateDescription = avoidDuplicateDescription,
465 def writeConfig(filename, defaultTimer, timers):
466 # Generate List in RAM
467 list = ['<?xml version="1.0" ?>\n<autotimer version="', CURRENT_CONFIG_VERSION, '">\n\n']
469 # This gets deleted afterwards if we do not have set any defaults
470 list.append(' <defaults')
473 if defaultTimer.hasTimespan():
474 list.extend([' from="', defaultTimer.getTimespanBegin(), '" to="', defaultTimer.getTimespanEnd(), '"'])
477 if defaultTimer.hasDuration():
478 list.extend([' maxduration="', str(defaultTimer.getDuration()), '"'])
481 if defaultTimer.hasDestination():
482 list.extend([' location="', stringToXML(defaultTimer.destination), '"'])
485 if defaultTimer.hasOffset():
486 if defaultTimer.isOffsetEqual():
487 list.extend([' offset="', str(defaultTimer.getOffsetBegin()), '"'])
489 list.extend([' offset="', str(defaultTimer.getOffsetBegin()), ',', str(defaultTimer.getOffsetEnd()), '"'])
492 if defaultTimer.hasCounter():
493 list.extend([' counter="', str(defaultTimer.getCounter()), '"'])
494 if defaultTimer.hasCounterFormatString():
495 list.extend([' counterFormat="', str(defaultTimer.getCounterFormatString()), '"'])
497 # Duplicate Description
498 if defaultTimer.getAvoidDuplicateDescription():
499 list.append(' avoidDuplicateDescription="1" ')
501 # Only display justplay if true
502 if defaultTimer.justplay:
503 list.extend([' justplay="', str(defaultTimer.getJustplay()), '"'])
505 # Only display encoding if != utf-8
506 if defaultTimer.encoding != 'UTF-8':
507 list.extend([' encoding="', str(defaultTimer.encoding), '"'])
509 # Only display searchType if exact
510 if defaultTimer.searchType == "exact":
511 list.extend([' searchType="', str(defaultTimer.searchType), '"'])
513 # Only display searchCase if sensitive
514 if defaultTimer.searchCase == "sensitive":
515 list.extend([' searchCase="', str(defaultTimer.searchCase), '"'])
517 # Close still opened defaults tag
521 for serviceref in defaultTimer.services:
522 list.extend([' <serviceref>', serviceref, '</serviceref>'])
523 ref = ServiceReference(str(serviceref))
524 list.extend([' <!-- ', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), ' -->\n'])
527 for bouquet in defaultTimer.bouquets:
528 list.extend([' <bouquet>', str(bouquet), '</bouquet>'])
529 ref = ServiceReference(str(bouquet))
530 list.extend([' <!-- ', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), ' -->\n'])
533 if defaultTimer.hasAfterEvent():
535 AFTEREVENT.NONE: "none",
536 AFTEREVENT.STANDBY: "standby",
537 AFTEREVENT.DEEPSTANDBY: "shutdown",
538 AFTEREVENT.AUTO: "auto"
540 for afterevent in defaultTimer.afterevent:
541 action, timespan = afterevent
542 list.append(' <afterevent')
543 if timespan[0] is not None:
544 list.append(' from="%02d:%02d" to="%02d:%02d"' % (timespan[0][0], timespan[0][1], timespan[1][0], timespan[1][1]))
545 list.extend(['>', idx[action], '</afterevent>\n'])
548 for title in defaultTimer.getExcludedTitle():
549 list.extend([' <exclude where="title">', stringToXML(title), '</exclude>\n'])
550 for short in defaultTimer.getExcludedShort():
551 list.extend([' <exclude where="shortdescription">', stringToXML(short), '</exclude>\n'])
552 for desc in defaultTimer.getExcludedDescription():
553 list.extend([' <exclude where="description">', stringToXML(desc), '</exclude>\n'])
554 for day in defaultTimer.getExcludedDays():
555 list.extend([' <exclude where="dayofweek">', stringToXML(day), '</exclude>\n'])
558 for title in defaultTimer.getIncludedTitle():
559 list.extend([' <include where="title">', stringToXML(title), '</include>\n'])
560 for short in defaultTimer.getIncludedShort():
561 list.extend([' <include where="shortdescription">', stringToXML(short), '</include>\n'])
562 for desc in defaultTimer.getIncludedDescription():
563 list.extend([' <include where="description">', stringToXML(desc), '</include>\n'])
564 for day in defaultTimer.getIncludedDays():
565 list.extend([' <include where="dayofweek">', stringToXML(day), '</include>\n'])
568 for tag in defaultTimer.tags:
569 list.extend([' <tag>', stringToXML(tag), '</tag>\n'])
571 # Keep the list clean
574 list.pop() # <defaults
576 list.append(' </defaults>\n\n')
580 # Common attributes (match, enabled)
581 list.extend([' <timer name="', stringToXML(timer.name), '" match="', stringToXML(timer.match), '" enabled="', timer.getEnabled(), '"'])
584 if timer.hasTimespan():
585 list.extend([' from="', timer.getTimespanBegin(), '" to="', timer.getTimespanEnd(), '"'])
588 if timer.hasDuration():
589 list.extend([' maxduration="', str(timer.getDuration()), '"'])
592 if timer.hasDestination():
593 list.extend([' location="', stringToXML(timer.destination), '"'])
596 if timer.hasOffset():
597 if timer.isOffsetEqual():
598 list.extend([' offset="', str(timer.getOffsetBegin()), '"'])
600 list.extend([' offset="', str(timer.getOffsetBegin()), ',', str(timer.getOffsetEnd()), '"'])
603 if timer.hasCounter():
604 list.extend([' lastBegin="', str(timer.getLastBegin()), '" counter="', str(timer.getCounter()), '" left="', str(timer.getCounterLeft()) ,'"'])
605 if timer.hasCounterFormatString():
606 list.extend([' lastActivation="', str(timer.getCounterLimit()), '"'])
607 list.extend([' counterFormat="', str(timer.getCounterFormatString()), '"'])
609 # Duplicate Description
610 if timer.getAvoidDuplicateDescription():
611 list.extend([' avoidDuplicateDescription="', str(timer.getAvoidDuplicateDescription()), '"'])
613 # Only display justplay if true
615 list.extend([' justplay="', str(timer.getJustplay()), '"'])
617 # Only display encoding if != utf-8
618 if timer.encoding != 'UTF-8':
619 list.extend([' encoding="', str(timer.encoding), '"'])
621 # Only display searchType if exact
622 if timer.searchType == "exact":
623 list.extend([' searchType="', str(timer.searchType), '"'])
625 # Only display searchCase if sensitive
626 if timer.searchCase == "sensitive":
627 list.extend([' searchCase="', str(timer.searchCase), '"'])
629 # Close still opened timer tag
633 for serviceref in timer.services:
634 list.extend([' <serviceref>', serviceref, '</serviceref>'])
635 ref = ServiceReference(str(serviceref))
636 list.extend([' <!-- ', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), ' -->\n'])
639 for bouquet in timer.bouquets:
640 list.extend([' <bouquet>', str(bouquet), '</bouquet>'])
641 ref = ServiceReference(str(bouquet))
642 list.extend([' <!-- ', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), ' -->\n'])
645 if timer.hasAfterEvent():
647 AFTEREVENT.NONE: "none",
648 AFTEREVENT.STANDBY: "standby",
649 AFTEREVENT.DEEPSTANDBY: "shutdown",
650 AFTEREVENT.AUTO: "auto"
652 for afterevent in timer.afterevent:
653 action, timespan = afterevent
654 list.append(' <afterevent')
655 if timespan[0] is not None:
656 list.append(' from="%02d:%02d" to="%02d:%02d"' % (timespan[0][0], timespan[0][1], timespan[1][0], timespan[1][1]))
657 list.extend(['>', idx[action], '</afterevent>\n'])
660 for title in timer.getExcludedTitle():
661 list.extend([' <exclude where="title">', stringToXML(title), '</exclude>\n'])
662 for short in timer.getExcludedShort():
663 list.extend([' <exclude where="shortdescription">', stringToXML(short), '</exclude>\n'])
664 for desc in timer.getExcludedDescription():
665 list.extend([' <exclude where="description">', stringToXML(desc), '</exclude>\n'])
666 for day in timer.getExcludedDays():
667 list.extend([' <exclude where="dayofweek">', stringToXML(day), '</exclude>\n'])
670 for title in timer.getIncludedTitle():
671 list.extend([' <include where="title">', stringToXML(title), '</include>\n'])
672 for short in timer.getIncludedShort():
673 list.extend([' <include where="shortdescription">', stringToXML(short), '</include>\n'])
674 for desc in timer.getIncludedDescription():
675 list.extend([' <include where="description">', stringToXML(desc), '</include>\n'])
676 for day in timer.getIncludedDays():
677 list.extend([' <include where="dayofweek">', stringToXML(day), '</include>\n'])
680 for tag in timer.tags:
681 list.extend([' <tag>', stringToXML(tag), '</tag>\n'])
684 list.append(' </timer>\n\n')
686 # End of Configuration
687 list.append('</autotimer>\n')
690 file = open(filename, 'w')
691 file.writelines(list)