AutoTimer: add "Fast Scan" support
[vuplus_dvbapp-plugin] / autotimer / src / AutoTimerComponent.py
index 366560d..363e8e6 100644 (file)
@@ -1,41 +1,65 @@
-# Format Counter
+# Format counter
 from time import strftime
 
 # regular expression
 from re import compile as re_compile
 
+# Alternatives and service restriction
+from enigma import eServiceReference, eServiceCenter
+
+# To get preferred component
+from Components.config import config
+
 class AutoTimerComponent(object):
        """AutoTimer Component which also handles validity checks"""
 
-       def __init__(self, id, *args, **kwargs):
+       """
+        Initiate
+       """
+       def __init__(self, id, name, match, enabled, *args, **kwargs):
                self.id = id
                self._afterevent = []
-               self.setValues(*args, **kwargs)
-
-       def __eq__(self, other):
-               try:
-                       return self.id == other.id
-               except AttributeError:
-                       return False
-
-       def __ne__(self, other):
-               return not self.__eq__(other)
+               self.setValues(name, match, enabled, *args, **kwargs)
 
+       """
+        Unsets all Attributes
+       """
+       def clear(self, id = -1, enabled = False):
+               self.id = id
+               self.setValues('', '', enabled)
+
+       """
+        Create a deep copy of this instance
+       """
+       def clone(self):
+               return self.__deepcopy__({})
+
+       """
+        Hook needed for WebIf
+       """
+       def getEntry(self):
+               return self
+
+       """
+        Keeps init small and helps setting many values at once
+       """
        def setValues(self, name, match, enabled, timespan = None, services = None, offset = None, \
                        afterevent = [], exclude = None, maxduration = None, destination = None, \
                        include = None, matchCount = 0, matchLeft = 0, matchLimit = '', matchFormatString = '', \
-                       lastBegin = 0, justplay = False, avoidDuplicateDescription = False, bouquets = None):
+                       lastBegin = 0, justplay = False, avoidDuplicateDescription = 0, bouquets = None, \
+                       tags = None, encoding = 'UTF-8', searchType = "partial", searchCase = "insensitive", \
+                       overrideAlternatives = False):
                self.name = name
                self.match = match
+               self.enabled = enabled
                self.timespan = timespan
                self.services = services
                self.offset = offset
                self.afterevent = afterevent
                self.exclude = exclude
-               self.include = include
                self.maxduration = maxduration
-               self.enabled = enabled
                self.destination = destination
+               self.include = include
                self.matchCount = matchCount
                self.matchLeft = matchLeft
                self.matchLimit = matchLimit
@@ -44,23 +68,41 @@ class AutoTimerComponent(object):
                self.justplay = justplay
                self.avoidDuplicateDescription = avoidDuplicateDescription
                self.bouquets = bouquets
+               self.tags = tags or []
+               self.encoding = encoding
+               self.searchType = searchType
+               self.searchCase = searchCase
+               self.overrideAlternatives = overrideAlternatives
 
-       def calculateDayspan(self, begin, end):
-               if end[0] < begin[0] or (end[0] == begin[0] and end[1] <= begin[1]):
-                       return (begin, end, True)
+### Attributes / Properties
+
+       def setAfterEvent(self, afterevent):
+               if afterevent is not self._afterevent:
+                       del self._afterevent[:]
                else:
-                       return (begin, end, False)
+                       self._afterevent = []
 
-       def setTimespan(self, timespan):
-               if timespan is None:
-                       self._timespan = (None,)
+               for action, timespan in afterevent:
+                       if timespan is None or timespan[0] is None:
+                               self._afterevent.append((action, (None,)))
+                       else:
+                               self._afterevent.append((action, self.calculateDayspan(*timespan)))
+
+       afterevent = property(lambda self: self._afterevent, setAfterEvent)
+
+       def setBouquets(self, bouquets):
+               if bouquets:
+                       self._bouquets = bouquets
                else:
-                       self._timespan = self.calculateDayspan(*timespan)
+                       self._bouquets = []
 
-       def getTimespan(self):
-               return self._timespan
+       bouquets = property(lambda self: self._bouquets , setBouquets)
 
