051d78a441259bb3166dc51172adcd43bcde536b
[vuplus_dvbapp-plugin] / autotimer / src / AutoTimerComponent.py
1 # Format Counter
2 from time import strftime
3
4 # regular expression
5 from re import compile as re_compile
6
7 class AutoTimerComponent(object):
8         """AutoTimer Component which also handles validity checks"""
9
10         """
11          Initiate
12         """
13         def __init__(self, id, name, match, enabled, *args, **kwargs):
14                 self.id = id
15                 self._afterevent = []
16                 self.setValues(name, match, enabled, *args, **kwargs)
17
18         """
19          Unsets all Attributes
20         """
21         def clear(self, id = -1, enabled = False):
22                 self.id = id
23                 self.setValues('', '', enabled)
24
25         """
26          Create a deep copy of this instance
27         """
28         def clone(self):
29                 return self.__deepcopy__({})
30
31         """
32          Hook needed for WebIf
33         """
34         def getEntry(self):
35                 return self
36
37         """
38          Keeps init small and helps setting many values at once
39         """
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"):
45                 self.name = name
46                 self.match = match
47                 self.enabled = enabled
48                 self.timespan = timespan
49                 self.services = services
50                 self.offset = offset
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
68
69 ### Attributes / Properties
70
71         def setAfterEvent(self, afterevent):
72                 if afterevent is not self._afterevent:
73                         del self._afterevent[:]
74
75                 Len = len(afterevent)
76                 if Len:
77                         for x in range(0, Len):
78                                 action, timespan = afterevent[x]
79
80                                 # Remove original entry
81                                 del afterevent[x]
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,)))
87                                 else:
88                                         self._afterevent.insert(x, (action, self.calculateDayspan(*timespan)))
89
90         afterevent = property(lambda self: self._afterevent, setAfterEvent)
91
92         def setBouquets(self, bouquets):
93                 if bouquets:
94                         self._bouquets = bouquets
95                 else:
96                         self._bouquets = []
97
98         bouquets = property(lambda self: self._bouquets , setBouquets)
99
100         def setEncoding(self, encoding):
101                 if encoding:
102                         self._encoding = encoding
103
104         encoding = property(lambda self: self._encoding, setEncoding)
105
106         def setExclude(self, exclude):
107                 if exclude:
108                         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]],
112                                 exclude[3]
113                         )
114                 else:
115                         self._exclude = ([], [], [], [])
116
117         exclude = property(lambda self: self._exclude, setExclude)
118
119         def setInclude(self, include):
120                 if include:
121                         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]],
125                                 include[3]
126                         )
127                 else:
128                         self._include = ([], [], [], [])
129
130         include = property(lambda self: self._include, setInclude)
131
132         def setSearchCase(self, case):
133                 assert case in ("sensitive", "insensitive"), "search case must be sensitive or insensitive"
134                 self._searchCase = case
135
136         searchCase = property(lambda self: self._searchCase, setSearchCase)
137
138         def setSearchType(self, type):
139                 assert type in ("exact", "partial"), "search type must be exact or partial"
140                 self._searchType = type
141
142         searchType = property(lambda self: self._searchType, setSearchType)
143
144         def setServices(self, services):
145                 if services:
146                         self._services = services
147                 else:
148                         self._services = []
149
150         services = property(lambda self: self._services, setServices)
151
152         def setTimespan(self, timespan):
153                 if timespan is None or len(timespan) and timespan[0] is None:
154                         self._timespan = (None,)
155                 else:
156                         self._timespan = self.calculateDayspan(*timespan)
157
158         timespan = property(lambda self: self._timespan, setTimespan)
159
160 ### See if Attributes are set
161
162         def hasAfterEvent(self):
163                 return len(self.afterevent)
164
165         def hasAfterEventTimespan(self):
166                 for afterevent in self.afterevent:
167                         if afterevent[1][0] is not None:
168                                 return True
169                 return False
170
171         def hasCounter(self):
172                 return self.matchCount != 0
173
174         def hasCounterFormatString(self):
175                 return self.matchFormatString != ''
176
177         def hasDestination(self):
178                 return self.destination is not None
179
180         def hasDuration(self):
181                 return self.maxduration is not None
182
183         def hasTags(self):
184                 return len(self.tags) > 0
185
186         def hasTimespan(self):
187                 return self.timespan[0] is not None
188
189         def hasOffset(self):
190                 return self.offset is not None
191
192 ### Helper
193
194         """
195          Returns a tulple of (input begin, input end, begin earlier than end)
196         """
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)
200                 else:
201                         return (begin, end, False)
202
203         """
204          Returns if a given timestruct is in a timespan
205         """
206         def checkAnyTimespan(self, time, begin = None, end = None, haveDayspan = False):
207                 if begin is None:
208                         return False
209
210                 # Check if we span a day
211                 if haveDayspan:
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
215                                 return False
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
219                                 return False
220                         return True
221                 else:
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
225                                 return True
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
229                                 return True
230                         return False
231
232         """
233          Returns a list of all allowed services by listing the bouquets
234         """
235         def getFullServices(self):
236                 list = self.services[:]
237
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:
244                                 while 1:
245                                         s = mylist.getNext()
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 :-)
248                                         if s.valid():
249                                                 # strip all after last :
250                                                 value = s.toString()
251                                                 pos = value.rfind(':')
252                                                 if pos != -1:
253                                                         value = value[:pos+1]
254
255                                                 list.append(value)
256                                         else:
257                                                 break
258
259                 return list
260
261         """
262          Called when a timer based on this component was added
263         """
264         def update(self, begin, timestamp):
265                 # Only update limit when we have new begin
266                 if begin > self.lastBegin:
267                         self.lastBegin = begin
268
269                         # Update Counter:
270                         # %m is Month, %U is week (sunday), %W is week (monday)
271                         newLimit = strftime(self.matchFormatString, timestamp)
272
273                         if newLimit != self.matchLimit:
274                                 self.matchLeft = self.matchCount
275                                 self.matchLimit = newLimit
276
277 ### Makes saving Config easier
278
279         getAvoidDuplicateDescription = lambda self: self.avoidDuplicateDescription
280
281         getBouquets = lambda self: self._bouquets
282
283         getCompleteAfterEvent = lambda self: self._afterevent
284
285         getCounter = lambda self: self.matchCount
286         getCounterFormatString = lambda self: self.matchFormatString
287         getCounterLeft = lambda self: self.matchLeft
288         getCounterLimit = lambda self: self.matchLimit
289
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
292
293         getDuration = lambda self: self.maxduration/60
294
295         getEnabled = lambda self: self.enabled and "yes" or "no"
296
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]]
302
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]
308
309         getJustplay = lambda self: self.justplay and "1" or "0"
310
311         getLastBegin = lambda self: self.lastBegin
312
313         getMatch = lambda self: self.match
314         getName = lambda self: self.name
315
316         getOffsetBegin = lambda self: self.offset[0]/60
317         getOffsetEnd = lambda self: self.offset[1]/60
318
319         getServices = lambda self: self._services
320
321         getTags = lambda self: self.tags
322
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])
326
327         isOffsetEqual = lambda self: self.offset[0] == self.offset[1]
328
329 ### Actual functionality
330
331         def applyOffset(self, begin, end):
332                 if self.offset is None:
333                         return (begin, end)
334                 return (begin - self.offset[0], end + self.offset[1])
335
336         def checkCounter(self, timestamp):
337                 # 0-Count is considered "unset"
338                 if self.matchCount == 0:
339                         return False
340
341                 # Check if event is in current timespan (we can only manage one!)
342                 limit = strftime(self.matchFormatString, timestamp)
343                 if limit != self.matchLimit:
344                         return True
345
346                 if self.matchLeft > 0:
347                         return False
348                 return True
349
350         def checkDuration(self, length):
351                 if self.maxduration is None:
352                         return False
353                 return length > self.maxduration
354
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:
363                                 return True
364
365                 for exclude in self.exclude[0]:
366                         if exclude.search(title):
367                                 return True
368                 for exclude in self.exclude[1]:
369                         if exclude.search(short):
370                                 return True
371                 for exclude in self.exclude[2]:
372                         if exclude.search(extended):
373                                 return True
374                 return False
375
376         def checkFilter(self, title, short, extended, dayofweek):
377                 if self.checkExcluded(title, short, extended, dayofweek):
378                         return True
379
380                 return self.checkIncluded(title, short, extended, dayofweek)
381
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:
390                                 return True
391
392                 for include in self.include[0]:
393                         if not include.search(title):
394                                 return True
395                 for include in self.include[1]:
396                         if not include.search(short):
397                                 return True
398                 for include in self.include[2]:
399                         if not include.search(extended):
400                                 return True
401
402                 return False
403
404         def checkServices(self, service):
405                 services = self.services
406                 bouquets = self.bouquets
407                 if len(services) or len(bouquets):
408                         if service in services:
409                                 return False
410
411                         from enigma import eServiceReference, eServiceCenter
412                         serviceHandler = eServiceCenter.getInstance()
413                         for bouquet in bouquets:
414                                 myref = eServiceReference(str(bouquet))
415                                 mylist = serviceHandler.list(myref)
416                                 if mylist is not None:
417                                         while 1:
418                                                 s = mylist.getNext()
419                                                 # TODO: I wonder if its sane to assume we get services here (and not just new lists)
420                                                 # We can ignore markers & directorys here because they won't match any event's service :-)
421                                                 if s.valid():
422                                                         # strip all after last :
423                                                         value = s.toString()
424                                                         pos = value.rfind(':')
425                                                         if pos != -1:
426                                                                 value = value[:pos+1]
427
428                                                         if value == service:
429                                                                 return False
430                                                 else:
431                                                         break
432                         return True
433                 return False
434
435         def checkTimespan(self, begin):
436                 return self.checkAnyTimespan(begin, *self.timespan)
437
438         def decrementCounter(self):
439                 if self.matchCount and self.matchLeft > 0:
440                         self.matchLeft -= 1
441
442         def getAfterEvent(self):
443                 for afterevent in self.afterevent:
444                         if afterevent[1][0] is None:
445                                 return afterevent[0]
446                 return None
447
448         def getAfterEventTimespan(self, end):
449                 for afterevent in self.afterevent:
450                         if not self.checkAnyTimespan(end, *afterevent[1]):
451                                 return afterevent[0]
452                 return None
453
454 ### Misc
455
456         def __copy__(self):
457                 return self.__class__(
458                         self.id,
459                         self.name,
460                         self.match,
461                         self.enabled,
462                         timespan = self.timespan,
463                         services = self.services,
464                         offset = self.offset,
465                         afterevent = self.afterevent,
466                         exclude = (self.getExcludedTitle(), self.getExcludedShort(), self.getExcludedDescription(), self.getExcludedDays()),
467                         maxduration = self.maxduration,
468                         destination = self.destination,
469                         include = (self.getIncludedTitle(), self.getIncludedShort(), self.getIncludedDescription(), self.getIncludedDays()),
470                         matchCount = self.matchCount,
471                         matchLeft = self.matchLeft,
472                         matchLimit = self.matchLimit,
473                         matchFormatString = self.matchFormatString,
474                         lastBegin = self.lastBegin,
475                         justplay = self.justplay,
476                         avoidDuplicateDescription = self.avoidDuplicateDescription,
477                         bouquets = self.bouquets,
478                         tags = self.tags,
479                         encoding = self.encoding,
480                         searchType = self.searchType,
481                         searchCase = self.searchCase
482                 )
483
484         def __deepcopy__(self, memo):
485                 return self.__class__(
486                         self.id,
487                         self.name,
488                         self.match,
489                         self.enabled,
490                         timespan = self.timespan,
491                         services = self.services[:],
492                         offset = self.offset and self.offset[:],
493                         afterevent = self.afterevent[:],
494                         exclude = (self.getExcludedTitle(), self.getExcludedShort(), self.getExcludedDescription(), self.exclude[3][:]),
495                         maxduration = self.maxduration,
496                         destination = self.destination,
497                         include = (self.getIncludedTitle(), self.getIncludedShort(), self.getIncludedDescription(), self.include[3][:]),
498                         matchCount = self.matchCount,
499                         matchLeft = self.matchLeft,
500                         matchLimit = self.matchLimit,
501                         matchFormatString = self.matchFormatString,
502                         lastBegin = self.lastBegin,
503                         justplay = self.justplay,
504                         avoidDuplicateDescription = self.avoidDuplicateDescription,
505                         bouquets = self.bouquets[:],
506                         tags = self.tags[:],
507                         encoding = self.encoding,
508                         searchType = self.searchType,
509                         searchCase = self.searchCase
510                 )
511
512         def __eq__(self, other):
513                 if isinstance(other, AutoTimerComponent):
514                         return self.id == other.id
515                 return False
516
517         def __lt__(self, other):
518                 if isinstance(other, AutoTimerComponent):
519                         return self.name.lower() < other.name.lower()
520                 return False
521
522         def __ne__(self, other):
523                 return not self.__eq__(other)
524
525         def __repr__(self):
526                 return ''.join([
527                         '<AutomaticTimer ',
528                         self.name,
529                         ' (',
530                         ', '.join([
531                                         str(self.match),
532                                         str(self.encoding),
533                                         str(self.searchCase),
534                                         str(self.searchType),
535                                         str(self.timespan),
536                                         str(self.services),
537                                         str(self.offset),
538                                         str(self.afterevent),
539                                         str(([x.pattern for x in self.exclude[0]],
540                                                 [x.pattern for x in self.exclude[1]],
541                                                 [x.pattern for x in self.exclude[2]],
542                                                 self.exclude[3]
543                                         )),
544                                         str(([x.pattern for x in self.include[0]],
545                                                 [x.pattern for x in self.include[1]],
546                                                 [x.pattern for x in self.include[2]],
547                                                 self.include[3]
548                                         )),
549                                         str(self.maxduration),
550                                         str(self.enabled),
551                                         str(self.destination),
552                                         str(self.matchCount),
553                                         str(self.matchLeft),
554                                         str(self.matchLimit),
555                                         str(self.matchFormatString),
556                                         str(self.lastBegin),
557                                         str(self.justplay),
558                                         str(self.avoidDuplicateDescription),
559                                         str(self.bouquets),
560                                         str(self.tags)
561                          ]),
562                          ")>"
563                 ])
564