2 from time import strftime
5 from re import compile as re_compile
7 class AutoTimerComponent(object):
8 """AutoTimer Component which also handles validity checks"""
13 def __init__(self, id, name, match, enabled, *args, **kwargs):
16 self.setValues(name, match, enabled, *args, **kwargs)
21 def clear(self, id = -1, enabled = False):
23 self.setValues('', '', enabled)
26 Create a deep copy of this instance
29 return self.__deepcopy__({})
38 Keeps init small and helps setting many values at once
40 def setValues(self, name, match, enabled, timespan = None, services = None, offset = None, \
41 afterevent = [], exclude = None, maxduration = None, destination = None, \
42 include = None, matchCount = 0, matchLeft = 0, matchLimit = '', matchFormatString = '', \
43 lastBegin = 0, justplay = False, avoidDuplicateDescription = 0, bouquets = None, \
44 tags = None, encoding = 'UTF-8', searchType = "partial", searchCase = "insensitive"):
47 self.enabled = enabled
48 self.timespan = timespan
49 self.services = services
51 self.afterevent = afterevent
52 self.exclude = exclude
53 self.maxduration = maxduration
54 self.destination = destination
55 self.include = include
56 self.matchCount = matchCount
57 self.matchLeft = matchLeft
58 self.matchLimit = matchLimit
59 self.matchFormatString = matchFormatString
60 self.lastBegin = lastBegin
61 self.justplay = justplay
62 self.avoidDuplicateDescription = avoidDuplicateDescription
63 self.bouquets = bouquets
64 self.tags = tags or []
65 self.encoding = encoding
66 self.searchType = searchType
67 self.searchCase = searchCase
69 ### Attributes / Properties
71 def setAfterEvent(self, afterevent):
72 if afterevent is not self._afterevent:
73 del self._afterevent[:]
77 for x in range(0, Len):
78 action, timespan = afterevent[x]
80 # Remove original entry
82 # If the second argument is a tuple we assume the entry is already parsed
83 if isinstance(timespan, tuple):
84 self._afterevent.insert(x, (action, timespan))
85 elif timespan is None:
86 self._afterevent.insert(x, (action, (None,)))
88 self._afterevent.insert(x, (action, self.calculateDayspan(*timespan)))
90 afterevent = property(lambda self: self._afterevent, setAfterEvent)
92 def setBouquets(self, bouquets):
94 self._bouquets = bouquets
98 bouquets = property(lambda self: self._bouquets , setBouquets)
100 def setEncoding(self, encoding):
102 self._encoding = encoding
104 encoding = property(lambda self: self._encoding, setEncoding)
106 def setExclude(self, exclude):
109 [re_compile(x) for x in exclude[0]],
110 [re_compile(x) for x in exclude[1]],
111 [re_compile(x) for x in exclude[2]],
115 self._exclude = ([], [], [], [])
117 exclude = property(lambda self: self._exclude, setExclude)
119 def setInclude(self, include):
122 [re_compile(x) for x in include[0]],
123 [re_compile(x) for x in include[1]],
124 [re_compile(x) for x in include[2]],
128 self._include = ([], [], [], [])
130 include = property(lambda self: self._include, setInclude)
132 def setSearchCase(self, case):
133 assert case in ("sensitive", "insensitive"), "search case must be sensitive or insensitive"
134 self._searchCase = case
136 searchCase = property(lambda self: self._searchCase, setSearchCase)
138 def setSearchType(self, type):
139 assert type in ("exact", "partial"), "search type must be exact or partial"
140 self._searchType = type
142 searchType = property(lambda self: self._searchType, setSearchType)
144 def setServices(self, services):
146 self._services = services
150 services = property(lambda self: self._services, setServices)
152 def setTimespan(self, timespan):
153 if timespan is None or len(timespan) and timespan[0] is None:
154 self._timespan = (None,)
156 self._timespan = self.calculateDayspan(*timespan)
158 timespan = property(lambda self: self._timespan, setTimespan)
160 ### See if Attributes are set
162 def hasAfterEvent(self):
163 return len(self.afterevent)
165 def hasAfterEventTimespan(self):
166 for afterevent in self.afterevent:
167 if afterevent[1][0] is not None:
171 def hasCounter(self):
172 return self.matchCount != 0
174 def hasCounterFormatString(self):
175 return self.matchFormatString != ''
177 def hasDestination(self):
178 return self.destination is not None
180 def hasDuration(self):
181 return self.maxduration is not None
184 return len(self.tags) > 0
186 def hasTimespan(self):
187 return self.timespan[0] is not None
190 return self.offset is not None
195 Returns a tulple of (input begin, input end, begin earlier than end)
197 def calculateDayspan(self, begin, end, ignore = None):
198 if end[0] < begin[0] or (end[0] == begin[0] and end[1] <= begin[1]):
199 return (begin, end, True)
201 return (begin, end, False)
204 Returns if a given timestruct is in a timespan
206 def checkAnyTimespan(self, time, begin = None, end = None, haveDayspan = False):
210 # Check if we span a day
212 # Check if begin of event is later than our timespan starts
213 if time.tm_hour > begin[0] or (time.tm_hour == begin[0] and time.tm_min >= begin[1]):
214 # If so, event is in our timespan
216 # Check if begin of event is earlier than our timespan end
217 if time.tm_hour < end[0] or (time.tm_hour == end[0] and time.tm_min <= end[1]):
218 # If so, event is in our timespan
222 # Check if event begins earlier than our timespan starts
223 if time.tm_hour < begin[0] or (time.tm_hour == begin[0] and time.tm_min < begin[1]):
224 # Its out of our timespan then
226 # Check if event begins later than our timespan ends
227 if time.tm_hour > end[0] or (time.tm_hour == end[0] and time.tm_min > end[1]):
228 # Its out of our timespan then
233 Returns a list of all allowed services by listing the bouquets
235 def getFullServices(self):
236 list = self.services[:]
238 from enigma import eServiceReference, eServiceCenter
239 serviceHandler = eServiceCenter.getInstance()
240 for bouquet in self.bouquets:
241 myref = eServiceReference(str(bouquet))
242 mylist = serviceHandler.list(myref)
243 if mylist is not None:
246 # TODO: I wonder if its sane to assume we get services here (and not just new lists)
247 # We can ignore markers & directorys here because they won't match any event's service :-)
249 # strip all after last :
251 pos = value.rfind(':')
253 value = value[:pos+1]
262 Called when a timer based on this component was added
264 def update(self, begin, timestamp):
265 # Only update limit when we have new begin
266 if begin > self.lastBegin:
267 self.lastBegin = begin
270 # %m is Month, %U is week (sunday), %W is week (monday)
271 newLimit = strftime(self.matchFormatString, timestamp)
273 if newLimit != self.matchLimit:
274 self.matchLeft = self.matchCount
275 self.matchLimit = newLimit
277 ### Makes saving Config easier
279 getAvoidDuplicateDescription = lambda self: self.avoidDuplicateDescription
281 getBouquets = lambda self: self._bouquets
283 getCompleteAfterEvent = lambda self: self._afterevent
285 getCounter = lambda self: self.matchCount
286 getCounterFormatString = lambda self: self.matchFormatString
287 getCounterLeft = lambda self: self.matchLeft
288 getCounterLimit = lambda self: self.matchLimit
290 # 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 ;-)
291 getDestination = lambda self: self.destination is not None
293 getDuration = lambda self: self.maxduration/60
295 getEnabled = lambda self: self.enabled and "yes" or "no"
297 getExclude = lambda self: self._exclude
298 getExcludedDays = lambda self: self.exclude[3]
299 getExcludedDescription = lambda self: [x.pattern for x in self.exclude[2]]
300 getExcludedShort = lambda self: [x.pattern for x in self.exclude[1]]
301 getExcludedTitle = lambda self: [x.pattern for x in self.exclude[0]]
303 getInclude = lambda self: self._include
304 getIncludedTitle = lambda self: [x.pattern for x in self.include[0]]
305 getIncludedShort = lambda self: [x.pattern for x in self.include[1]]
306 getIncludedDescription = lambda self: [x.pattern for x in self.include[2]]
307 getIncludedDays = lambda self: self.include[3]
309 getJustplay = lambda self: self.justplay and "1" or "0"
311 getLastBegin = lambda self: self.lastBegin
313 getMatch = lambda self: self.match
314 getName = lambda self: self.name
316 getOffsetBegin = lambda self: self.offset[0]/60
317 getOffsetEnd = lambda self: self.offset[1]/60
319 getServices = lambda self: self._services
321 getTags = lambda self: self.tags
323 getTimespan = lambda self: self._timespan
324 getTimespanBegin = lambda self: '%02d:%02d' % (self.timespan[0][0], self.timespan[0][1])
325 getTimespanEnd = lambda self: '%02d:%02d' % (self.timespan[1][0], self.timespan[1][1])
327 isOffsetEqual = lambda self: self.offset[0] == self.offset[1]
329 ### Actual functionality
331 def applyOffset(self, begin, end):
332 if self.offset is None:
334 return (begin - self.offset[0], end + self.offset[1])
336 def checkCounter(self, timestamp):
337 # 0-Count is considered "unset"
338 if self.matchCount == 0:
341 # Check if event is in current timespan (we can only manage one!)
342 limit = strftime(self.matchFormatString, timestamp)
343 if limit != self.matchLimit:
346 if self.matchLeft > 0:
350 def checkDuration(self, length):
351 if self.maxduration is None:
353 return length > self.maxduration
355 def checkExcluded(self, title, short, extended, dayofweek):
356 if len(self.exclude[3]):
357 list = [x for x in self.exclude[3]]
358 if "weekend" in list:
359 list.extend(["5", "6"])
360 if "weekday" in list:
361 list.extend(["0", "1", "2", "3", "4"])
362 if dayofweek in list:
365 for exclude in self.exclude[0]:
366 if exclude.search(title):
368 for exclude in self.exclude[1]:
369 if exclude.search(short):
371 for exclude in self.exclude[2]:
372 if exclude.search(extended):
376 def checkFilter(self, title, short, extended, dayofweek):
377 if self.checkExcluded(title, short, extended, dayofweek):
380 return self.checkIncluded(title, short, extended, dayofweek)
382 def checkIncluded(self, title, short, extended, dayofweek):
383 if len(self.include[3]):
384 list = [x for x in self.include[3]]
385 if "weekend" in list:
386 list.extend(["5", "6"])
387 if "weekday" in list:
388 list.extend(["0", "1", "2", "3", "4"])
389 if dayofweek not in list:
392 for include in self.include[0]:
393 if not include.search(title):
395 for include in self.include[1]:
396 if not include.search(short):
398 for include in self.include[2]:
399 if not include.search(extended):
404 def checkServices(self, service):
405 if len(self.services) or len(self.bouquets):
406 if service in self.services:
409 from enigma import eServiceReference, eServiceCenter
410 serviceHandler = eServiceCenter.getInstance()
411 for bouquet in self.bouquets:
412 myref = eServiceReference(str(bouquet))
413 mylist = serviceHandler.list(myref)
414 if mylist is not None:
417 # TODO: I wonder if its sane to assume we get services here (and not just new lists)
418 # We can ignore markers & directorys here because they won't match any event's service :-)
420 # strip all after last :
422 pos = value.rfind(':')
424 value = value[:pos+1]
433 def checkTimespan(self, begin):
434 return self.checkAnyTimespan(begin, *self.timespan)
436 def decrementCounter(self):
437 if self.matchCount and self.matchLeft > 0:
440 def getAfterEvent(self):
441 for afterevent in self.afterevent:
442 if afterevent[1][0] is None:
446 def getAfterEventTimespan(self, end):
447 for afterevent in self.afterevent:
448 if not self.checkAnyTimespan(end, *afterevent[1]):
455 return self.__class__(
460 timespan = self.timespan,
461 services = self.services,
462 offset = self.offset,
463 afterevent = self.afterevent,
464 exclude = (self.getExcludedTitle(), self.getExcludedShort(), self.getExcludedDescription(), self.getExcludedDays()),
465 maxduration = self.maxduration,
466 destination = self.destination,
467 include = (self.getIncludedTitle(), self.getIncludedShort(), self.getIncludedDescription(), self.getIncludedDays()),
468 matchCount = self.matchCount,
469 matchLeft = self.matchLeft,
470 matchLimit = self.matchLimit,
471 matchFormatString = self.matchFormatString,
472 lastBegin = self.lastBegin,
473 justplay = self.justplay,
474 avoidDuplicateDescription = self.avoidDuplicateDescription,
475 bouquets = self.bouquets,
477 encoding = self.encoding,
478 searchType = self.searchType,
479 searchCase = self.searchCase
482 def __deepcopy__(self, memo):
483 return self.__class__(
488 timespan = self.timespan,
489 services = self.services[:],
490 offset = self.offset and self.offset[:],
491 afterevent = self.afterevent[:],
492 exclude = (self.getExcludedTitle(), self.getExcludedShort(), self.getExcludedDescription(), self.exclude[3][:]),
493 maxduration = self.maxduration,
494 destination = self.destination,
495 include = (self.getIncludedTitle(), self.getIncludedShort(), self.getIncludedDescription(), self.include[3][:]),
496 matchCount = self.matchCount,
497 matchLeft = self.matchLeft,
498 matchLimit = self.matchLimit,
499 matchFormatString = self.matchFormatString,
500 lastBegin = self.lastBegin,
501 justplay = self.justplay,
502 avoidDuplicateDescription = self.avoidDuplicateDescription,
503 bouquets = self.bouquets[:],
505 encoding = self.encoding,
506 searchType = self.searchType,
507 searchCase = self.searchCase
510 def __eq__(self, other):
511 if isinstance(other, AutoTimerComponent):
512 return self.id == other.id
515 def __lt__(self, other):
516 if isinstance(other, AutoTimerComponent):
517 return self.name.lower() < other.name.lower()
520 def __ne__(self, other):
521 return not self.__eq__(other)
531 str(self.searchCase),
532 str(self.searchType),
536 str(self.afterevent),
537 str(([x.pattern for x in self.exclude[0]],
538 [x.pattern for x in self.exclude[1]],
539 [x.pattern for x in self.exclude[2]],
542 str(([x.pattern for x in self.include[0]],
543 [x.pattern for x in self.include[1]],
544 [x.pattern for x in self.include[2]],
547 str(self.maxduration),
549 str(self.destination),
550 str(self.matchCount),
552 str(self.matchLimit),
553 str(self.matchFormatString),
556 str(self.avoidDuplicateDescription),