-       timespan = property(getTimespan, setTimespan)
+       def setEncoding(self, encoding):
+               if encoding:
+                       self._encoding = encoding
+
+       encoding = property(lambda self: self._encoding, setEncoding)
 
        def setExclude(self, exclude):
                if exclude:
@@ -73,10 +115,7 @@ class AutoTimerComponent(object):
                else:
                        self._exclude = ([], [], [], [])
 
-       def getExclude(self):
-               return self._exclude
-
-       exclude = property(getExclude, setExclude)
+       exclude = property(lambda self: self._exclude, setExclude)
 
        def setInclude(self, include):
                if include:
@@ -89,10 +128,19 @@ class AutoTimerComponent(object):
                else:
                        self._include = ([], [], [], [])
 
-       def getInclude(self):
-               return self._include
+       include = property(lambda self: self._include, setInclude)
+
+       def setSearchCase(self, case):
+               assert case in ("sensitive", "insensitive"), "search case must be sensitive or insensitive"
+               self._searchCase = case
+
+       searchCase = property(lambda self: self._searchCase, setSearchCase)
 
-       include = property(getInclude, setInclude)
+       def setSearchType(self, type):
+               assert type in ("exact", "partial"), "search type must be exact or partial"
+               self._searchType = type
+
+       searchType = property(lambda self: self._searchType, setSearchType)
 
        def setServices(self, services):
                if services:
@@ -100,46 +148,62 @@ class AutoTimerComponent(object):
                else:
                        self._services = []
 
-       def getServices(self):
-               return self._services
-
-       services = property(getServices, setServices)
+       services = property(lambda self: self._services, setServices)
 
-       def setBouquets(self, bouquets):
-               if bouquets:
-                       self._bouquets = bouquets
+       def setTimespan(self, timespan):
+               if timespan is None or timespan and timespan[0] is None:
+                       self._timespan = (None,)
                else:
-                       self._bouquets = []
+                       self._timespan = self.calculateDayspan(*timespan)
 
-       def getBouquets(self):
-               return self._bouquets
+       timespan = property(lambda self: self._timespan, setTimespan)
 
-       bouquets = property(getBouquets, setBouquets)
+### See if Attributes are set
 
-       def setAfterEvent(self, afterevent):
-               del self._afterevent[:]
-               if len(afterevent):
-                       for definition in afterevent:
-                               action, timespan = definition
-                               if timespan is None:
-                                       self._afterevent.append((action, (None,)))
-                               else:
-                                       self._afterevent.append((action, self.calculateDayspan(*timespan)))
+       def hasAfterEvent(self):
+               return len(self.afterevent)
+
+       def hasAfterEventTimespan(self):
+               for afterevent in self.afterevent:
+                       if afterevent[1][0] is not None:
+                               return True
+               return False
 
-       def getCompleteAfterEvent(self):
-               return self._afterevent
+       def hasCounter(self):
+               return self.matchCount != 0
 
-       afterevent = property(getCompleteAfterEvent, setAfterEvent)
+       def hasCounterFormatString(self):
+               return self.matchFormatString != ''
+
+       def hasDestination(self):
+               return self.destination is not None
+
+       def hasDuration(self):
+               return self.maxduration is not None
+
+       def hasTags(self):
+               return len(self.tags)
 
        def hasTimespan(self):
                return self.timespan[0] is not None
 
-       def getTimespanBegin(self):
-               return '%02d:%02d' % (self.timespan[0][0], self.timespan[0][1])
+       def hasOffset(self):
+               return self.offset is not None
+
+### Helper
 
-       def getTimespanEnd(self):
-               return '%02d:%02d' % (self.timespan[1][0], self.timespan[1][1])
+       """
+        Returns a tulple of (input begin, input end, begin earlier than end)
+       """
+       def calculateDayspan(self, begin, end, ignore = None):
+               if end[0] < begin[0] or (end[0] == begin[0] and end[1] <= begin[1]):
+                       return (begin, end, True)
+               else:
+                       return (begin, end, False)
 
+       """
+        Returns if a given timestruct is in a timespan
+       """
        def checkAnyTimespan(self, time, begin = None, end = None, haveDayspan = False):
                if begin is None:
                        return False
