2 from time import strftime
5 from re import compile as re_compile
7 # Alternatives and service restriction
8 from enigma import eServiceReference, eServiceCenter
10 class AutoTimerComponent(object):
11 """AutoTimer Component which also handles validity checks"""
16 def __init__(self, id, name, match, enabled, *args, **kwargs):
19 self.setValues(name, match, enabled, *args, **kwargs)
24 def clear(self, id = -1, enabled = False):
26 self.setValues('', '', enabled)
29 Create a deep copy of this instance
32 return self.__deepcopy__({})
41 Keeps init small and helps setting many values at once
43 def setValues(self, name, match, enabled, timespan = None, services = None, offset = None, \
44 afterevent = [], exclude = None, maxduration = None, destination = None, \
45 include = None, matchCount = 0, matchLeft = 0, matchLimit = '', matchFormatString = '', \
46 lastBegin = 0, justplay = False, avoidDuplicateDescription = 0, bouquets = None, \
47 tags = None, encoding = 'UTF-8', searchType = "partial", searchCase = "insensitive", \
48 overrideAlternatives = False):
51 self.enabled = enabled
52 self.timespan = timespan
53 self.services = services
55 self.afterevent = afterevent
56 self.exclude = exclude
57 self.maxduration = maxduration
58 self.destination = destination
59 self.include = include
60 self.matchCount = matchCount
61 self.matchLeft = matchLeft
62 self.matchLimit = matchLimit
63 self.matchFormatString = matchFormatString
64 self.lastBegin = lastBegin
65 self.justplay = justplay
66 self.avoidDuplicateDescription = avoidDuplicateDescription
67 self.bouquets = bouquets
68 self.tags = tags or []
69 self.encoding = encoding
70 self.searchType = searchType
71 self.searchCase = searchCase
72 self.overrideAlternatives = overrideAlternatives
74 ### Attributes / Properties
76 def setAfterEvent(self, afterevent):
77 if afterevent is not self._afterevent:
78 del self._afterevent[:]
82 for action, timespan in afterevent:
83 if timespan is None or timespan[0] is None:
84 self._afterevent.append((action, (None,)))
86 self._afterevent.append((action, self.calculateDayspan(*timespan)))
88 afterevent = property(lambda self: self._afterevent, setAfterEvent)
90 def setBouquets(self, bouquets):
92 self._bouquets = bouquets
96 bouquets = property(lambda self: self._bouquets , setBouquets)
98 def setEncoding(self, encoding):
100 self._encoding = encoding
102 encoding = property(lambda self: self._encoding, setEncoding)
104 def setExclude(self, exclude):
107 [re_compile(x) for x in exclude[0]],
108 [re_compile(x) for x in exclude[1]],
109 [re_compile(x) for x in exclude[2]],
113 self._exclude = ([], [], [], [])
115 exclude = property(lambda self: self._exclude, setExclude)
117 def setInclude(self, include):
120 [re_compile(x) for x in include[0]],
121 [re_compile(x) for x in include[1]],
122 [re_compile(x) for x in include[2]],
126 self._include = ([], [], [], [])
128 include = property(lambda self: self._include, setInclude)
130 def setSearchCase(self, case):
131 assert case in ("sensitive", "insensitive"), "search case must be sensitive or insensitive"
132 self._searchCase = case
134 searchCase = property(lambda self: self._searchCase, setSearchCase)
136 def setSearchType(self, type):
137 assert type in ("exact", "partial"), "search type must be exact or partial"
138 self._searchType = type
140 searchType = property(lambda self: self._searchType, setSearchType)
142 def setServices(self, services):
144 self._services = services
148 services = property(lambda self: self._services, setServices)
150 def setTimespan(self, timespan):
151 if timespan is None or timespan and timespan[0] is None:
152 self._timespan = (None,)
154 self._timespan = self.calculateDayspan(*timespan)
156 timespan = property(lambda self: self._timespan, setTimespan)
158 ### See if Attributes are set
160 def hasAfterEvent(self):
161 return len(self.afterevent)
163 def hasAfterEventTimespan(self):
164 for afterevent in self.afterevent:
165 if afterevent[1][0] is not None:
169 def hasCounter(self):
170 return self.matchCount != 0
172 def hasCounterFormatString(self):
173 return self.matchFormatString != ''
175 def hasDestination(self):
176 return self.destination is not None
178 def hasDuration(self):
179 return self.maxduration is not None
182 return len(self.tags)
184 def hasTimespan(self):
185 return self.timespan[0] is not None
188 return self.offset is not None
193 Returns a tulple of (input begin, input end, begin earlier than end)
195 def calculateDayspan(self, begin, end, ignore = None):
196 if end[0] < begin[0] or (end[0] == begin[0] and end[1] <= begin[1]):
197 return (begin, end, True)
199 return (begin, end, False)
202 Returns if a given timestruct is in a timespan
204 def checkAnyTimespan(self, time, begin = None, end = None, haveDayspan = False):
208 # Check if we span a day
210 # Check if begin of event is later than our timespan starts
211 if time.tm_hour > begin[0] or (time.tm_hour == begin[0] and time.tm_min >= begin[1]):
212 # If so, event is in our timespan
214 # Check if begin of event is earlier than our timespan end
215 if time.tm_hour < end[0] or (time.tm_hour == end[0] and time.tm_min <= end[1]):
216 # If so, event is in our timespan
220 # Check if event begins earlier than our timespan starts
221 if time.tm_hour < begin[0] or (time.tm_hour == begin[0] and time.tm_min < begin[1]):
222 # Its out of our timespan then
224 # Check if event begins later than our timespan ends
225 if time.tm_hour > end[0] or (time.tm_hour == end[0] and time.tm_min > end[1]):
226 # Its out of our timespan then
231 Called when a timer based on this component was added
233 def update(self, begin, timestamp):
234 # Only update limit when we have new begin
235 if begin > self.lastBegin:
236 self.lastBegin = begin
239 # %m is Month, %U is week (sunday), %W is week (monday)
240 newLimit = strftime(self.matchFormatString, timestamp)
242 if newLimit != self.matchLimit:
243 self.matchLeft = self.matchCount
244 self.matchLimit = newLimit
246 ### Makes saving Config easier
248 getAvoidDuplicateDescription = lambda self: self.avoidDuplicateDescription
250 getBouquets = lambda self: self._bouquets
252 getCompleteAfterEvent = lambda self: self._afterevent
254 getCounter = lambda self: self.matchCount
255 getCounterFormatString = lambda self: self.matchFormatString
256 getCounterLeft = lambda self: self.matchLeft
257 getCounterLimit = lambda self: self.matchLimit
259 # XXX: as this function was not added by me (ritzMo) i'll leave it like this but i'm not really sure if this is right ;-)
260 getDestination = lambda self: self.destination is not None
262 getDuration = lambda self: self.maxduration/60
264 getEnabled = lambda self: self.enabled and "yes" or "no"
266 getExclude = lambda self: self._exclude
267 getExcludedDays = lambda self: self.exclude[3]
268 getExcludedDescription = lambda self: [x.pattern for x in self.exclude[2]]
269 getExcludedShort = lambda self: [x.pattern for x in self.exclude[1]]
270 getExcludedTitle = lambda self: [x.pattern for x in self.exclude[0]]
272 getInclude = lambda self: self._include
273 getIncludedTitle = lambda self: [x.pattern for x in self.include[0]]
274 getIncludedShort = lambda self: [x.pattern for x in self.include[1]]
275 getIncludedDescription = lambda self: [x.pattern for x in self.include[2]]
276 getIncludedDays = lambda self: self.include[3]
278 getJustplay = lambda self: self.justplay and "1" or "0"
280 getLastBegin = lambda self: self.lastBegin
282 getMatch = lambda self: self.match
283 getName = lambda self: self.name
285 getOffsetBegin = lambda self: self.offset[0]/60
286 getOffsetEnd = lambda self: self.offset[1]/60
288 getOverrideAlternatives = lambda self: self.overrideAlternatives and "1" or "0"
290 getServices = lambda self: self._services
292 getTags = lambda self: self.tags
294 getTimespan = lambda self: self._timespan
295 getTimespanBegin = lambda self: '%02d:%02d' % (self.timespan[0][0], self.timespan[0][1])
296 getTimespanEnd = lambda self: '%02d:%02d' % (self.timespan[1][0], self.timespan[1][1])
298 isOffsetEqual = lambda self: self.offset[0] == self.offset[1]
300 ### Actual functionality
302 def applyOffset(self, begin, end):
303 if self.offset is None:
305 return (begin - self.offset[0], end + self.offset[1])
307 def checkCounter(self, timestamp):
308 # 0-Count is considered "unset"
309 if self.matchCount == 0:
312 # Check if event is in current timespan (we can only manage one!)
313 limit = strftime(self.matchFormatString, timestamp)
314 if limit != self.matchLimit:
317 if self.matchLeft > 0:
321 def checkDuration(self, length):
322 if self.maxduration is None:
324 return length > self.maxduration
326 def checkExcluded(self, title, short, extended, dayofweek):
328 list = self.exclude[3]
329 if dayofweek in list:
331 if "weekend" in list and dayofweek in ("5", "6"):
333 if "weekday" in list and dayofweek in ("0", "1", "2", "3", "4"):
336 for exclude in self.exclude[0]:
337 if exclude.search(title):
339 for exclude in self.exclude[1]:
340 if exclude.search(short):
342 for exclude in self.exclude[2]:
343 if exclude.search(extended):
347 def checkFilter(self, title, short, extended, dayofweek):
348 if self.checkExcluded(title, short, extended, dayofweek):
351 return self.checkIncluded(title, short, extended, dayofweek)
353 def checkIncluded(self, title, short, extended, dayofweek):
355 list = self.include[3][:]
356 if "weekend" in list:
357 list.extend(("5", "6"))
358 if "weekday" in list:
359 list.extend(("0", "1", "2", "3", "4"))
360 if dayofweek not in list:
363 for include in self.include[0]:
364 if not include.search(title):
366 for include in self.include[1]:
367 if not include.search(short):
369 for include in self.include[2]:
370 if not include.search(extended):
375 def checkServices(self, check_service):
376 services = self.services
377 bouquets = self.bouquets
378 if services or bouquets:
381 for service in services:
382 if service == check_service:
385 myref = eServiceReference(str(service))
386 if myref.flags & eServiceReference.isGroup:
387 addbouquets.append(service)
389 serviceHandler = eServiceCenter.getInstance()
390 for bouquet in bouquets + addbouquets:
391 myref = eServiceReference(str(bouquet))
392 mylist = serviceHandler.list(myref)
393 if mylist is not None:
396 # TODO: I wonder if its sane to assume we get services here (and not just new lists)
397 # We can ignore markers & directorys here because they won't match any event's service :-)
399 # strip all after last :
401 pos = value.rfind(':')
403 if value[pos-1] == ':':
405 value = value[:pos+1]
407 if value == check_service:
415 Return alternative service including a given ref.
416 Note that this only works for alternatives that the autotimer is restricted to.
418 def getAlternative(self, override_service):
419 services = self.services
421 serviceHandler = eServiceCenter.getInstance()
423 for service in services:
424 myref = eServiceReference(str(service))
425 if myref.flags & eServiceReference.isGroup:
426 mylist = serviceHandler.list(myref)
427 if mylist is not None:
431 # strip all after last :
433 pos = value.rfind(':')
435 if value[pos-1] == ':':
437 value = value[:pos+1]
439 if value == override_service:
443 return override_service
445 def checkTimespan(self, begin):
446 return self.checkAnyTimespan(begin, *self.timespan)
448 def decrementCounter(self):
449 if self.matchCount and self.matchLeft > 0:
452 def getAfterEvent(self):
453 for afterevent in self.afterevent:
454 if afterevent[1][0] is None:
458 def getAfterEventTimespan(self, end):
459 for afterevent in self.afterevent:
460 if not self.checkAnyTimespan(end, *afterevent[1]):
467 return self.__class__(
472 timespan = self.timespan,
473 services = self.services,
474 offset = self.offset,
475 afterevent = self.afterevent,
476 exclude = (self.getExcludedTitle(), self.getExcludedShort(), self.getExcludedDescription(), self.getExcludedDays()),
477 maxduration = self.maxduration,
478 destination = self.destination,
479 include = (self.getIncludedTitle(), self.getIncludedShort(), self.getIncludedDescription(), self.getIncludedDays()),
480 matchCount = self.matchCount,
481 matchLeft = self.matchLeft,
482 matchLimit = self.matchLimit,
483 matchFormatString = self.matchFormatString,
484 lastBegin = self.lastBegin,
485 justplay = self.justplay,
486 avoidDuplicateDescription = self.avoidDuplicateDescription,
487 bouquets = self.bouquets,
489 encoding = self.encoding,
490 searchType = self.searchType,
491 searchCase = self.searchCase,
492 overrideAlternatives = self.overrideAlternatives
495 def __deepcopy__(self, memo):
496 return self.__class__(
501 timespan = self.timespan,
502 services = self.services[:],
503 offset = self.offset and self.offset[:],
504 afterevent = self.afterevent[:],
505 exclude = (self.getExcludedTitle(), self.getExcludedShort(), self.getExcludedDescription(), self.exclude[3][:]),
506 maxduration = self.maxduration,
507 destination = self.destination,
508 include = (self.getIncludedTitle(), self.getIncludedShort(), self.getIncludedDescription(), self.include[3][:]),
509 matchCount = self.matchCount,
510 matchLeft = self.matchLeft,
511 matchLimit = self.matchLimit,
512 matchFormatString = self.matchFormatString,
513 lastBegin = self.lastBegin,
514 justplay = self.justplay,
515 avoidDuplicateDescription = self.avoidDuplicateDescription,
516 bouquets = self.bouquets[:],
518 encoding = self.encoding,
519 searchType = self.searchType,
520 searchCase = self.searchCase,
521 overrideAlternatives = self.overrideAlternatives
524 def __eq__(self, other):
525 if isinstance(other, AutoTimerComponent):
526 return self.id == other.id
529 def __lt__(self, other):
530 if isinstance(other, AutoTimerComponent):
531 return self.name.lower() < other.name.lower()
534 def __ne__(self, other):
535 return not self.__eq__(other)
545 str(self.searchCase),
546 str(self.searchType),
550 str(self.afterevent),
551 str(([x.pattern for x in self.exclude[0]],
552 [x.pattern for x in self.exclude[1]],
553 [x.pattern for x in self.exclude[2]],
556 str(([x.pattern for x in self.include[0]],
557 [x.pattern for x in self.include[1]],
558 [x.pattern for x in self.include[2]],
561 str(self.maxduration),
563 str(self.destination),
564 str(self.matchCount),
566 str(self.matchLimit),
567 str(self.matchFormatString),
570 str(self.avoidDuplicateDescription),
573 str(self.overrideAlternatives),