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")
79 start = element.get("from")
80 end = element.get("to")
82 start = [int(x) for x in start.split(':')]
83 end = [int(x) for x in end.split(':')]
84 baseTimer.timespan = (start, end)
87 maxduration = element.get("maxduration")
89 baseTimer.maxduration = int(maxduration)*60
91 # Read out recording path
92 baseTimer.destination = element.get("location", "").encode("UTF-8") or None
95 offset = element.get("offset")
97 offset = offset.split(",")
99 before = after = int(offset[0] or 0) * 60
101 before = int(offset[0] or 0) * 60
102 after = int(offset[1] or 0) * 60
103 baseTimer.offset = (before, after)
106 baseTimer.matchCount = int(element.get("counter", 0))
107 baseTimer.matchFormatString = element.get("counterFormat", "")
109 baseTimer.matchLeft = int(element.get("left", baseTimer.matchCount))
110 baseTimer.matchLimit = element.get("lastActivation", "")
111 baseTimer.lastBegin = int(element.get("lastBegin", 0))
114 baseTimer.justplay = int(element.get("justplay", 0))
116 # Read out avoidDuplicateDescription
117 baseTimer.avoidDuplicateDescription = int(element.get("avoidDuplicateDescription", 0))
119 # Read out allowed services
120 servicelist = baseTimer.services
121 for service in element.findall("serviceref"):
124 # strip all after last :
125 pos = value.rfind(':')
127 value = value[:pos+1]
129 servicelist.append(value)
130 baseTimer.services = servicelist
132 # Read out allowed bouquets
133 bouquets = baseTimer.bouquets
134 for bouquet in element.findall("bouquet"):
137 bouquets.append(value)
138 baseTimer.bouquets = bouquets
140 # Read out afterevent
142 "none": AFTEREVENT.NONE,
143 "deepstandby": AFTEREVENT.DEEPSTANDBY,
144 "shutdown": AFTEREVENT.DEEPSTANDBY,
145 "standby": AFTEREVENT.STANDBY,
146 "auto": AFTEREVENT.AUTO
148 afterevent = baseTimer.afterevent
149 for element in element.findall("afterevent"):
152 if idx.has_key(value):
155 print '[AutoTimer] Erroneous config contains invalid value for "afterevent":', afterevent,', ignoring definition'
158 start = element.get("from")
159 end = element.get("to")
161 start = [int(x) for x in start.split(':')]
162 end = [int(x) for x in end.split(':')]
163 afterevent.append((value, (start, end)))
165 afterevent.append((value, None))
166 baseTimer.afterevent = afterevent
169 idx = {"title": 0, "shortdescription": 1, "description": 2, "dayofweek": 3}
170 excludes = (baseTimer.getExcludedTitle(), baseTimer.getExcludedShort(), baseTimer.getExcludedDescription(), baseTimer.getExcludedDays())
171 for exclude in element.findall("exclude"):
172 where = exclude.get("where")
174 if not (value and where):
177 if idx.has_key(where):
178 excludes[idx[where]].append(value.encode("UTF-8"))
179 baseTimer.exclude = excludes
181 # Read out includes (use same idx)
182 includes = (baseTimer.getIncludedTitle(), baseTimer.getIncludedShort(), baseTimer.getIncludedDescription(), baseTimer.getIncludedDays())
183 for include in element.findall("include"):
184 where = include.get("where")
186 if not (value and where):
189 if idx.has_key(where):
190 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.findall("tag"):
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.findall("timer"):
210 # Increment uniqueTimerId
214 name = timer.get("name")
216 name = name.encode("UTF-8")
217 # Get name (= match) (V1)
220 name = getValue(timer.findall("name"), "").encode("UTF-8")
223 print '[AutoTimer] Erroneous config is missing attribute "name", skipping entry'
226 # Read out match (V3+)
227 match = timer.get("match")
230 match = match.encode("UTF-8")
232 print '[AutoTimer] Erroneous config contains empty attribute "match", skipping entry'
236 # Setting match to name
240 # See if Timer is ensabled (V2+)
241 enabled = timer.get("enabled")
245 elif enabled == "yes":
248 print '[AutoTimer] Erroneous config contains invalid value for "enabled":', enabled,', skipping entry'
252 elements = timer.findall("enabled")
254 if getValue(elements, "yes") == "no":
261 # Read out timespan (V4+; Falling back on missing definition should be OK)
262 start = timer.get("from")
263 end = timer.get("to")
265 start = [int(x) for x in start.split(':')]
266 end = [int(x) for x in end.split(':')]
267 timetuple = (start, end)
270 elements = timer.findall("timespan")
273 # Read out last definition
274 start = elements[Len-1].get("from")
275 end = elements[Len-1].get("to")
277 start = [int(x) for x in start.split(':')]
278 end = [int(x) for x in end.split(':')]
279 timetuple = (start, end)
281 print '[AutoTimer] Erroneous config contains invalid definition of "timespan", ignoring definition'
286 # Read out allowed services (V*)
287 elements = timer.findall("serviceref")
290 for service in elements:
293 # strip all after last :
294 pos = value.rfind(':')
296 value = value[:pos+1]
298 servicelist.append(value)
302 # Read out allowed bouquets (V* though officially supported since V4)
304 for bouquet in timer.findall("bouquet"):
307 bouquets.append(value)
309 # Read out offset (V4+)
310 offset = timer.get("offset")
312 offset = offset.split(",")
314 before = after = int(offset[0] or 0) * 60
316 before = int(offset[0] or 0) * 60
317 after = int(offset[1] or 0) * 60
318 offset = (before, after)
321 elements = timer.findall("offset")
324 value = elements[Len-1].get("both")
326 before = int(elements[Len-1].get("before", 0)) * 60
327 after = int(elements[Len-1].get("after", 0)) * 60
329 before = after = int(value) * 60
330 offset = (before, after)
335 counter = int(timer.get("counter", '0'))
336 counterLeft = int(timer.get("left", counter))
337 counterLimit = timer.get("lastActivation")
338 counterFormat = timer.get("counterFormat", "")
339 lastBegin = int(timer.get("lastBegin", 0))
342 justplay = int(timer.get("justplay", '0'))
344 # Read out avoidDuplicateDescription
345 avoidDuplicateDescription = int(timer.get("avoidDuplicateDescription", 0))
347 # Read out afterevent (compatible to V* though behaviour for V3- is different as V4+ allows multiple afterevents while the last definication was chosen before)
349 "none": AFTEREVENT.NONE,
350 "deepstandby": AFTEREVENT.DEEPSTANDBY,
351 "shutdown": AFTEREVENT.DEEPSTANDBY,
352 "standby": AFTEREVENT.STANDBY,
353 "auto": AFTEREVENT.AUTO
356 for element in timer.findall("afterevent"):
359 if idx.has_key(value):
362 print '[AutoTimer] Erroneous config contains invalid value for "afterevent":', afterevent,', ignoring definition'
365 start = element.get("from")
366 end = element.get("to")
368 start = [int(x) for x in start.split(':')]
369 end = [int(x) for x in end.split(':')]
370 afterevent.append((value, (start, end)))
372 afterevent.append((value, None))
374 # Read out exclude (V*)
375 idx = {"title": 0, "shortdescription": 1, "description": 2, "dayofweek": 3}
376 excludes = ([], [], [], [])
377 for exclude in timer.findall("exclude"):
378 where = exclude.get("where")
380 if not (value and where):
383 if idx.has_key(where):
384 excludes[idx[where]].append(value.encode("UTF-8"))
386 # Read out includes (use same idx) (V4+ feature, should not harm V3-)
387 includes = ([], [], [], [])
388 for include in timer.findall("include"):
389 where = include.get("where")
391 if not (value and where):
394 if idx.has_key(where):
395 includes[idx[where]].append(value.encode("UTF-8"))
397 # Read out max length (V4+)
398 maxlen = timer.get("maxduration")
400 maxlen = int(maxlen)*60
403 elements = timer.findall("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.get("destination", "").encode("UTF-8") or None
414 # Read out recording tags (needs my enhanced tag support patch)
416 for tag in timer.findall("tag"):
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 # Only display encoding if != utf-8
489 if defaultTimer.encoding != 'UTF-8':
490 list.extend([' encoding="', str(defaultTimer.encoding), '"'])
492 # Close still opened defaults tag
496 for serviceref in defaultTimer.services:
497 list.extend([' <serviceref>', serviceref, '</serviceref>'])
498 ref = ServiceReference(str(serviceref))
499 list.extend([' <!-- ', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), ' -->\n'])
502 for bouquet in defaultTimer.bouquets:
503 list.extend([' <bouquet>', str(bouquet), '</bouquet>'])
504 ref = ServiceReference(str(bouquet))
505 list.extend([' <!-- ', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), ' -->\n'])
508 if defaultTimer.hasAfterEvent():
510 AFTEREVENT.NONE: "none",
511 AFTEREVENT.STANDBY: "standby",
512 AFTEREVENT.DEEPSTANDBY: "shutdown",
513 AFTEREVENT.AUTO: "auto"
515 for afterevent in defaultTimer.afterevent:
516 action, timespan = afterevent
517 list.append(' <afterevent')
518 if timespan[0] is not None:
519 list.append(' from="%02d:%02d" to="%02d:%02d"' % (timespan[0][0], timespan[0][1], timespan[1][0], timespan[1][1]))
520 list.extend(['>', idx[action], '</afterevent>\n'])
523 for title in defaultTimer.getExcludedTitle():
524 list.extend([' <exclude where="title">', stringToXML(title), '</exclude>\n'])
525 for short in defaultTimer.getExcludedShort():
526 list.extend([' <exclude where="shortdescription">', stringToXML(short), '</exclude>\n'])
527 for desc in defaultTimer.getExcludedDescription():
528 list.extend([' <exclude where="description">', stringToXML(desc), '</exclude>\n'])
529 for day in defaultTimer.getExcludedDays():
530 list.extend([' <exclude where="dayofweek">', stringToXML(day), '</exclude>\n'])
533 for title in defaultTimer.getIncludedTitle():
534 list.extend([' <include where="title">', stringToXML(title), '</include>\n'])
535 for short in defaultTimer.getIncludedShort():
536 list.extend([' <include where="shortdescription">', stringToXML(short), '</include>\n'])
537 for desc in defaultTimer.getIncludedDescription():
538 list.extend([' <include where="description">', stringToXML(desc), '</include>\n'])
539 for day in defaultTimer.getIncludedDays():
540 list.extend([' <include where="dayofweek">', stringToXML(day), '</include>\n'])
543 for tag in defaultTimer.tags:
544 list.extend([' <tag>', stringToXML(tag), '</tag>\n'])
547 list.append(' </defaults>\n\n')
551 # Common attributes (match, enabled)
552 list.extend([' <timer name="', stringToXML(timer.name), '" match="', stringToXML(timer.match), '" enabled="', timer.getEnabled(), '"'])
555 if timer.hasTimespan():
556 list.extend([' from="', timer.getTimespanBegin(), '" to="', timer.getTimespanEnd(), '"'])
559 if timer.hasDuration():
560 list.extend([' maxduration="', str(timer.getDuration()), '"'])
563 if timer.hasDestination():
564 list.extend([' location="', stringToXML(timer.destination), '"'])
567 if timer.hasOffset():
568 if timer.isOffsetEqual():
569 list.extend([' offset="', str(timer.getOffsetBegin()), '"'])
571 list.extend([' offset="', str(timer.getOffsetBegin()), ',', str(timer.getOffsetEnd()), '"'])
574 if timer.hasCounter():
575 list.extend([' lastBegin="', str(timer.getLastBegin()), '" counter="', str(timer.getCounter()), '" left="', str(timer.getCounterLeft()) ,'"'])
576 if timer.hasCounterFormatString():
577 list.extend([' lastActivation="', str(timer.getCounterLimit()), '"'])
578 list.extend([' counterFormat="', str(timer.getCounterFormatString()), '"'])
580 # Duplicate Description
581 if timer.getAvoidDuplicateDescription():
582 list.extend([' avoidDuplicateDescription="', str(timer.getAvoidDuplicateDescription()), '"'])
584 # Only display justplay if true
586 list.extend([' justplay="', str(timer.getJustplay()), '"'])
588 # Only display encoding if != utf-8
589 if timer.encoding != 'UTF-8':
590 list.extend([' encoding="', str(timer.encoding), '"'])
592 # Close still opened timer tag
596 for serviceref in timer.services:
597 list.extend([' <serviceref>', serviceref, '</serviceref>'])
598 ref = ServiceReference(str(serviceref))
599 list.extend([' <!-- ', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), ' -->\n'])
602 for bouquet in timer.bouquets:
603 list.extend([' <bouquet>', str(bouquet), '</bouquet>'])
604 ref = ServiceReference(str(bouquet))
605 list.extend([' <!-- ', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), ' -->\n'])
608 if timer.hasAfterEvent():
610 AFTEREVENT.NONE: "none",
611 AFTEREVENT.STANDBY: "standby",
612 AFTEREVENT.DEEPSTANDBY: "shutdown",
613 AFTEREVENT.AUTO: "auto"
615 for afterevent in timer.afterevent:
616 action, timespan = afterevent
617 list.append(' <afterevent')
618 if timespan[0] is not None:
619 list.append(' from="%02d:%02d" to="%02d:%02d"' % (timespan[0][0], timespan[0][1], timespan[1][0], timespan[1][1]))
620 list.extend(['>', idx[action], '</afterevent>\n'])
623 for title in timer.getExcludedTitle():
624 list.extend([' <exclude where="title">', stringToXML(title), '</exclude>\n'])
625 for short in timer.getExcludedShort():
626 list.extend([' <exclude where="shortdescription">', stringToXML(short), '</exclude>\n'])
627 for desc in timer.getExcludedDescription():
628 list.extend([' <exclude where="description">', stringToXML(desc), '</exclude>\n'])
629 for day in timer.getExcludedDays():
630 list.extend([' <exclude where="dayofweek">', stringToXML(day), '</exclude>\n'])
633 for title in timer.getIncludedTitle():
634 list.extend([' <include where="title">', stringToXML(title), '</include>\n'])
635 for short in timer.getIncludedShort():
636 list.extend([' <include where="shortdescription">', stringToXML(short), '</include>\n'])
637 for desc in timer.getIncludedDescription():
638 list.extend([' <include where="description">', stringToXML(desc), '</include>\n'])
639 for day in timer.getIncludedDays():
640 list.extend([' <include where="dayofweek">', stringToXML(day), '</include>\n'])
643 for tag in timer.tags:
644 list.extend([' <tag>', stringToXML(tag), '</tag>\n'])
647 list.append(' </timer>\n\n')
649 # End of Configuration
650 list.append('</autotimer>\n')
653 file = open(filename, 'w')
654 file.writelines(list)