@@ -156,7 +220,7 @@ class AutoTimerComponent(object):
                                return False
                        return True
                else:
-                       # Check if event begins earlier than our timespan starts 
+                       # Check if event begins earlier than our timespan starts
                        if time.tm_hour < begin[0] or (time.tm_hour == begin[0] and time.tm_min < begin[1]):
                                # Its out of our timespan then
                                return True
@@ -166,79 +230,111 @@ class AutoTimerComponent(object):
                                return True
                        return False
 
-       def checkTimespan(self, begin):
-               return self.checkAnyTimespan(begin, *self.timespan)
+       """
+        Called when a timer based on this component was added
+       """
+       def update(self, begin, timestamp):
+               # Only update limit when we have new begin
+               if begin > self.lastBegin:
+                       self.lastBegin = begin
 
-       def hasDuration(self):
-               return self.maxduration is not None
+                       # Update Counter:
+                       # %m is Month, %U is week (sunday), %W is week (monday)
+                       newLimit = strftime(self.matchFormatString, timestamp)
 
-       def getDuration(self):
-               return self.maxduration/60
+                       if newLimit != self.matchLimit:
+                               self.matchLeft = self.matchCount
+                               self.matchLimit = newLimit
 
-       def checkDuration(self, length):
-               if self.maxduration is None:
-                       return False
-               return length > self.maxduration
+### Makes saving Config easier
 
-       def getFullServices(self):
-               list = self.services[:]
-
-               from enigma import eServiceReference, eServiceCenter
-               serviceHandler = eServiceCenter.getInstance()
-               for bouquet in self.bouquets:
-                       myref = eServiceReference(str(bouquet))
-                       mylist = serviceHandler.list(myref)
-                       if mylist is not None:
-                               while 1:
-                                       s = mylist.getNext()
-                                       # TODO: I wonder if its sane to assume we get services here (and not just new lists)
-                                       # We can ignore markers & directorys here because they won't match any event's service :-)
-                                       if s.valid():
-                                               # TODO: see if we need to check on custom names here
-                                               list.append(s.toString())
-                                       else:
-                                               break
-               
-               return list
-
-       def checkServices(self, service):
-               if len(self.services) or len(self.bouquets): 
-                       return service not in self.getFullServices()
-               return False
+       getAvoidDuplicateDescription = lambda self: self.avoidDuplicateDescription
+
+       getBouquets = lambda self: self._bouquets
+
+       getCompleteAfterEvent = lambda self: self._afterevent
+
+       getCounter = lambda self: self.matchCount
+       getCounterFormatString = lambda self: self.matchFormatString
+       getCounterLeft = lambda self: self.matchLeft
+       getCounterLimit = lambda self: self.matchLimit
+
+       # 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 ;-)
+       getDestination = lambda self: self.destination is not None
+
+       getDuration = lambda self: self.maxduration/60
+
+       getEnabled = lambda self: self.enabled and "yes" or "no"
+
+       getExclude = lambda self: self._exclude
+       getExcludedDays = lambda self: self.exclude[3]
+       getExcludedDescription = lambda self: [x.pattern for x in self.exclude[2]]
+       getExcludedShort = lambda self: [x.pattern for x in self.exclude[1]]
+       getExcludedTitle = lambda self: [x.pattern for x in self.exclude[0]]
+
+       getInclude = lambda self: self._include
+       getIncludedTitle = lambda self: [x.pattern for x in self.include[0]]
+       getIncludedShort = lambda self: [x.pattern for x in self.include[1]]
+       getIncludedDescription = lambda self: [x.pattern for x in self.include[2]]
+       getIncludedDays = lambda self: self.include[3]
+
+       getJustplay = lambda self: self.justplay and "1" or "0"
+
+       getLastBegin = lambda self: self.lastBegin
 
-       def getExcludedTitle(self):
-               return [x.pattern for x in self.exclude[0]]
+       getMatch = lambda self: self.match
+       getName = lambda self: self.name
 
-       def getExcludedShort(self):
-               return [x.pattern for x in self.exclude[1]]
+       getOffsetBegin = lambda self: self.offset[0]/60
+       getOffsetEnd = lambda self: self.offset[1]/60
 
