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[:]
80 for action, timespan in afterevent:
81 # If the second argument is a tuple we assume the entry is already parsed
82 if isinstance(timespan, tuple):
83 self._afterevent.insert(x, (action, timespan))
84 elif timespan is None:
85 self._afterevent.insert(x, (action, (None,)))
87 self._afterevent.insert(x, (action, self.calculateDayspan(*timespan)))
89 afterevent = property(lambda self: self._afterevent, setAfterEvent)
91 def setBouquets(self, bouquets):
93 self._bouquets = bouquets
97 bouquets = property(lambda self: self._bouquets , setBouquets)
99 def setEncoding(self, encoding):
101 self._encoding = encoding
103 encoding = property(lambda self: self._encoding, setEncoding)
105 def setExclude(self, exclude):
108 [re_compile(x) for x in exclude[0]],
109 [re_compile(x) for x in exclude[1]],
110 [re_compile(x) for x in exclude[2]],
114 self._exclude = ([], [], [], [])
116 exclude = property(lambda self: self._exclude, setExclude)
118 def setInclude(self, include):
121 [re_compile(x) for x in include[0]],
122 [re_compile(x) for x in include[1]],
123 [re_compile(x) for x in include[2]],
127 self._include = ([], [], [], [])
129 include = property(lambda self: self._include, setInclude)
131 def setSearchCase(self, case):
132 assert case in ("sensitive", "insensitive"), "search case must be sensitive or insensitive"
133 self._searchCase = case
135 searchCase = property(lambda self: self._searchCase, setSearchCase)
137 def setSearchType(self, type):
138 assert type in ("exact", "partial"), "search type must be exact or partial"
139 self._searchType = type
141 searchType = property(lambda self: self._searchType, setSearchType)
143 def setServices(self, services):
145 self._services = services
149 services = property(lambda self: self._services, setServices)
151 def setTimespan(self, timespan):
152 if timespan is None or timespan and timespan[0] is None:
153 self._timespan = (None,)
155 self._timespan = self.calculateDayspan(*timespan)
157 timespan = property(lambda self: self._timespan, setTimespan)
159 ### See if Attributes are set
161 def hasAfterEvent(self):
162 return len(self.afterevent)
164 def hasAfterEventTimespan(self):
165 for afterevent in self.afterevent:
166 if afterevent[1][0] is not None:
170 def hasCounter(self):
171 return self.matchCount != 0
173 def hasCounterFormatString(self):
174 return self.matchFormatString != ''
176 def hasDestination(self):
177 return self.destination is not None
179 def hasDuration(self):
180 return self.maxduration is not None
183 return len(self.tags)
185 def hasTimespan(self):
186 return self.timespan[0] is not None
189 return self.offset is not None
194 Returns a tulple of (input begin, input end, begin earlier than end)
196 def calculateDayspan(self, begin, end, ignore = None):
197 if end[0] < begin[0] or (end[0] == begin[0] and end[1] <= begin[1]):
198 return (begin, end, True)
200 return (begin, end, False)
203 Returns if a given timestruct is in a timespan
205 def checkAnyTimespan(self, time, begin = None, end = None, haveDayspan = False):
209 # Check if we span a day
211 # Check if begin of event is later than our timespan starts
212 if time.tm_hour > begin[0] or (time.tm_hour == begin[0] and time.tm_min >= begin[1]):
213 # If so, event is in our timespan
215 # Check if begin of event is earlier than our timespan end
216 if time.tm_hour < end[0] or (time.tm_hour == end[0] and time.tm_min <= end[1]):
217 # If so, event is in our timespan
221 # Check if event begins earlier than our timespan starts
222 if time.tm_hour < begin[0] or (time.tm_hour == begin[0] and time.tm_min < begin[1]):
223 # Its out of our timespan then
225 # Check if event begins later than our timespan ends
226 if time.tm_hour > end[0] or (time.tm_hour == end[0] and time.tm_min > end[1]):
227 # Its out of our timespan then
232 Called when a timer based on this component was added
234 def update(self, begin, timestamp):
235 # Only update limit when we have new begin
236 if begin > self.lastBegin:
237 self.lastBegin = begin
240 # %m is Month, %U is week (sunday), %W is week (monday)
241 newLimit = strftime(self.matchFormatString, timestamp)
243 if newLimit != self.matchLimit:
244 self.matchLeft = self.matchCount
245 self.matchLimit = newLimit
247 ### Makes saving Config easier
249 getAvoidDuplicateDescription = lambda self: self.avoidDuplicateDescription
251 getBouquets = lambda self: self._bouquets
253 getCompleteAfterEvent = lambda self: self._afterevent
255 getCounter = lambda self: self.matchCount
256 getCounterFormatString = lambda self: self.matchFormatString
257 getCounterLeft = lambda self: self.matchLeft
258 getCounterLimit = lambda self: self.matchLimit
260 # 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 ;-)
261 getDestination = lambda self: self.destination is not None
263 getDuration = lambda self: self.maxduration/60
265 getEnabled = lambda self: self.enabled and "yes" or "no"
267 getExclude = lambda self: self._exclude
268 getExcludedDays = lambda self: self.exclude[3]
269 getExcludedDescription = lambda self: [x.pattern for x in self.exclude[2]]
270 getExcludedShort = lambda self: [x.pattern for x in self.exclude[1]]
271 getExcludedTitle = lambda self: [x.pattern for x in self.exclude[0]]
273 getInclude = lambda self: self._include
274 getIncludedTitle = lambda self: [x.pattern for x in self.include[0]]
275 getIncludedShort = lambda self: [x.pattern for x in self.include[1]]
276 getIncludedDescription = lambda self: [x.pattern for x in self.include[2]]
277 getIncludedDays = lambda self: self.include[3]
279 getJustplay = lambda self: self.justplay and "1" or "0"
281 getLastBegin = lambda self: self.lastBegin
283 getMatch = lambda self: self.match
284 getName = lambda self: self.name
286 getOffsetBegin = lambda self: self.offset[0]/60
287 getOffsetEnd = lambda self: self.offset[1]/60
289 getOverrideAlternatives = lambda self: self.overrideAlternatives and "1" or "0"
291 getServices = lambda self: self._services
293 getTags = lambda self: self.tags
295 getTimespan = lambda self: self._timespan
296 getTimespanBegin = lambda self: '%02d:%02d' % (self.timespan[0][0], self.timespan[0][1])
297 getTimespanEnd = lambda self: '%02d:%02d' % (self.timespan[1][0], self.timespan[1][1])
299 isOffsetEqual = lambda self: self.offset[0] == self.offset[1]
301 ### Actual functionality
303 def applyOffset(self, begin, end):
304 if self.offset is None:
306 return (begin - self.offset[0], end + self.offset[1])
308 def checkCounter(self, timestamp):
309 # 0-Count is considered "unset"
310 if self.matchCount == 0:
313 # Check if event is in current timespan (we can only manage one!)
314 limit = strftime(self.matchFormatString, timestamp)
315 if limit != self.matchLimit:
318 if self.matchLeft > 0:
322 def checkDuration(self, length):
323 if self.maxduration is None:
325 return length > self.maxduration
327 def checkExcluded(self, title, short, extended, dayofweek):
329 list = self.exclude[3]
330 if dayofweek in list:
332 if "weekend" in list and dayofweek in ("5", "6"):
334 if "weekday" in list and dayofweek in ("0", "1", "2", "3", "4"):
337 for exclude in self.exclude[0]:
338 if exclude.search(title):
340 for exclude in self.exclude[1]:
341 if exclude.search(short):
343 for exclude in self.exclude[2]:
344 if exclude.search(extended):
348 def checkFilter(self, title, short, extended, dayofweek):
349 if self.checkExcluded(title, short, extended, dayofweek):
352 return self.checkIncluded(title, short, extended, dayofweek)
354 def checkIncluded(self, title, short, extended, dayofweek):
356 list = self.include[3][:]
357 if "weekend" in list:
358 list.extend(("5", "6"))
359 if "weekday" in list:
360 list.extend(("0", "1", "2", "3", "4"))
361 if dayofweek not in list:
364 for include in self.include[0]:
365 if not include.search(title):
367 for include in self.include[1]:
368 if not include.search(short):
370 for include in self.include[2]:
371 if not include.search(extended):
376 def checkServices(self, check_service):
377 services = self.services
378 bouquets = self.bouquets
379 if services or bouquets:
382 for service in services:
383 if service == check_service:
386 myref = eServiceReference(str(service))
387 if myref.flags & eServiceReference.isGroup:
388 addbouquets.append(service)
390 serviceHandler = eServiceCenter.getInstance()
391 for bouquet in bouquets + addbouquets:
392 myref = eServiceReference(str(bouquet))
393 mylist = serviceHandler.list(myref)
394 if mylist is not None:
397 # TODO: I wonder if its sane to assume we get services here (and not just new lists)
398 # We can ignore markers & directorys here because they won't match any event's service :-)
400 # strip all after last :
402 pos = value.rfind(':')
404 value = value[:pos+1]
406 if value == check_service:
414 Return alternative service including a given ref.
415 Note that this only works for alternatives that the autotimer is restricted to.
417 def getAlternative(self, override_service):
418 services = self.services
420 serviceHandler = eServiceCenter.getInstance()
422 for service in services:
423 myref = eServiceReference(str(service))
424 if myref.flags & eServiceReference.isGroup:
425 mylist = serviceHandler.list(myref)
426 if mylist is not None:
430 # strip all after last :
432 pos = value.rfind(':')
434 value = value[:pos+1]
436 if value == override_service:
440 return override_service
442 def checkTimespan(self, begin):
443 return self.checkAnyTimespan(begin, *self.timespan)
445 def decrementCounter(self):
446 if self.matchCount and self.matchLeft > 0:
449 def getAfterEvent(self):
450 for afterevent in self.afterevent:
451 if afterevent[1][0] is None:
455 def getAfterEventTimespan(self, end):
456 for afterevent in self.afterevent:
457 if not self.checkAnyTimespan(end, *afterevent[1]):
464 return self.__class__(
469 timespan = self.timespan,
470 services = self.services,
471 offset = self.offset,
472 afterevent = self.afterevent,
473 exclude = (self.getExcludedTitle(), self.getExcludedShort(), self.getExcludedDescription(), self.getExcludedDays()),
474 maxduration = self.maxduration,
475 destination = self.destination,
476 include = (self.getIncludedTitle(), self.getIncludedShort(), self.getIncludedDescription(), self.getIncludedDays()),
477 matchCount = self.matchCount,
478 matchLeft = self.matchLeft,
479 matchLimit = self.matchLimit,
480 matchFormatString = self.matchFormatString,
481 lastBegin = self.lastBegin,
482 justplay = self.justplay,
483 avoidDuplicateDescription = self.avoidDuplicateDescription,
484 bouquets = self.bouquets,
486 encoding = self.encoding,
487 searchType = self.searchType,
488 searchCase = self.searchCase,
489 overrideAlternatives = self.overrideAlternatives
492 def __deepcopy__(self, memo):
493 return self.__class__(
498 timespan = self.timespan,
499 services = self.services[:],
500 offset = self.offset and self.offset[:],
501 afterevent = self.afterevent[:],
502 exclude = (self.getExcludedTitle(), self.getExcludedShort(), self.getExcludedDescription(), self.exclude[3][:]),
503 maxduration = self.maxduration,
504 destination = self.destination,
505 include = (self.getIncludedTitle(), self.getIncludedShort(), self.getIncludedDescription(), self.include[3][:]),
506 matchCount = self.matchCount,
507 matchLeft = self.matchLeft,
508 matchLimit = self.matchLimit,
509 matchFormatString = self.matchFormatString,
510 lastBegin = self.lastBegin,
511 justplay = self.justplay,
512 avoidDuplicateDescription = self.avoidDuplicateDescription,
513 bouquets = self.bouquets[:],
515 encoding = self.encoding,
516 searchType = self.searchType,
517 searchCase = self.searchCase,
518 overrideAlternatives = self.overrideAlternatives
521 def __eq__(self, other):
522 if isinstance(other, AutoTimerComponent):
523 return self.id == other.id
526 def __lt__(self, other):
527 if isinstance(other, AutoTimerComponent):
528 return self.name.lower() < other.name.lower()
531 def __ne__(self, other):
532 return not self.__eq__(other)
542 str(self.searchCase),
543 str(self.searchType),
547 str(self.afterevent),
548 str(([x.pattern for x in self.exclude[0]],
549 [x.pattern for x in self.exclude[1]],
550 [x.pattern for x in self.exclude[2]],
553 str(([x.pattern for x in self.include[0]],
554 [x.pattern for x in self.include[1]],
555 [x.pattern for x in self.include[2]],
558 str(self.maxduration),
560 str(self.destination),
561 str(self.matchCount),
563 str(self.matchLimit),
564 str(self.matchFormatString),
567 str(self.avoidDuplicateDescription),
570 str(self.overrideAlternatives),