1 # -*- coding: UTF-8 -*-
2 # for localized messages
5 from AutoTimerComponent import preferredAutoTimerComponent
6 from RecordTimer import AFTEREVENT
7 from Tools.XMLTools import stringToXML
8 from ServiceReference import ServiceReference
10 from enigma import eServiceReference
12 CURRENT_CONFIG_VERSION = "5"
14 def getValue(definitions, default):
18 # How many definitions are present
19 if isinstance(definitions, list):
20 Len = len(definitions)
22 childNodes = definitions[Len-1].text
26 ret = definitions.text
28 # Return stripped output or (if empty) default
29 return ret.strip() or default
31 def parseConfig(configuration, list, version = None, uniqueTimerId = 0, defaultTimer = None):
32 if version != CURRENT_CONFIG_VERSION:
33 parseConfigOld(configuration, list, uniqueTimerId)
36 if defaultTimer is not None:
37 # Read in defaults for a new timer
38 for defaults in configuration.findall("defaults"):
39 parseEntry(defaults, defaultTimer, True)
41 for timer in configuration.findall("timer"):
43 baseTimer = preferredAutoTimerComponent(
50 if parseEntry(timer, baseTimer):
51 list.append(baseTimer)
53 def parseEntry(element, baseTimer, defaults = False):
56 baseTimer.match = element.get("match", "").encode("UTF-8")
57 if not baseTimer.match:
58 print '[AutoTimer] Erroneous config is missing attribute "match", skipping entry'
62 baseTimer.name = element.get("name", "").encode("UTF-8")
63 if not baseTimer.name:
64 print '[AutoTimer] Timer is missing attribute "name", defaulting to match'
65 baseTimer.name = baseTimer.match
68 enabled = element.get("enabled", "yes")
70 baseTimer.enabled = False
71 elif enabled == "yes":
72 baseTimer.enabled = True
74 print '[AutoTimer] Erroneous config contains invalid value for "enabled":', enabled,', disabling'
75 baseTimer.enabled = False
77 # Read out encoding (won't change if no value is set)
78 baseTimer.encoding = element.get("encoding")
80 # Read out search type/case
81 baseTimer.searchType = element.get("searchType", baseTimer.searchType)
82 baseTimer.searchCase = element.get("searchCase", baseTimer.searchCase)
84 # Read out if we should change to alternative services
85 baseTimer.overrideAlternatives = int(element.get("overrideAlternatives", baseTimer.overrideAlternatives))
88 start = element.get("from")
89 end = element.get("to")
91 start = [int(x) for x in start.split(':')]
92 end = [int(x) for x in end.split(':')]
93 baseTimer.timespan = (start, end)
96 maxduration = element.get("maxduration")
98 baseTimer.maxduration = int(maxduration)*60
100 # Read out recording path
101 default = baseTimer.destination or ""
102 baseTimer.destination = element.get("location", default).encode("UTF-8") or None
105 offset = element.get("offset")
107 offset = offset.split(",")
109 before = after = int(offset[0] or 0) * 60
111 before = int(offset[0] or 0) * 60
112 after = int(offset[1] or 0) * 60
113 baseTimer.offset = (before, after)
116 baseTimer.matchCount = int(element.get("counter", 0))
117 baseTimer.matchFormatString = element.get("counterFormat", "")
119 baseTimer.matchLeft = int(element.get("left", baseTimer.matchCount))
120 baseTimer.matchLimit = element.get("lastActivation", "")
121 baseTimer.lastBegin = int(element.get("lastBegin", 0))
124 baseTimer.justplay = int(element.get("justplay", 0))
126 # Read out avoidDuplicateDescription
127 baseTimer.avoidDuplicateDescription = int(element.get("avoidDuplicateDescription", 0))
129 # Read out allowed services
130 l = element.findall("serviceref")
137 myref = eServiceReference(str(value))
138 if not (myref.flags & eServiceReference.isGroup):
139 # strip all after last :
140 pos = value.rfind(':')
142 if value[pos-1] == ':':
144 value = value[:pos+1]
146 servicelist.append(value)
147 baseTimer.services = servicelist
149 # Read out allowed bouquets
150 l = element.findall("bouquet")
156 bouquets.append(value)
157 baseTimer.bouquets = bouquets
159 # Read out afterevent
160 l = element.findall("afterevent")
163 "none": AFTEREVENT.NONE,
164 "deepstandby": AFTEREVENT.DEEPSTANDBY,
165 "shutdown": AFTEREVENT.DEEPSTANDBY,
166 "standby": AFTEREVENT.STANDBY,
167 "auto": AFTEREVENT.AUTO
171 value = afterevent.text
173 if idx.has_key(value):
176 print '[AutoTimer] Erroneous config contains invalid value for "afterevent":', afterevent,', ignoring definition'
179 start = afterevent.get("from")
180 end = afterevent.get("to")
182 start = [int(x) for x in start.split(':')]
183 end = [int(x) for x in end.split(':')]
184 afterevents.append((value, (start, end)))
186 afterevents.append((value, None))
187 baseTimer.afterevent = afterevents
190 l = element.findall("exclude")
191 idx = {"title": 0, "shortdescription": 1, "description": 2, "dayofweek": 3}
193 excludes = ([], [], [], [])
195 where = exclude.get("where")
197 if not (value and where):
200 if idx.has_key(where):
201 excludes[idx[where]].append(value.encode("UTF-8"))
202 baseTimer.exclude = excludes
204 # Read out includes (use same idx)
205 l = element.findall("include")
207 includes = ([], [], [], [])
209 where = include.get("where")
211 if not (value and where):
214 if idx.has_key(where):
215 includes[idx[where]].append(value.encode("UTF-8"))
216 baseTimer.include = includes
218 # Read out recording tags
219 l = element.findall("tag")
227 tags.append(value.encode("UTF-8"))
228 baseTimer.tags = tags
232 def parseConfigOld(configuration, list, uniqueTimerId = 0):
233 print "[AutoTimer] Trying to parse old config"
236 for timer in configuration.findall("timer"):
237 # Increment uniqueTimerId
241 name = timer.get("name")
243 name = name.encode("UTF-8")
244 # Get name (= match) (V1)
247 name = getValue(timer.findall("name"), "").encode("UTF-8")
250 print '[AutoTimer] Erroneous config is missing attribute "name", skipping entry'
253 # Read out match (V3+)
254 match = timer.get("match")
257 match = match.encode("UTF-8")
259 print '[AutoTimer] Erroneous config contains empty attribute "match", skipping entry'
263 # Setting match to name
267 # See if Timer is ensabled (V2+)
268 enabled = timer.get("enabled")
272 elif enabled == "yes":
275 print '[AutoTimer] Erroneous config contains invalid value for "enabled":', enabled,', skipping entry'
279 elements = timer.findall("enabled")
281 if getValue(elements, "yes") == "no":
288 # Read out timespan (V4+; Falling back on missing definition should be OK)
289 start = timer.get("from")
290 end = timer.get("to")
292 start = [int(x) for x in start.split(':')]
293 end = [int(x) for x in end.split(':')]
294 timetuple = (start, end)
297 elements = timer.findall("timespan")
300 # Read out last definition
301 start = elements[Len-1].get("from")
302 end = elements[Len-1].get("to")
304 start = [int(x) for x in start.split(':')]
305 end = [int(x) for x in end.split(':')]
306 timetuple = (start, end)
308 print '[AutoTimer] Erroneous config contains invalid definition of "timespan", ignoring definition'
313 # Read out allowed services (V*)
314 elements = timer.findall("serviceref")
317 for service in elements:
320 myref = eServiceReference(str(value))
321 if not (myref.flags & eServiceReference.isGroup):
322 # strip all after last :
323 pos = value.rfind(':')
325 if value[pos-1] == ':':
327 value = value[:pos+1]
329 servicelist.append(value)
333 # Read out allowed bouquets (V* though officially supported since V4)
335 for bouquet in timer.findall("bouquet"):
338 bouquets.append(value)
340 # Read out offset (V4+)
341 offset = timer.get("offset")
343 offset = offset.split(",")
345 before = after = int(offset[0] or 0) * 60
347 before = int(offset[0] or 0) * 60
348 after = int(offset[1] or 0) * 60
349 offset = (before, after)
352 elements = timer.findall("offset")
355 value = elements[Len-1].get("both")
357 before = int(elements[Len-1].get("before", 0)) * 60
358 after = int(elements[Len-1].get("after", 0)) * 60
360 before = after = int(value) * 60
361 offset = (before, after)
366 counter = int(timer.get("counter", '0'))
367 counterLeft = int(timer.get("left", counter))
368 counterLimit = timer.get("lastActivation")
369 counterFormat = timer.get("counterFormat", "")
370 lastBegin = int(timer.get("lastBegin", 0))
373 justplay = int(timer.get("justplay", '0'))
375 # Read out avoidDuplicateDescription
376 avoidDuplicateDescription = int(timer.get("avoidDuplicateDescription", 0))
378 # Read out afterevent (compatible to V* though behaviour for V3- is different as V4+ allows multiple afterevents while the last definication was chosen before)
380 "none": AFTEREVENT.NONE,
381 "deepstandby": AFTEREVENT.DEEPSTANDBY,
382 "shutdown": AFTEREVENT.DEEPSTANDBY,
383 "standby": AFTEREVENT.STANDBY,
384 "auto": AFTEREVENT.AUTO
387 for element in timer.findall("afterevent"):
390 if idx.has_key(value):
393 print '[AutoTimer] Erroneous config contains invalid value for "afterevent":', afterevent,', ignoring definition'
396 start = element.get("from")
397 end = element.get("to")
399 start = [int(x) for x in start.split(':')]
400 end = [int(x) for x in end.split(':')]
401 afterevent.append((value, (start, end)))
403 afterevent.append((value, None))
405 # Read out exclude (V*)
406 idx = {"title": 0, "shortdescription": 1, "description": 2, "dayofweek": 3}
407 excludes = ([], [], [], [])
408 for exclude in timer.findall("exclude"):
409 where = exclude.get("where")
411 if not (value and where):
414 if idx.has_key(where):
415 excludes[idx[where]].append(value.encode("UTF-8"))
417 # Read out includes (use same idx) (V4+ feature, should not harm V3-)
418 includes = ([], [], [], [])
419 for include in timer.findall("include"):
420 where = include.get("where")
422 if not (value and where):
425 if idx.has_key(where):
426 includes[idx[where]].append(value.encode("UTF-8"))
428 # Read out max length (V4+)
429 maxlen = timer.get("maxduration")
431 maxlen = int(maxlen)*60
434 elements = timer.findall("maxduration")
436 maxlen = getValue(elements, None)
437 if maxlen is not None:
438 maxlen = int(maxlen)*60
442 # Read out recording path
443 destination = timer.get("destination", "").encode("UTF-8") or None
445 # Read out recording tags
447 for tag in timer.findall("tag"):
452 tags.append(value.encode("UTF-8"))
454 # Finally append timer
455 list.append(preferredAutoTimerComponent(
460 timespan = timetuple,
461 services = servicelist,
463 afterevent = afterevent,
466 maxduration = maxlen,
467 destination = destination,
468 matchCount = counter,
469 matchLeft = counterLeft,
470 matchLimit = counterLimit,
471 matchFormatString = counterFormat,
472 lastBegin = lastBegin,
474 avoidDuplicateDescription = avoidDuplicateDescription,
479 def buildConfig(defaultTimer, timers, webif = False):
480 # Generate List in RAM
481 list = ['<?xml version="1.0" ?>\n<autotimer version="', CURRENT_CONFIG_VERSION, '">\n\n']
483 # This gets deleted afterwards if we do not have set any defaults
484 list.append(' <defaults')
487 if defaultTimer.hasTimespan():
488 list.extend((' from="', defaultTimer.getTimespanBegin(), '" to="', defaultTimer.getTimespanEnd(), '"'))
491 if defaultTimer.hasDuration():
492 list.extend((' maxduration="', str(defaultTimer.getDuration()), '"'))
495 if defaultTimer.hasDestination():
496 list.extend((' location="', stringToXML(defaultTimer.destination), '"'))
499 if defaultTimer.hasOffset():
500 if defaultTimer.isOffsetEqual():
501 list.extend((' offset="', str(defaultTimer.getOffsetBegin()), '"'))
503 list.extend((' offset="', str(defaultTimer.getOffsetBegin()), ',', str(defaultTimer.getOffsetEnd()), '"'))
506 if defaultTimer.hasCounter():
507 list.extend((' counter="', str(defaultTimer.getCounter()), '"'))
508 if defaultTimer.hasCounterFormatString():
509 list.extend((' counterFormat="', str(defaultTimer.getCounterFormatString()), '"'))
511 # Duplicate Description
512 if defaultTimer.getAvoidDuplicateDescription():
513 list.append(' avoidDuplicateDescription="1" ')
515 # Only display justplay if true
516 if defaultTimer.justplay:
517 list.extend((' justplay="', str(defaultTimer.getJustplay()), '"'))
519 # Only display encoding if != utf-8
520 if defaultTimer.encoding != 'UTF-8':
521 list.extend((' encoding="', str(defaultTimer.encoding), '"'))
523 # Only display searchType if exact
524 if defaultTimer.searchType == "exact":
525 list.extend((' searchType="', str(defaultTimer.searchType), '"'))
527 # Only display searchCase if sensitive
528 if defaultTimer.searchCase == "sensitive":
529 list.extend((' searchCase="', str(defaultTimer.searchCase), '"'))
531 # Close still opened defaults tag
535 # Services + Bouquets
536 for serviceref in defaultTimer.services + defaultTimer.bouquets:
537 ref = ServiceReference(str(serviceref))
540 ' <e2servicereference>', str(serviceref), '</e2servicereference>\n',
541 ' <e2servicename>', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), '</e2servicename>\n',
546 for serviceref in defaultTimer.services:
547 ref = ServiceReference(str(serviceref))
548 list.extend((' <serviceref>', serviceref, '</serviceref>',
549 ' <!-- ', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), ' -->\n',
553 for bouquet in defaultTimer.bouquets:
554 ref = ServiceReference(str(bouquet))
555 list.extend((' <bouquet>', str(bouquet), '</bouquet>',
556 ' <!-- ', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), ' -->\n',
560 if defaultTimer.hasAfterEvent():
562 AFTEREVENT.NONE: "none",
563 AFTEREVENT.STANDBY: "standby",
564 AFTEREVENT.DEEPSTANDBY: "shutdown",
565 AFTEREVENT.AUTO: "auto"
567 for afterevent in defaultTimer.afterevent:
568 action, timespan = afterevent
569 list.append(' <afterevent')
570 if timespan[0] is not None:
571 list.append(' from="%02d:%02d" to="%02d:%02d"' % (timespan[0][0], timespan[0][1], timespan[1][0], timespan[1][1]))
572 list.extend(('>', idx[action], '</afterevent>\n'))
575 for title in defaultTimer.getExcludedTitle():
576 list.extend((' <exclude where="title">', stringToXML(title), '</exclude>\n'))
577 for short in defaultTimer.getExcludedShort():
578 list.extend((' <exclude where="shortdescription">', stringToXML(short), '</exclude>\n'))
579 for desc in defaultTimer.getExcludedDescription():
580 list.extend((' <exclude where="description">', stringToXML(desc), '</exclude>\n'))
581 for day in defaultTimer.getExcludedDays():
582 list.extend((' <exclude where="dayofweek">', stringToXML(day), '</exclude>\n'))
585 for title in defaultTimer.getIncludedTitle():
586 list.extend((' <include where="title">', stringToXML(title), '</include>\n'))
587 for short in defaultTimer.getIncludedShort():
588 list.extend((' <include where="shortdescription">', stringToXML(short), '</include>\n'))
589 for desc in defaultTimer.getIncludedDescription():
590 list.extend((' <include where="description">', stringToXML(desc), '</include>\n'))
591 for day in defaultTimer.getIncludedDays():
592 list.extend((' <include where="dayofweek">', stringToXML(day), '</include>\n'))
595 for tag in defaultTimer.tags:
596 list.extend((' <tag>', stringToXML(tag), '</tag>\n'))
598 # Keep the list clean
601 list.pop() # <defaults
603 list.append(' </defaults>\n\n')
607 # Common attributes (match, enabled)
608 list.extend((' <timer name="', stringToXML(timer.name), '" match="', stringToXML(timer.match), '" enabled="', timer.getEnabled(), '"'))
611 if timer.hasTimespan():
612 list.extend((' from="', timer.getTimespanBegin(), '" to="', timer.getTimespanEnd(), '"'))
615 if timer.hasDuration():
616 list.extend((' maxduration="', str(timer.getDuration()), '"'))
619 if timer.hasDestination():
620 list.extend((' location="', stringToXML(timer.destination), '"'))
623 if timer.hasOffset():
624 if timer.isOffsetEqual():
625 list.extend((' offset="', str(timer.getOffsetBegin()), '"'))
627 list.extend((' offset="', str(timer.getOffsetBegin()), ',', str(timer.getOffsetEnd()), '"'))
630 if timer.hasCounter():
631 list.extend((' lastBegin="', str(timer.getLastBegin()), '" counter="', str(timer.getCounter()), '" left="', str(timer.getCounterLeft()) ,'"'))
632 if timer.hasCounterFormatString():
633 list.extend((' lastActivation="', str(timer.getCounterLimit()), '"'))
634 list.extend((' counterFormat="', str(timer.getCounterFormatString()), '"'))
636 # Duplicate Description
637 if timer.getAvoidDuplicateDescription():
638 list.extend((' avoidDuplicateDescription="', str(timer.getAvoidDuplicateDescription()), '"'))
640 # Only display justplay if true
642 list.extend((' justplay="', str(timer.getJustplay()), '"'))
644 # Only display encoding if != utf-8
645 if timer.encoding != 'UTF-8':
646 list.extend((' encoding="', str(timer.encoding), '"'))
648 # Only display searchType if exact
649 if timer.searchType == "exact":
650 list.extend((' searchType="', str(timer.searchType), '"'))
652 # Only display searchCase if sensitive
653 if timer.searchCase == "sensitive":
654 list.extend((' searchCase="', str(timer.searchCase), '"'))
656 # Only display overrideAlternatives if true
657 if timer.overrideAlternatives:
658 list.extend((' overrideAlternatives="', str(timer.getOverrideAlternatives()), '"'))
660 # Close still opened timer tag
664 # Services + Bouquets
665 for serviceref in timer.services + timer.bouquets:
666 ref = ServiceReference(str(serviceref))
669 ' <e2servicereference>', str(serviceref), '</e2servicereference>\n',
670 ' <e2servicename>', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), '</e2servicename>\n',
675 for serviceref in timer.services:
676 ref = ServiceReference(str(serviceref))
677 list.extend((' <serviceref>', serviceref, '</serviceref>',
678 ' <!-- ', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), ' -->\n',
682 for bouquet in timer.bouquets:
683 ref = ServiceReference(str(bouquet))
684 list.extend((' <bouquet>', str(bouquet), '</bouquet>',
685 ' <!-- ', stringToXML(ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', '')), ' -->\n',
689 if timer.hasAfterEvent():
691 AFTEREVENT.NONE: "none",
692 AFTEREVENT.STANDBY: "standby",
693 AFTEREVENT.DEEPSTANDBY: "shutdown",
694 AFTEREVENT.AUTO: "auto"
696 for afterevent in timer.afterevent:
697 action, timespan = afterevent
698 list.append(' <afterevent')
699 if timespan[0] is not None:
700 list.append(' from="%02d:%02d" to="%02d:%02d"' % (timespan[0][0], timespan[0][1], timespan[1][0], timespan[1][1]))
701 list.extend(('>', idx[action], '</afterevent>\n'))
704 for title in timer.getExcludedTitle():
705 list.extend((' <exclude where="title">', stringToXML(title), '</exclude>\n'))
706 for short in timer.getExcludedShort():
707 list.extend((' <exclude where="shortdescription">', stringToXML(short), '</exclude>\n'))
708 for desc in timer.getExcludedDescription():
709 list.extend((' <exclude where="description">', stringToXML(desc), '</exclude>\n'))
710 for day in timer.getExcludedDays():
711 list.extend((' <exclude where="dayofweek">', stringToXML(day), '</exclude>\n'))
714 for title in timer.getIncludedTitle():
715 list.extend((' <include where="title">', stringToXML(title), '</include>\n'))
716 for short in timer.getIncludedShort():
717 list.extend((' <include where="shortdescription">', stringToXML(short), '</include>\n'))
718 for desc in timer.getIncludedDescription():
719 list.extend((' <include where="description">', stringToXML(desc), '</include>\n'))
720 for day in timer.getIncludedDays():
721 list.extend((' <include where="dayofweek">', stringToXML(day), '</include>\n'))
724 for tag in timer.tags:
725 list.extend((' <tag>', stringToXML(tag), '</tag>\n'))
728 list.append(' </timer>\n\n')
730 # End of Configuration
731 list.append('</autotimer>\n')