-       def getExcludedDescription(self):
-               return [x.pattern for x in self.exclude[2]]
+       getOverrideAlternatives = lambda self: self.overrideAlternatives and "1" or "0"
 
-       def getExcludedDays(self):
-               return self.exclude[3]
+       getServices = lambda self: self._services
 
-       def getIncludedTitle(self):
-               return [x.pattern for x in self.include[0]]
+       getTags = lambda self: self.tags
+
+       getTimespan = lambda self: self._timespan
+       getTimespanBegin = lambda self: '%02d:%02d' % (self.timespan[0][0], self.timespan[0][1])
+       getTimespanEnd = lambda self: '%02d:%02d' % (self.timespan[1][0], self.timespan[1][1])
+
+       isOffsetEqual = lambda self: self.offset[0] == self.offset[1]
+
+### Actual functionality
+
+       def applyOffset(self, begin, end):
+               if self.offset is None:
+                       return (begin, end)
+               return (begin - self.offset[0], end + self.offset[1])
+
+       def checkCounter(self, timestamp):
+               # 0-Count is considered "unset"
+               if self.matchCount == 0:
+                       return False
 
-       def getIncludedShort(self):
-               return [x.pattern for x in self.include[1]]
+               # Check if event is in current timespan (we can only manage one!)
+               limit = strftime(self.matchFormatString, timestamp)
+               if limit != self.matchLimit:
+                       return True
 
-       def getIncludedDescription(self):
-               return [x.pattern for x in self.include[2]]
+               if self.matchLeft > 0:
+                       return False
+               return True
 
-       def getIncludedDays(self):
-               return self.include[3]
+       def checkDuration(self, length):
+               if self.maxduration is None:
+                       return False
+               return length > self.maxduration
 
        def checkExcluded(self, title, short, extended, dayofweek):
-               if len(self.exclude[3]):
-                       list = [x for x in self.exclude[3]]
-                       if "weekend" in list:
-                               list.extend(["5", "6"])
-                       if "weekday" in list:
-                               list.extend(["0", "1", "2", "3", "4"])
+               if self.exclude[3]:
+                       list = self.exclude[3]
                        if dayofweek in list:
                                return True
+                       if "weekend" in list and dayofweek in ("5", "6"):
+                               return True
+                       if "weekday" in list and dayofweek in ("0", "1", "2", "3", "4"):
+                               return True
 
                for exclude in self.exclude[0]:
                        if exclude.search(title):
@@ -251,13 +347,19 @@ class AutoTimerComponent(object):
                                return True
                return False
 
+       def checkFilter(self, title, short, extended, dayofweek):
+               if self.checkExcluded(title, short, extended, dayofweek):
+                       return True
+
+               return self.checkIncluded(title, short, extended, dayofweek)
+
        def checkIncluded(self, title, short, extended, dayofweek):
-               if len(self.include[3]):
-                       list = [x for x in self.include[3]]
+               if self.include[3]:
+                       list = self.include[3][:]
                        if "weekend" in list:
-                               list.extend(["5", "6"])
+                               list.extend(("5", "6"))
                        if "weekday" in list:
-                               list.extend(["0", "1", "2", "3", "4"])
+                               list.extend(("0", "1", "2", "3", "4"))
                        if dayofweek not in list:
                                return True
 
@@ -273,123 +375,192 @@ class AutoTimerComponent(object):
 
                return False
 
-       def checkFilter(self, title, short, extended, dayofweek):
-               if self.checkExcluded(title, short, extended, dayofweek):
+       def checkServices(self, check_service):
+               services = self.services
+               bouquets = self.bouquets
+               if services or bouquets:
+                       addbouquets = []
+
+                       for service in services:
+                               if service == check_service:
+                                       return False
+
+                               myref = eServiceReference(str(service))
+                               if myref.flags & eServiceReference.isGroup:
+                                       addbouquets.append(service)
+
+                       serviceHandler = eServiceCenter.getInstance()
+                       for bouquet in bouquets + addbouquets:
+                               myref = eServiceReference(str(bouquet))
+                               mylist = serviceHandler.list(myref)
+                               if mylist is not None:
+                                       while 1:
+                                               s = mylist.getNext()
+                                               # TODO: I wonder if its sane to assume we get services here (and not just new lists)
+                                               # We can ignore markers & directorys here because they won't match any event's service :-)
+                                               if s.valid():
+                                                       # strip all after last :
+                                                       value = s.toString()
+                                                       pos = value.rfind(':')
+                                                       if pos != -1:
+                                                               if value[pos-1] == ':':
+                                                                       pos -= 1
+                                                               value = value[:pos+1]
+
+                                                       if value == check_service:
+                                                               return False
+                                               else:
+                                                       break
                        return True
+               return False
 
-               return self.checkIncluded(title, short, extended, dayofweek)
-
-       def hasOffset(self):
-               return self.offset is not None
-
-       def isOffsetEqual(self):
-               return self.offset[0] == self.offset[1]
-
-       def applyOffset(self, begin, end):
-               if self.offset is None:
-                       return (begin, end)
-               return (begin - self.offset[0], end + self.offset[1])
-
-       def getOffsetBegin(self):
-               return self.offset[0]/60
-
-       def getOffsetEnd(self):
-               return self.offset[1]/60
+       """
+       Return alternative service including a given ref.
+       Note that this only works for alternatives that the autotimer is restricted to.
+       """
+       def getAlternative(self, override_service):
+               services = self.services
+               if services:
+                       serviceHandler = eServiceCenter.getInstance()
+
+                       for service in services:
+                               myref = eServiceReference(str(service))
+                               if myref.flags & eServiceReference.isGroup:
+                                       mylist = serviceHandler.list(myref)
+                                       if mylist is not None:
+                                               while 1:
+                                                       s = mylist.getNext()
+                                                       if s.valid():
+                                                               # strip all after last :
+                                                               value = s.toString()
+                                                               pos = value.rfind(':')
+                                                               if pos != -1:
+                                                                       if value[pos-1] == ':':
+                                                                               pos -= 1
+                                                                       value = value[:pos+1]
+
+                                                               if value == override_service:
+                                                                       return service
+                                                       else:
+                                                               break
+               return override_service
 
-       def hasAfterEvent(self):
-               return len(self.afterevent)
+       def checkTimespan(self, begin):
+               return self.checkAnyTimespan(begin, *self.timespan)
 
-       def hasAfterEventTimespan(self):
-               for afterevent in self.afterevent:
-                       if afterevent[1][0] is not None:
-                               return True
-               return False
+       def decrementCounter(self):
+               if self.matchCount and self.matchLeft > 0:
+                       self.matchLeft -= 1
 
-       def getAfterEventTimespan(self, end):
+       def getAfterEvent(self):
                for afterevent in self.afterevent:
-                       if not self.checkAnyTimespan(end, *afterevent[1]):
+                       if afterevent[1][0] is None:
                                return afterevent[0]
                return None
 
-       def getAfterEvent(self):
+       def getAfterEventTimespan(self, end):
                for afterevent in self.afterevent:
-                       if afterevent[1][0] is None:
+                       if not self.checkAnyTimespan(end, *afterevent[1]):
                                return afterevent[0]
                return None
 
-       def getEnabled(self):
-               return self.enabled and "yes" or "no"
-
-       def getJustplay(self):
-               return self.justplay and "1" or "0"
+### Misc
 
-       def hasDestination(self):
-               return self.destination is not None
-
-       def hasCounter(self):
-               return self.matchCount != 0
-
-       def hasCounterFormatString(self):
-               return self.matchFormatString != ''
-
-       def getCounter(self):
-               return self.matchCount
-
-       def getCounterLeft(self):
-               return self.matchLeft
-
-       def getCounterLimit(self):
-               return self.matchLimit
-
-       def getCounterFormatString(self):
-               return self.matchFormatString
-
-       def checkCounter(self, timestamp):
-               # 0-Count is considered "unset"
-               if self.matchCount == 0:
-                       return False
-
-               # Check if event is in current timespan (we can only manage one!)
-               limit = strftime(self.matchFormatString, timestamp)
-               if limit != self.matchLimit:
-                       return True
-
-               if self.matchLeft > 0:
-                       self.matchLeft -= 1
-                       return False
-               return True
-
-       def update(self, begin, timestamp):
-               # Only update limit when we have new begin
-               if begin > self.lastBegin:
-                       self.lastBegin = begin
-
-                       # Update Counter:
-                       # %m is Month, %U is week (sunday), %W is week (monday)
-                       newLimit = strftime(self.matchFormatString, timestamp)
+       def __copy__(self):
+               return self.__class__(
+                       self.id,
+                       self.name,
+                       self.match,
+                       self.enabled,
+                       timespan = self.timespan,
+                       services = self.services,
+                       offset = self.offset,
+                       afterevent = self.afterevent,
+                       exclude = (self.getExcludedTitle(), self.getExcludedShort(), self.getExcludedDescription(), self.getExcludedDays()),
+                       maxduration = self.maxduration,
+                       destination = self.destination,
+                       include = (self.getIncludedTitle(), self.getIncludedShort(), self.getIncludedDescription(), self.getIncludedDays()),
+                       matchCount = self.matchCount,
+                       matchLeft = self.matchLeft,
+                       matchLimit = self.matchLimit,
+                       matchFormatString = self.matchFormatString,
+                       lastBegin = self.lastBegin,
+                       justplay = self.justplay,
+                       avoidDuplicateDescription = self.avoidDuplicateDescription,
+                       bouquets = self.bouquets,
+                       tags = self.tags,
+                       encoding = self.encoding,
+                       searchType = self.searchType,
+                       searchCase = self.searchCase,
+                       overrideAlternatives = self.overrideAlternatives
+               )
+
+       def __deepcopy__(self, memo):
+               return self.__class__(
+                       self.id,
+                       self.name,
+                       self.match,
+                       self.enabled,
+                       timespan = self.timespan,
+                       services = self.services[:],
+                       offset = self.offset and self.offset[:],
+                       afterevent = self.afterevent[:],
+                       exclude = (self.getExcludedTitle(), self.getExcludedShort(), self.getExcludedDescription(), self.exclude[3][:]),
+                       maxduration = self.maxduration,
+                       destination = self.destination,
+                       include = (self.getIncludedTitle(), self.getIncludedShort(), self.getIncludedDescription(), self.include[3][:]),
+                       matchCount = self.matchCount,
+                       matchLeft = self.matchLeft,
+                       matchLimit = self.matchLimit,
+                       matchFormatString = self.matchFormatString,
+                       lastBegin = self.lastBegin,
+                       justplay = self.justplay,
+                       avoidDuplicateDescription = self.avoidDuplicateDescription,
+                       bouquets = self.bouquets[:],
+                       tags = self.tags[:],
+                       encoding = self.encoding,
+                       searchType = self.searchType,
+                       searchCase = self.searchCase,
+                       overrideAlternatives = self.overrideAlternatives
+               )
 
-                       if newLimit != self.matchLimit:
-                               self.matchLeft = self.matchCount
-                               self.matchLimit = newLimit
+       def __eq__(self, other):
+               if isinstance(other, AutoTimerComponent):
+                       return self.id == other.id
+               return False
 
-       def getLastBegin(self):
-               return self.lastBegin
+       def __lt__(self, other):
+               if isinstance(other, AutoTimerComponent):
+                       return self.name.lower() < other.name.lower()
+               return False
 
-       def getAvoidDuplicateDescription(self):
-               return self.avoidDuplicateDescription
+       def __ne__(self, other):
+               return not self.__eq__(other)
 
        def __repr__(self):
-               return ''.join([
+               return ''.join((
                        '<AutomaticTimer ',
                        self.name,
                        ' (',
-                       ', '.join([
+                       ', '.join((
                                        str(self.match),
+                                       str(self.encoding),
+                                       str(self.searchCase),
+                                       str(self.searchType),
                                        str(self.timespan),
                                        str(self.services),
                                        str(self.offset),
                                        str(self.afterevent),
-                                       str(self.exclude),
+                                       str(([x.pattern for x in self.exclude[0]],
+                                               [x.pattern for x in self.exclude[1]],
+                                               [x.pattern for x in self.exclude[2]],
+                                               self.exclude[3]
+                                       )),
+                                       str(([x.pattern for x in self.include[0]],
+                                               [x.pattern for x in self.include[1]],
+                                               [x.pattern for x in self.include[2]],
+                                               self.include[3]
+                                       )),
                                        str(self.maxduration),
                                        str(self.enabled),
                                        str(self.destination),
@@ -398,7 +569,103 @@ class AutoTimerComponent(object):
                                        str(self.matchLimit),
                                        str(self.matchFormatString),
                                        str(self.lastBegin),
-                                       str(self.justplay)
-                        ]),
+                                       str(self.justplay),
+                                       str(self.avoidDuplicateDescription),
+                                       str(self.bouquets),
+                                       str(self.tags),
+                                       str(self.overrideAlternatives),
+                        )),
                         ")>"
-               ])
+               ))
+
+class AutoTimerFastscanComponent(AutoTimerComponent):
+       def __init__(self, *args, **kwargs):
+               AutoTimerComponent.__init__(self, *args, **kwargs)
+               self._fastServices = None
+
+       def setBouquets(self, bouquets):
+               AutoTimerComponent.setBouquets(self, bouquets)
+               self._fastServices = None
+
+       def setServices(self, services):
+               AutoTimerComponent.setServices(self, services)
+               self._fastServices = None
+
+       def getFastServices(self):
+               if self._fastServices is None:
+                       fastServices = []
+                       append = fastServices.append
+                       addbouquets = []
+                       for service in self.services:
+                               myref = eServiceReference(str(service))
+                               if myref.flags & eServiceReference.isGroup:
+                                       addbouquets.append(service)
+                               else:
+                                       comp = service.split(':')
+                                       append(':'.join(comp[3:]))
+
+                       serviceHandler = eServiceCenter.getInstance()
+                       for bouquet in bouquets + addbouquets:
+                               myref = eServiceReference(str(bouquet))
+                               mylist = serviceHandler.list(myref)
+                               if mylist is not None:
+                                       while 1:
+                                               s = mylist.getNext()
+                                               # TODO: I wonder if its sane to assume we get services here (and not just new lists)
+                                               # We can ignore markers & directorys here because they won't match any event's service :-)
+                                               if s.valid():
+                                                       # strip all after last :
+                                                       value = s.toString()
+                                                       pos = value.rfind(':')
+                                                       if pos != -1:
+                                                               if value[pos-1] == ':':
+                                                                       pos -= 1
+                                                               value = value[:pos+1]
+
+                                                       comp = value.split(':')
+                                                       append(':'.join(value[3:]))
+                                               else:
+                                                       break
+                       self._fastServices = fastServices
+               return self._fastServices
+
+       def checkServices(self, check_service):
+               services = self.getFastServices()
+               if services:
+                       check = ':'.join(check_service.split(':')[3:])
+                       for service in services:
+                               if service == check:
+                                       return False # included
+                       return True # not included
+               return False # no restriction
+
+       def getAlternative(self, override_service):
+               services = self.services
+               if services:
+                       override = ':'.join(override_service.split(':')[3:])
+                       serviceHandler = eServiceCenter.getInstance()
+
+                       for service in services:
+                               myref = eServiceReference(str(service))
+                               if myref.flags & eServiceReference.isGroup:
+                                       mylist = serviceHandler.list(myref)
+                                       if mylist is not None:
+                                               while 1:
+                                                       s = mylist.getNext()
+                                                       if s.valid():
+                                                               # strip all after last :
+                                                               value = s.toString()
+                                                               pos = value.rfind(':')
+                                                               if pos != -1:
+                                                                       if value[pos-1] == ':':
+                                                                               pos -= 1
+                                                                       value = value[:pos+1]
+
+                                                               if ':'.join(value.split(':')[3:]) == override:
+                                                                       return service
+                                                       else:
+                                                               break
+               return override_service
+
+# very basic factory ;-)
+preferredAutoTimerComponent = lambda *args, **kwargs: AutoTimerFastscanComponent(*args, **kwargs) if config.plugins.autotimer.fastscan.value else AutoTimerComponent(*args, **kwargs)