def parseTimer(self, timer, epgcache, serviceHandler, recordHandler, checkEvtLimit, evtLimit, timers, conflicting, similars, skipped, timerdict, moviedict, simulateOnly=False): new = 0 modified = 0 # Search EPG, default to empty list epgmatches = epgcache.search( ('RITBDSE', 1000, typeMap[timer.searchType], timer.match, caseMap[timer.searchCase]) ) or [] # Sort list of tuples by begin time 'B' epgmatches.sort(key=itemgetter(3)) # Contains the the marked similar eits and the conflicting strings similardict = defaultdict(list) # Loop over all EPG matches for idx, ( serviceref, eit, name, begin, duration, shortdesc, extdesc ) in enumerate( epgmatches ): startLog() # timer destination dir dest = timer.destination or config.usage.default_path.value evtBegin = begin evtEnd = end = begin + duration doLog("possible epgmatch %s" % (name)) doLog("Serviceref %s" % (str(serviceref))) eserviceref = eServiceReference(serviceref) evt = epgcache.lookupEventId(eserviceref, eit) if not evt: doLog("Could not create Event!") skipped.append((name, begin, end, str(serviceref), timer.name, getLog())) continue # Try to determine real service (we always choose the last one) n = evt.getNumOfLinkageServices() if n > 0: i = evt.getLinkageService(eserviceref, n-1) serviceref = i.toString() doLog("Serviceref2 %s" % (str(serviceref))) # If event starts in less than 60 seconds skip it if begin < time() + 60: doLog("Skipping an event because it starts in less than 60 seconds") skipped.append((name, begin, end, serviceref, timer.name, getLog())) continue # Convert begin time timestamp = localtime(begin) # Update timer timer.update(begin, timestamp) # Check if eit is in similar matches list # NOTE: ignore evtLimit for similar timers as I feel this makes the feature unintuitive similarTimer = False if eit in similardict: similarTimer = True dayofweek = None # NOTE: ignore day on similar timer else: # If maximum days in future is set then check time if checkEvtLimit: if begin > evtLimit: doLog("Skipping an event because of maximum days in future is reached") skipped.append((name, begin, end, serviceref, timer.name, getLog())) continue dayofweek = str(timestamp.tm_wday) # Check timer conditions # NOTE: similar matches do not care about the day/time they are on, so ignore them if timer.checkServices(serviceref): doLog("Skipping an event because of check services") skipped.append((name, begin, end, serviceref, timer.name, getLog())) continue if timer.checkDuration(duration): doLog("Skipping an event because of duration check") skipped.append((name, begin, end, serviceref, timer.name, getLog())) continue if not similarTimer: if timer.checkTimespan(timestamp): doLog("Skipping an event because of timestamp check") skipped.append((name, begin, end, serviceref, timer.name, getLog())) continue if timer.checkTimeframe(begin): doLog("Skipping an event because of timeframe check") skipped.append((name, begin, end, serviceref, timer.name, getLog())) continue # Initialize newEntry = None oldExists = False # Eventually change service to alternative if timer.overrideAlternatives: serviceref = timer.getAlternative(serviceref) if timer.series_labeling and sp_getSeasonEpisode is not None: #doLog("Request name, desc, path %s %s %s" % (name,shortdesc,dest)) sp = sp_getSeasonEpisode(serviceref, name, evtBegin, evtEnd, shortdesc, dest) if sp and type(sp) in (tuple, list) and len(sp) == 4: name = sp[0] or name shortdesc = sp[1] or shortdesc dest = sp[2] or dest doLog(str(sp[3])) #doLog("Returned name, desc, path %s %s %s" % (name,shortdesc,dest)) else: # Nothing found doLog(str(sp)) # If AutoTimer name not equal match, do a second lookup with the name if timer.name.lower() != timer.match.lower(): #doLog("Request name, desc, path %s %s %s" % (timer.name,shortdesc,dest)) sp = sp_getSeasonEpisode(serviceref, timer.name, evtBegin, evtEnd, shortdesc, dest) if sp and type(sp) in (tuple, list) and len(sp) == 4: name = sp[0] or name shortdesc = sp[1] or shortdesc dest = sp[2] or dest doLog(str(sp[3])) #doLog("Returned name, desc, path %s %s %s" % (name,shortdesc,dest)) else: doLog(str(sp)) if timer.checkFilter(name, shortdesc, extdesc, dayofweek): doLog("Skipping an event because of filter check") skipped.append((name, begin, end, serviceref, timer.name, getLog())) continue if timer.hasOffset(): # Apply custom Offset begin, end = timer.applyOffset(begin, end) else: # Apply E2 Offset begin -= config.recording.margin_before.value * 60 end += config.recording.margin_after.value * 60 # Overwrite endtime if requested if timer.justplay and not timer.setEndtime: end = begin # Check for existing recordings in directory if timer.avoidDuplicateDescription == 3: # Reset movie Exists movieExists = False if dest and dest not in moviedict: self.addDirectoryToMovieDict(moviedict, dest, serviceHandler) for movieinfo in moviedict.get(dest, ()): if self.checkDuplicates(timer, name, movieinfo.get("name"), shortdesc, movieinfo.get("shortdesc"), extdesc, movieinfo.get("extdesc") ): doLog("We found a matching recorded movie, skipping event:", name) movieExists = True break if movieExists: doLog("Skipping an event because movie already exists") skipped.append((name, begin, end, serviceref, timer.name, getLog())) continue # Check for double Timers # We first check eit and if user wants us to guess event based on time # we try this as backup. The allowed diff should be configurable though. for rtimer in timerdict.get(serviceref, ()): if rtimer.eit == eit: oldExists = True doLog("We found a timer based on eit") newEntry = rtimer break elif config.plugins.autotimer.try_guessing.value: if timer.hasOffset(): # Remove custom Offset rbegin = rtimer.begin + timer.offset[0] * 60 rend = rtimer.end - timer.offset[1] * 60 else: # Remove E2 Offset rbegin = rtimer.begin + config.recording.margin_before.value * 60 rend = rtimer.end - config.recording.margin_after.value * 60 # As alternative we could also do a epg lookup #revent = epgcache.lookupEventId(rtimer.service_ref.ref, rtimer.eit) #rbegin = revent.getBeginTime() or 0 #rduration = revent.getDuration() or 0 #rend = rbegin + rduration or 0 if getTimeDiff(rbegin, rend, evtBegin, evtEnd) > ((duration/10)*8): oldExists = True doLog("We found a timer based on time guessing") newEntry = rtimer break if timer.avoidDuplicateDescription >= 1 \ and not rtimer.disabled: if self.checkDuplicates(timer, name, rtimer.name, shortdesc, rtimer.description, extdesc, rtimer.extdesc ): # if searchForDuplicateDescription > 1 then check short description oldExists = True doLog("We found a timer (similar service) with same description, skipping event") break # We found no timer we want to edit if newEntry is None: # But there is a match if oldExists: doLog("Skipping an event because a timer on same service exists") skipped.append((name, begin, end, serviceref, timer.name, getLog())) continue # We want to search for possible doubles if timer.avoidDuplicateDescription >= 2: for rtimer in chain.from_iterable( itervalues(timerdict) ): if not rtimer.disabled: if self.checkDuplicates(timer, name, rtimer.name, shortdesc, rtimer.description, extdesc, rtimer.extdesc ): oldExists = True doLog("We found a timer (any service) with same description, skipping event") break if oldExists: doLog("Skipping an event because a timer on any service exists") skipped.append((name, begin, end, serviceref, timer.name, getLog())) continue if timer.checkCounter(timestamp): doLog("Not adding new timer because counter is depleted.") skipped.append((name, begin, end, serviceref, timer.name, getLog())) continue # Append to timerlist and abort if simulating timers.append((name, begin, end, serviceref, timer.name, getLog())) if simulateOnly: continue if newEntry is not None: # Abort if we don't want to modify timers or timer is repeated if config.plugins.autotimer.refresh.value == "none" or newEntry.repeated: doLog("Won't modify existing timer because either no modification allowed or repeated timer") continue if hasattr(newEntry, "isAutoTimer"): msg = "[AutoTimer] AutoTimer %s modified this automatically generated timer." % (timer.name) doLog(msg) newEntry.log(501, msg) elif config.plugins.autotimer.add_autotimer_to_tags.value and TAG in newEntry.tags: msg = "[AutoTimer] AutoTimer %s modified this automatically generated timer." % (timer.name) doLog(msg) newEntry.log(501, msg) else: if config.plugins.autotimer.refresh.value != "all": doLog("Won't modify existing timer because it's no timer set by us") continue msg = "[AutoTimer] Warning, AutoTimer %s messed with a timer which might not belong to it: %s ." % (timer.name, newEntry.name) doLog(msg) newEntry.log(501, msg) modified += 1 self.modifyTimer(newEntry, name, shortdesc, begin, end, serviceref, eit) msg = "[AutoTimer] AutoTimer modified timer: %s ." % (newEntry.name) doLog(msg) newEntry.log(501, msg) else: newEntry = RecordTimerEntry(ServiceReference(serviceref), begin, end, name, shortdesc, eit) msg = "[AutoTimer] Try to add new timer based on AutoTimer %s." % (timer.name) doLog(msg) newEntry.log(500, msg) # Mark this entry as AutoTimer (only AutoTimers will have this Attribute set) # It is only temporarily, after a restart it will be lost, # because it won't be stored in the timer xml file newEntry.isAutoTimer = True # Apply afterEvent if timer.hasAfterEvent(): afterEvent = timer.getAfterEventTimespan(localtime(end)) if afterEvent is None: afterEvent = timer.getAfterEvent() if afterEvent is not None: newEntry.afterEvent = afterEvent newEntry.dirname = dest newEntry.calculateFilename() newEntry.justplay = timer.justplay newEntry.vpsplugin_enabled = timer.vps_enabled newEntry.vpsplugin_overwrite = timer.vps_overwrite tags = timer.tags[:] if config.plugins.autotimer.add_autotimer_to_tags.value: if TAG not in tags: tags.append(TAG) if config.plugins.autotimer.add_name_to_tags.value: tagname = timer.name.strip() if tagname: tagname = tagname[0].upper() + tagname[1:].replace(" ", "_") if tagname not in tags: tags.append(tagname) newEntry.tags = tags if oldExists: # XXX: this won't perform a sanity check, but do we actually want to do so? recordHandler.timeChanged(newEntry) else: conflictString = "" if similarTimer: conflictString = similardict[eit].conflictString msg = "[AutoTimer] Try to add similar Timer because of conflicts with %s." % (conflictString) doLog(msg) newEntry.log(504, msg) # Try to add timer conflicts = recordHandler.record(newEntry) if conflicts: # Maybe use newEntry.log conflictString += ' / '.join(["%s (%s)" % (x.name, strftime("%Y%m%d %H%M", localtime(x.begin))) for x in conflicts]) doLog("conflict with %s detected" % (conflictString)) if config.plugins.autotimer.addsimilar_on_conflict.value: # We start our search right after our actual index # Attention we have to use a copy of the list, because we have to append the previous older matches lepgm = len(epgmatches) for i in xrange(lepgm): servicerefS, eitS, nameS, beginS, durationS, shortdescS, extdescS = epgmatches[ (i+idx+1)%lepgm ] if self.checkDuplicates(timer, name, nameS, shortdesc, shortdescS, extdesc, extdescS, force=True ): # Check if the similar is already known if eitS not in similardict: doLog("Found similar Timer: " + name) # Store the actual and similar eit and conflictString, so it can be handled later newEntry.conflictString = conflictString similardict[eit] = newEntry similardict[eitS] = newEntry similarTimer = True if beginS <= evtBegin: # Event is before our actual epgmatch so we have to append it to the epgmatches list epgmatches.append((servicerefS, eitS, nameS, beginS, durationS, shortdescS, extdescS)) # If we need a second similar it will be found the next time else: similarTimer = False newEntry = similardict[eitS] break if conflicts is None: timer.decrementCounter() new += 1 newEntry.extdesc = extdesc timerdict[serviceref].append(newEntry) # Similar timers are in new timers list and additionally in similar timers list if similarTimer: similars.append((name, begin, end, serviceref, timer.name)) similardict.clear() # Don't care about similar timers elif not similarTimer: conflicting.append((name, begin, end, serviceref, timer.name)) if config.plugins.autotimer.disabled_on_conflict.value: msg = "[AutoTimer] Timer disabled because of conflicts with %s." % (conflictString) doLog(msg) newEntry.log(503, msg) newEntry.disabled = True # We might want to do the sanity check locally so we don't run it twice - but I consider this workaround a hack anyway conflicts = recordHandler.record(newEntry) if sp_showResult is not None: sp_showResult() return (new, modified)
def parseTimer(self, timer, epgcache, serviceHandler, recordHandler, checkEvtLimit, evtLimit, timers, conflicting, similars, skipped, timerdict, moviedict, simulateOnly=False): new = 0 modified = 0 # Search EPG, default to empty list epgmatches = epgcache.search( ('RITBDSE', 1000, typeMap[timer.searchType], timer.match, caseMap[timer.searchCase])) or [] # Sort list of tuples by begin time 'B' epgmatches.sort(key=itemgetter(3)) # Contains the the marked similar eits and the conflicting strings similardict = defaultdict(list) # Loop over all EPG matches for idx, (serviceref, eit, name, begin, duration, shortdesc, extdesc) in enumerate(epgmatches): startLog() # timer destination dir dest = timer.destination or config.usage.default_path.value evtBegin = begin evtEnd = end = begin + duration doLog("possible epgmatch %s" % (name)) doLog("Serviceref %s" % (str(serviceref))) eserviceref = eServiceReference(serviceref) evt = epgcache.lookupEventId(eserviceref, eit) if not evt: doLog("Could not create Event!") skipped.append( (name, begin, end, str(serviceref), timer.name, getLog())) continue # Try to determine real service (we always choose the last one) n = evt.getNumOfLinkageServices() if n > 0: i = evt.getLinkageService(eserviceref, n - 1) serviceref = i.toString() doLog("Serviceref2 %s" % (str(serviceref))) # If event starts in less than 60 seconds skip it if begin < time() + 60: doLog( "Skipping an event because it starts in less than 60 seconds" ) skipped.append( (name, begin, end, serviceref, timer.name, getLog())) continue # Convert begin time timestamp = localtime(begin) # Update timer timer.update(begin, timestamp) # Check if eit is in similar matches list # NOTE: ignore evtLimit for similar timers as I feel this makes the feature unintuitive similarTimer = False if eit in similardict: similarTimer = True dayofweek = None # NOTE: ignore day on similar timer else: # If maximum days in future is set then check time if checkEvtLimit: if begin > evtLimit: doLog( "Skipping an event because of maximum days in future is reached" ) skipped.append((name, begin, end, serviceref, timer.name, getLog())) continue dayofweek = str(timestamp.tm_wday) # Check timer conditions # NOTE: similar matches do not care about the day/time they are on, so ignore them if timer.checkServices(serviceref): doLog("Skipping an event because of check services") skipped.append( (name, begin, end, serviceref, timer.name, getLog())) continue if timer.checkDuration(duration): doLog("Skipping an event because of duration check") skipped.append( (name, begin, end, serviceref, timer.name, getLog())) continue if not similarTimer: if timer.checkTimespan(timestamp): doLog("Skipping an event because of timestamp check") skipped.append( (name, begin, end, serviceref, timer.name, getLog())) continue if timer.checkTimeframe(begin): doLog("Skipping an event because of timeframe check") skipped.append( (name, begin, end, serviceref, timer.name, getLog())) continue # Initialize newEntry = None oldExists = False # Eventually change service to alternative if timer.overrideAlternatives: serviceref = timer.getAlternative(serviceref) if timer.series_labeling and sp_getSeasonEpisode is not None: #doLog("Request name, desc, path %s %s %s" % (name,shortdesc,dest)) sp = sp_getSeasonEpisode(serviceref, name, evtBegin, evtEnd, shortdesc, dest) if sp and type(sp) in (tuple, list) and len(sp) == 4: name = sp[0] or name shortdesc = sp[1] or shortdesc dest = sp[2] or dest doLog(str(sp[3])) #doLog("Returned name, desc, path %s %s %s" % (name,shortdesc,dest)) else: # Nothing found doLog(str(sp)) # If AutoTimer name not equal match, do a second lookup with the name if timer.name.lower() != timer.match.lower(): #doLog("Request name, desc, path %s %s %s" % (timer.name,shortdesc,dest)) sp = sp_getSeasonEpisode(serviceref, timer.name, evtBegin, evtEnd, shortdesc, dest) if sp and type(sp) in (tuple, list) and len(sp) == 4: name = sp[0] or name shortdesc = sp[1] or shortdesc dest = sp[2] or dest doLog(str(sp[3])) #doLog("Returned name, desc, path %s %s %s" % (name,shortdesc,dest)) else: doLog(str(sp)) if timer.checkFilter(name, shortdesc, extdesc, dayofweek): doLog("Skipping an event because of filter check") skipped.append( (name, begin, end, serviceref, timer.name, getLog())) continue if timer.hasOffset(): # Apply custom Offset begin, end = timer.applyOffset(begin, end) else: # Apply E2 Offset begin -= config.recording.margin_before.value * 60 end += config.recording.margin_after.value * 60 # Overwrite endtime if requested if timer.justplay and not timer.setEndtime: end = begin # Check for existing recordings in directory if timer.avoidDuplicateDescription == 3: # Reset movie Exists movieExists = False if dest and dest not in moviedict: self.addDirectoryToMovieDict(moviedict, dest, serviceHandler) for movieinfo in moviedict.get(dest, ()): if self.checkDuplicates(timer, name, movieinfo.get("name"), shortdesc, movieinfo.get("shortdesc"), extdesc, movieinfo.get("extdesc")): doLog( "We found a matching recorded movie, skipping event:", name) movieExists = True break if movieExists: doLog("Skipping an event because movie already exists") skipped.append( (name, begin, end, serviceref, timer.name, getLog())) continue # Check for double Timers # We first check eit and if user wants us to guess event based on time # we try this as backup. The allowed diff should be configurable though. for rtimer in timerdict.get(serviceref, ()): if rtimer.eit == eit: oldExists = True doLog("We found a timer based on eit") newEntry = rtimer break elif config.plugins.autotimer.try_guessing.value: if timer.hasOffset(): # Remove custom Offset rbegin = rtimer.begin + timer.offset[0] * 60 rend = rtimer.end - timer.offset[1] * 60 else: # Remove E2 Offset rbegin = rtimer.begin + config.recording.margin_before.value * 60 rend = rtimer.end - config.recording.margin_after.value * 60 # As alternative we could also do a epg lookup #revent = epgcache.lookupEventId(rtimer.service_ref.ref, rtimer.eit) #rbegin = revent.getBeginTime() or 0 #rduration = revent.getDuration() or 0 #rend = rbegin + rduration or 0 if getTimeDiff(rbegin, rend, evtBegin, evtEnd) > ((duration / 10) * 8): oldExists = True doLog("We found a timer based on time guessing") newEntry = rtimer break if timer.avoidDuplicateDescription >= 1 \ and not rtimer.disabled: if self.checkDuplicates(timer, name, rtimer.name, shortdesc, rtimer.description, extdesc, rtimer.extdesc): # if searchForDuplicateDescription > 1 then check short description oldExists = True doLog( "We found a timer (similar service) with same description, skipping event" ) break # We found no timer we want to edit if newEntry is None: # But there is a match if oldExists: doLog( "Skipping an event because a timer on same service exists" ) skipped.append( (name, begin, end, serviceref, timer.name, getLog())) continue # We want to search for possible doubles if timer.avoidDuplicateDescription >= 2: for rtimer in chain.from_iterable(itervalues(timerdict)): if not rtimer.disabled: if self.checkDuplicates(timer, name, rtimer.name, shortdesc, rtimer.description, extdesc, rtimer.extdesc): oldExists = True doLog( "We found a timer (any service) with same description, skipping event" ) break if oldExists: doLog( "Skipping an event because a timer on any service exists" ) skipped.append((name, begin, end, serviceref, timer.name, getLog())) continue if timer.checkCounter(timestamp): doLog("Not adding new timer because counter is depleted.") skipped.append( (name, begin, end, serviceref, timer.name, getLog())) continue # Append to timerlist and abort if simulating timers.append((name, begin, end, serviceref, timer.name, getLog())) if simulateOnly: continue if newEntry is not None: # Abort if we don't want to modify timers or timer is repeated if config.plugins.autotimer.refresh.value == "none" or newEntry.repeated: doLog( "Won't modify existing timer because either no modification allowed or repeated timer" ) continue if hasattr(newEntry, "isAutoTimer"): msg = "[AutoTimer] AutoTimer %s modified this automatically generated timer." % ( timer.name) doLog(msg) newEntry.log(501, msg) elif config.plugins.autotimer.add_autotimer_to_tags.value and TAG in newEntry.tags: msg = "[AutoTimer] AutoTimer %s modified this automatically generated timer." % ( timer.name) doLog(msg) newEntry.log(501, msg) else: if config.plugins.autotimer.refresh.value != "all": doLog( "Won't modify existing timer because it's no timer set by us" ) continue msg = "[AutoTimer] Warning, AutoTimer %s messed with a timer which might not belong to it: %s ." % ( timer.name, newEntry.name) doLog(msg) newEntry.log(501, msg) modified += 1 self.modifyTimer(newEntry, name, shortdesc, begin, end, serviceref, eit) msg = "[AutoTimer] AutoTimer modified timer: %s ." % ( newEntry.name) doLog(msg) newEntry.log(501, msg) else: newEntry = RecordTimerEntry(ServiceReference(serviceref), begin, end, name, shortdesc, eit) msg = "[AutoTimer] Try to add new timer based on AutoTimer %s." % ( timer.name) doLog(msg) newEntry.log(500, msg) # Mark this entry as AutoTimer (only AutoTimers will have this Attribute set) # It is only temporarily, after a restart it will be lost, # because it won't be stored in the timer xml file newEntry.isAutoTimer = True # Apply afterEvent if timer.hasAfterEvent(): afterEvent = timer.getAfterEventTimespan(localtime(end)) if afterEvent is None: afterEvent = timer.getAfterEvent() if afterEvent is not None: newEntry.afterEvent = afterEvent newEntry.dirname = dest newEntry.calculateFilename() newEntry.justplay = timer.justplay newEntry.vpsplugin_enabled = timer.vps_enabled newEntry.vpsplugin_overwrite = timer.vps_overwrite tags = timer.tags[:] if config.plugins.autotimer.add_autotimer_to_tags.value: if TAG not in tags: tags.append(TAG) if config.plugins.autotimer.add_name_to_tags.value: tagname = timer.name.strip() if tagname: tagname = tagname[0].upper() + tagname[1:].replace( " ", "_") if tagname not in tags: tags.append(tagname) newEntry.tags = tags if oldExists: # XXX: this won't perform a sanity check, but do we actually want to do so? recordHandler.timeChanged(newEntry) else: conflictString = "" if similarTimer: conflictString = similardict[eit].conflictString msg = "[AutoTimer] Try to add similar Timer because of conflicts with %s." % ( conflictString) doLog(msg) newEntry.log(504, msg) # Try to add timer conflicts = recordHandler.record(newEntry) if conflicts: # Maybe use newEntry.log conflictString += ' / '.join([ "%s (%s)" % (x.name, strftime("%Y%m%d %H%M", localtime(x.begin))) for x in conflicts ]) doLog("conflict with %s detected" % (conflictString)) if config.plugins.autotimer.addsimilar_on_conflict.value: # We start our search right after our actual index # Attention we have to use a copy of the list, because we have to append the previous older matches lepgm = len(epgmatches) for i in xrange(lepgm): servicerefS, eitS, nameS, beginS, durationS, shortdescS, extdescS = epgmatches[ (i + idx + 1) % lepgm] if self.checkDuplicates(timer, name, nameS, shortdesc, shortdescS, extdesc, extdescS, force=True): # Check if the similar is already known if eitS not in similardict: doLog("Found similar Timer: " + name) # Store the actual and similar eit and conflictString, so it can be handled later newEntry.conflictString = conflictString similardict[eit] = newEntry similardict[eitS] = newEntry similarTimer = True if beginS <= evtBegin: # Event is before our actual epgmatch so we have to append it to the epgmatches list epgmatches.append( (servicerefS, eitS, nameS, beginS, durationS, shortdescS, extdescS)) # If we need a second similar it will be found the next time else: similarTimer = False newEntry = similardict[eitS] break if conflicts is None: timer.decrementCounter() new += 1 newEntry.extdesc = extdesc timerdict[serviceref].append(newEntry) # Similar timers are in new timers list and additionally in similar timers list if similarTimer: similars.append( (name, begin, end, serviceref, timer.name)) similardict.clear() # Don't care about similar timers elif not similarTimer: conflicting.append( (name, begin, end, serviceref, timer.name)) if config.plugins.autotimer.disabled_on_conflict.value: msg = "[AutoTimer] Timer disabled because of conflicts with %s." % ( conflictString) doLog(msg) newEntry.log(503, msg) newEntry.disabled = True # We might want to do the sanity check locally so we don't run it twice - but I consider this workaround a hack anyway conflicts = recordHandler.record(newEntry) if sp_showResult is not None: sp_showResult() return (new, modified)
def parseTimer(self, timer, epgcache, serviceHandler, recordHandler, checkEvtLimit, evtLimit, timers, conflicting, similars, skipped, timerdict, moviedict, simulateOnly=False): new = 0 modified = 0 # Precompute timer destination dir dest = timer.destination or config.usage.default_path.value # Workaround to allow search for umlauts if we know the encoding #match = timer.match match = timer.match.replace('\xc2\x86', '').replace('\xc2\x87', '') if timer.encoding != 'UTF-8': try: match = match.decode('UTF-8').encode(timer.encoding) except UnicodeDecodeError: pass if timer.searchType == "description": epgmatches = [] mask = (eServiceReference.isMarker | eServiceReference.isDirectory) casesensitive = timer.searchCase == "sensitive" if not casesensitive: match = match.lower() # Service filter defined # Search only using the specified services test = [(service, 0, -1, -1) for service in timer.services] for bouquet in timer.bouquets: services = serviceHandler.list(eServiceReference(bouquet)) if not services is None: while True: service = services.getNext() if not service.valid(): #check end of list break if not (service.flags & mask): test.append( (service.toString(), 0, -1, -1 ) ) if not test: # No service filter defined # Search within all services - could be very slow # Get all bouquets bouquetlist = [] if config.usage.multibouquet.value: refstr = '1:7:1:0:0:0:0:0:0:0:FROM BOUQUET "bouquets.tv" ORDER BY bouquet' bouquetroot = eServiceReference(refstr) bouquets = serviceHandler.list(bouquetroot) if bouquets: while True: s = bouquets.getNext() if not s.valid(): break if s.flags & eServiceReference.isDirectory: info = serviceHandler.info(s) if info: bouquetlist.append((info.getName(s), s)) mask = (eServiceReference.isMarker | eServiceReference.isDirectory) for name, bouquet in bouquetlist: if not bouquet.valid(): #check end of list break if bouquet.flags & eServiceReference.isDirectory: services = serviceHandler.list(bouquet) if not services is None: while True: service = services.getNext() if not service.valid(): #check end of list break if not (service.flags & mask): test.append( (service.toString(), 0, -1, -1 ) ) else: service_types_tv = '1:7:1:0:0:0:0:0:0:0:(type == 1) || (type == 17) || (type == 22) || (type == 25) || (type == 134) || (type == 195)' refstr = '%s FROM BOUQUET "userbouquet.favourites.tv" ORDER BY bouquet'%(service_types_tv) bouquetroot = eServiceReference(refstr) info = serviceHandler.info(bouquetroot) if info: bouquetlist.append((info.getName(bouquetroot), bouquetroot)) mask = (eServiceReference.isMarker | eServiceReference.isDirectory) for name, bouquet in bouquetlist: if bouquet.flags & eServiceReference.isDirectory: services = serviceHandler.list(bouquet) if not services is None: while True: service = services.getNext() if not service.valid(): #check end of list break if not (service.flags & mask): test.append( (service.toString(), 0, -1, -1 ) ) if test: # Get all events # eEPGCache.lookupEvent( [ format of the returned tuples, ( service, 0 = event intersects given start_time, start_time -1 for now_time), ] ) test.insert(0, 'RITBDSE') allevents = epgcache.lookupEvent(test) or [] # Filter events for serviceref, eit, name, begin, duration, shortdesc, extdesc in allevents: if match in (shortdesc if casesensitive else shortdesc.lower()) \ or match in (extdesc if casesensitive else extdesc.lower()): epgmatches.append( (serviceref, eit, name, begin, duration, shortdesc, extdesc) ) else: # Search EPG, default to empty list epgmatches = epgcache.search( ('RITBDSE', 2000, typeMap[timer.searchType], match, caseMap[timer.searchCase]) ) or [] # Sort list of tuples by begin time 'B' epgmatches.sort(key=itemgetter(3)) # Contains the the marked similar eits and the conflicting strings similardict = defaultdict(list) # Loop over all EPG matches for idx, ( serviceref, eit, name, begin, duration, shortdesc, extdesc ) in enumerate( epgmatches ): startLog() evtBegin = begin evtEnd = end = begin + duration doLog("[AutoTimer] possible epgmatch %s" % (name)) doLog("[AutoTimer] Serviceref %s" % (str(serviceref))) eserviceref = eServiceReference(serviceref) evt = epgcache.lookupEventId(eserviceref, eit) if not evt: doLog("[AutoTimer] Could not create Event!") skipped.append((name, begin, end, str(serviceref), timer.name, getLog())) continue # Try to determine real service (we always choose the last one) n = evt.getNumOfLinkageServices() if n > 0: i = evt.getLinkageService(eserviceref, n-1) doLog("[AutoTimer] Serviceref2 %s" % (str(serviceref))) serviceref = i.toString() evtBegin = begin evtEnd = end = begin + duration # If event starts in less than 60 seconds skip it if begin < time() + 60: doLog("[AutoTimer] Skipping an event because it starts in less than 60 seconds") skipped.append((name, begin, end, serviceref, timer.name, getLog())) continue # Set short description to equal extended description if it is empty. if not shortdesc: shortdesc = extdesc # Convert begin time timestamp = localtime(begin) # Update timer timer.update(begin, timestamp) # Check if eit is in similar matches list # NOTE: ignore evtLimit for similar timers as I feel this makes the feature unintuitive similarTimer = False if eit in similardict: similarTimer = True dayofweek = None # NOTE: ignore day on similar timer else: # If maximum days in future is set then check time if checkEvtLimit: if begin > evtLimit: doLog("[AutoTimer] Skipping an event because of maximum days in future is reached") skipped.append((name, begin, end, serviceref, timer.name, getLog())) continue dayofweek = str(timestamp.tm_wday) # Check timer conditions # NOTE: similar matches do not care about the day/time they are on, so ignore them if timer.checkServices(serviceref): doLog("[AutoTimer] Skipping an event because of check services") skipped.append((name, begin, end, serviceref, timer.name, getLog())) continue if timer.checkDuration(duration): doLog("[AutoTimer] Skipping an event because of duration check") skipped.append((name, begin, end, serviceref, timer.name, getLog())) continue if not similarTimer: if timer.checkTimespan(timestamp): doLog("[AutoTimer] Skipping an event because of timestamp check") skipped.append((name, begin, end, serviceref, timer.name, getLog())) continue if timer.checkTimeframe(begin): doLog("[AutoTimer] Skipping an event because of timeframe check") skipped.append((name, begin, end, serviceref, timer.name, getLog())) continue # Initialize newEntry = None oldExists = False dirname = None # Eventually change service to alternative if timer.overrideAlternatives: serviceref = timer.getAlternative(serviceref) if timer.series_labeling and sp_getSeasonEpisode is not None: #doLog("[AutoTimer SeriesPlugin] Request name, desc, path %s %s %s" % (name,shortdesc,dest)) sp = sp_getSeasonEpisode(serviceref, name, evtBegin, evtEnd, shortdesc, dest) if sp and type(sp) in (tuple, list) and len(sp) == 4: name = sp[0] shortdesc = sp[1] dirname = sp[2] doLog(str(sp[3])) #doLog("[AutoTimer SeriesPlugin] Returned name, desc, path %s %s %s" % (name,shortdesc,dirname)) else: # Nothing found doLog(str(sp)) # If AutoTimer name not equal match, do a second lookup with the name if timer.name.lower() != timer.match.lower(): sp = sp_getSeasonEpisode(serviceref, timer.name, evtBegin, evtEnd, shortdesc, dest) if sp and type(sp) in (tuple, list) and len(sp) == 4: name = sp[0] shortdesc = sp[1] dirname = sp[2] doLog(str(sp[3])) #doLog("Returned name, desc, path %s %s %s" % (name,shortdesc,dirname)) else: doLog(str(sp)) if timer.checkFilter(name, shortdesc, extdesc, dayofweek): doLog("[AutoTimer] Skipping an event because of filter check") skipped.append((name, begin, end, serviceref, timer.name, getLog())) continue if timer.hasOffset(): # Apply custom Offset begin, end = timer.applyOffset(begin, end) else: # Apply E2 Offset begin -= config.recording.margin_before.value * 60 end += config.recording.margin_after.value * 60 # Overwrite endtime if requested if timer.justplay and not timer.setEndtime: end = begin # Check for existing recordings in directory if timer.avoidDuplicateDescription == 3: # Reset movie Exists movieExists = False if dest and dest not in moviedict: self.addDirectoryToMovieDict(moviedict, dest, serviceHandler) for movieinfo in moviedict.get(dest, ()): if self.checkDuplicates(timer, name, movieinfo.get("name"), shortdesc, movieinfo.get("shortdesc"), extdesc, movieinfo.get("extdesc") ): doLog("[AutoTimer] We found a matching recorded movie, skipping event:", name) movieExists = True break if movieExists: doLog("[AutoTimer] Skipping an event because movie already exists") skipped.append((name, begin, end, serviceref, timer.name, getLog())) continue # Check for double Timers # We first check eit and if user wants us to guess event based on time # we try this as backup. The allowed diff should be configurable though. for rtimer in timerdict.get(serviceref, ()): if rtimer.eit == eit: oldExists = True doLog("[AutoTimer] We found a timer based on eit") newEntry = rtimer break elif config.plugins.autotimer.try_guessing.value and getTimeDiff(rtimer, evtBegin, evtEnd) > ((duration/10)*8): oldExists = True doLog("[AutoTimer] We found a timer based on time guessing") newEntry = rtimer break elif timer.avoidDuplicateDescription >= 1 \ and not rtimer.disabled: if self.checkDuplicates(timer, name, rtimer.name, shortdesc, rtimer.description, extdesc, rtimer.extdesc ): # if searchForDuplicateDescription > 1 then check short description oldExists = True doLog("[AutoTimer] We found a timer (similar service) with same description, skipping event") break # We found no timer we want to edit if newEntry is None: # But there is a match if oldExists: doLog("[AutoTimer] Skipping an event because a timer on same service exists") skipped.append((name, begin, end, serviceref, timer.name, getLog())) continue # We want to search for possible doubles if timer.avoidDuplicateDescription >= 2: for rtimer in chain.from_iterable( itervalues(timerdict) ): if not rtimer.disabled: if self.checkDuplicates(timer, name, rtimer.name, shortdesc, rtimer.description, extdesc, rtimer.extdesc ): oldExists = True doLog("[AutoTimer] We found a timer (any service) with same description, skipping event") break if oldExists: doLog("[AutoTimer] Skipping an event because a timer on any service exists") skipped.append((name, begin, end, serviceref, timer.name, getLog())) continue if timer.checkCounter(timestamp): doLog("[AutoTimer] Not adding new timer because counter is depleted.") skipped.append((name, begin, end, serviceref, timer.name, getLog())) continue # Append to timerlist and abort if simulating timers.append((name, begin, end, serviceref, timer.name, getLog())) if simulateOnly: continue if newEntry is not None: # Abort if we don't want to modify timers or timer is repeated if config.plugins.autotimer.refresh.value == "none" or newEntry.repeated: doLog("[AutoTimer] Won't modify existing timer because either no modification allowed or repeated timer") continue if hasattr(newEntry, "isAutoTimer") or TAG in newEntry.tags: newEntry.log(501, "[AutoTimer] AutoTimer %s modified this automatically generated timer." % (timer.name)) else: if config.plugins.autotimer.refresh.value != "all": doLog("[AutoTimer] Won't modify existing timer because it's no timer set by us") continue newEntry.log(501, "[AutoTimer] Warning, AutoTimer %s messed with a timer which might not belong to it: %s ." % (timer.name, newEntry.name)) modified += 1 self.modifyTimer(newEntry, name, shortdesc, begin, end, serviceref, eit) newEntry.log(501, "[AutoTimer] AutoTimer modified timer: %s ." % (newEntry.name)) else: newEntry = RecordTimerEntry(ServiceReference(serviceref), begin, end, name, shortdesc, eit) newEntry.log(500, "[AutoTimer] Try to add new timer based on AutoTimer %s." % (timer.name)) # Mark this entry as AutoTimer (only AutoTimers will have this Attribute set) # It is only temporarily, after a restart it will be lost, # because it won't be stored in the timer xml file newEntry.isAutoTimer = True # Apply afterEvent if timer.hasAfterEvent(): afterEvent = timer.getAfterEventTimespan(localtime(end)) if afterEvent is None: afterEvent = timer.getAfterEvent() if afterEvent is not None: newEntry.afterEvent = afterEvent newEntry.dirname = dirname or timer.destination newEntry.justplay = timer.justplay newEntry.vpsplugin_enabled = timer.vps_enabled newEntry.vpsplugin_overwrite = timer.vps_overwrite newEntry.conflict_detection = timer.conflict_detection newEntry.always_zap = timer.always_zap newEntry.zap_wakeup = timer.zap_wakeup tags = timer.tags[:] if config.plugins.autotimer.add_autotimer_to_tags.value: tags.append('AutoTimer') if config.plugins.autotimer.add_name_to_tags.value: tagname = timer.name.strip() if tagname: tagname = tagname[0].upper() + tagname[1:].replace(" ", "_") tags.append(tagname) if TAG not in tags: tags.append(TAG) newEntry.tags = tags if oldExists: # XXX: this won't perform a sanity check, but do we actually want to do so? recordHandler.timeChanged(newEntry) #if renameTimer is not None and timer.series_labeling: # renameTimer(newEntry, name, evtBegin, evtEnd) else: conflictString = "" if similarTimer: conflictString = similardict[eit].conflictString newEntry.log(504, "[AutoTimer] Try to add similar Timer because of conflicts with %s." % (conflictString)) # Try to add timer conflicts = recordHandler.record(newEntry) if conflicts: # Maybe use newEntry.log conflictString += ' / '.join(["%s (%s)" % (x.name, strftime("%Y%m%d %H%M", localtime(x.begin))) for x in conflicts]) doLog("[AutoTimer] conflict with %s detected" % (conflictString)) if config.plugins.autotimer.addsimilar_on_conflict.value: # We start our search right after our actual index # Attention we have to use a copy of the list, because we have to append the previous older matches lepgm = len(epgmatches) for i in xrange(lepgm): servicerefS, eitS, nameS, beginS, durationS, shortdescS, extdescS = epgmatches[ (i+idx+1)%lepgm ] if self.checkDuplicates(timer, name, nameS, shortdesc, shortdescS, extdesc, extdescS, force=True ): # Check if the similar is already known if eitS not in similardict: doLog("[AutoTimer] Found similar Timer: " + name) # Store the actual and similar eit and conflictString, so it can be handled later newEntry.conflictString = conflictString similardict[eit] = newEntry similardict[eitS] = newEntry similarTimer = True if beginS <= evtBegin: # Event is before our actual epgmatch so we have to append it to the epgmatches list epgmatches.append((servicerefS, eitS, nameS, beginS, durationS, shortdescS, extdescS)) # If we need a second similar it will be found the next time else: similarTimer = False newEntry = similardict[eitS] break if conflicts is None: timer.decrementCounter() new += 1 newEntry.extdesc = extdesc timerdict[serviceref].append(newEntry) #if renameTimer is not None and timer.series_labeling: # renameTimer(newEntry, name, evtBegin, evtEnd) # Similar timers are in new timers list and additionally in similar timers list if similarTimer: similars.append((name, begin, end, serviceref, timer.name)) similardict.clear() # Don't care about similar timers elif not similarTimer: conflicting.append((name, begin, end, serviceref, timer.name)) if config.plugins.autotimer.disabled_on_conflict.value: newEntry.log(503, "[AutoTimer] Timer disabled because of conflicts with %s." % (conflictString)) newEntry.disabled = True # We might want to do the sanity check locally so we don't run it twice - but I consider this workaround a hack anyway conflicts = recordHandler.record(newEntry) if sp_showResult is not None: sp_showResult() return (new, modified)
def parseTimer(self, timer, epgcache, serviceHandler, recordHandler, checkEvtLimit, evtLimit, timers, conflicting, similars, timerdict, moviedict, simulateOnly=False): new = 0 modified = 0 # Precompute timer destination dir dest = timer.destination or config.usage.default_path.value # Workaround to allow search for umlauts if we know the encoding #match = timer.match match = timer.match.replace('\xc2\x86', '').replace('\xc2\x87', '') if timer.encoding != 'UTF-8': try: match = match.decode('UTF-8').encode(timer.encoding) except UnicodeDecodeError: pass if timer.searchType == "description": epgmatches = [] mask = (eServiceReference.isMarker | eServiceReference.isDirectory) casesensitive = timer.searchCase == "sensitive" if not casesensitive: match = match.lower() # Service filter defined # Search only using the specified services test = [(service, 0, -1, -1) for service in timer.services] for bouquet in timer.bouquets: services = serviceHandler.list(eServiceReference(bouquet)) if not services is None: while True: service = services.getNext() if not service.valid(): #check end of list break if not (service.flags & mask): test.append((service.toString(), 0, -1, -1)) if not test: # No service filter defined # Search within all services - could be very slow # Get all bouquets bouquetlist = [] if config.usage.multibouquet.value: refstr = '1:7:1:0:0:0:0:0:0:0:FROM BOUQUET "bouquets.tv" ORDER BY bouquet' bouquetroot = eServiceReference(refstr) bouquets = serviceHandler.list(bouquetroot) if bouquets: while True: s = bouquets.getNext() if not s.valid(): break if s.flags & eServiceReference.isDirectory: info = serviceHandler.info(s) if info: bouquetlist.append((info.getName(s), s)) mask = (eServiceReference.isMarker | eServiceReference.isDirectory) for name, bouquet in bouquetlist: if not bouquet.valid(): #check end of list break if bouquet.flags & eServiceReference.isDirectory: services = serviceHandler.list(bouquet) if not services is None: while True: service = services.getNext() if not service.valid(): #check end of list break if not (service.flags & mask): test.append( (service.toString(), 0, -1, -1)) else: service_types_tv = '1:7:1:0:0:0:0:0:0:0:(type == 1) || (type == 17) || (type == 22) || (type == 25) || (type == 134) || (type == 195)' refstr = '%s FROM BOUQUET "userbouquet.favourites.tv" ORDER BY bouquet' % ( service_types_tv) bouquetroot = eServiceReference(refstr) info = serviceHandler.info(bouquetroot) if info: bouquetlist.append( (info.getName(bouquetroot), bouquetroot)) mask = (eServiceReference.isMarker | eServiceReference.isDirectory) for name, bouquet in bouquetlist: if bouquet.flags & eServiceReference.isDirectory: services = serviceHandler.list(bouquet) if not services is None: while True: service = services.getNext() if not service.valid(): #check end of list break if not (service.flags & mask): test.append( (service.toString(), 0, -1, -1)) if test: # Get all events # eEPGCache.lookupEvent( [ format of the returned tuples, ( service, 0 = event intersects given start_time, start_time -1 for now_time), ] ) test.insert(0, 'RITBDSE') allevents = epgcache.lookupEvent(test) or [] # Filter events for serviceref, eit, name, begin, duration, shortdesc, extdesc in allevents: if match in (shortdesc if casesensitive else shortdesc.lower()) \ or match in (extdesc if casesensitive else extdesc.lower()): epgmatches.append((serviceref, eit, name, begin, duration, shortdesc, extdesc)) else: # Search EPG, default to empty list epgmatches = epgcache.search( ('RITBDSE', 2000, typeMap[timer.searchType], match, caseMap[timer.searchCase])) or [] # Sort list of tuples by begin time 'B' epgmatches.sort(key=itemgetter(3)) # Contains the the marked similar eits and the conflicting strings similardict = defaultdict(list) # Loop over all EPG matches for idx, (serviceref, eit, name, begin, duration, shortdesc, extdesc) in enumerate(epgmatches): print("[AutoTimer] possible epgmatch %s" % (name)) eserviceref = eServiceReference(serviceref) evt = epgcache.lookupEventId(eserviceref, eit) if not evt: print("[AutoTimer] Could not create Event!") continue # Try to determine real service (we always choose the last one) n = evt.getNumOfLinkageServices() if n > 0: i = evt.getLinkageService(eserviceref, n - 1) serviceref = i.toString() evtBegin = begin evtEnd = end = begin + duration # If event starts in less than 60 seconds skip it if begin < time() + 60: print( "[AutoTimer] Skipping an event because it starts in less than 60 seconds" ) continue # Set short description to equal extended description if it is empty. if not shortdesc: shortdesc = extdesc # Convert begin time timestamp = localtime(begin) # Update timer timer.update(begin, timestamp) # Check if eit is in similar matches list # NOTE: ignore evtLimit for similar timers as I feel this makes the feature unintuitive similarTimer = False if eit in similardict: similarTimer = True dayofweek = None # NOTE: ignore day on similar timer else: # If maximum days in future is set then check time if checkEvtLimit: if begin > evtLimit: continue dayofweek = str(timestamp.tm_wday) # Check timer conditions # NOTE: similar matches do not care about the day/time they are on, so ignore them if timer.checkServices(serviceref) \ or timer.checkDuration(duration) \ or (not similarTimer and (\ timer.checkTimespan(timestamp) \ or timer.checkTimeframe(begin) \ )): continue # Initialize newEntry = None oldExists = False dirname = None # Eventually change service to alternative if timer.overrideAlternatives: serviceref = timer.getAlternative(serviceref) if timer.series_labeling and sp_getSeasonEpisode is not None: print( "[AutoTimer SeriesPlugin] Request name, desc, path %s %s %s" % (name, shortdesc, dest)) sp = sp_getSeasonEpisode(serviceref, name, evtBegin, evtEnd, shortdesc, dest) if sp and len(sp) == 3: name = sp[0] shortdesc = sp[1] dirname = sp[2] print( "[AutoTimer SeriesPlugin] Returned name, desc, path %s %s %s" % (name, shortdesc, dirname)) else: print("[AutoTimer SeriesPlugin] Returned %s" % (str(sp))) if timer.checkFilter(name, shortdesc, extdesc, dayofweek): continue if timer.hasOffset(): # Apply custom Offset begin, end = timer.applyOffset(begin, end) else: # Apply E2 Offset begin -= config.recording.margin_before.value * 60 end += config.recording.margin_after.value * 60 # Overwrite endtime if requested if timer.justplay and not timer.setEndtime: end = begin # Check for existing recordings in directory if timer.avoidDuplicateDescription == 3: # Reset movie Exists movieExists = False if dest and dest not in moviedict: self.addDirectoryToMovieDict(moviedict, dest, serviceHandler) for movieinfo in moviedict.get(dest, ()): if self.checkDuplicates(timer, name, movieinfo.get("name"), shortdesc, movieinfo.get("shortdesc"), extdesc, movieinfo.get("extdesc")): print( "[AutoTimer] We found a matching recorded movie, skipping event:", name) movieExists = True break if movieExists: continue # Check for double Timers # We first check eit and if user wants us to guess event based on time # we try this as backup. The allowed diff should be configurable though. for rtimer in timerdict.get(serviceref, ()): if rtimer.eit == eit: oldExists = True print("[AutoTimer] We found a timer based on eit") newEntry = rtimer break elif config.plugins.autotimer.try_guessing.value and getTimeDiff( rtimer, evtBegin, evtEnd) > ((duration / 10) * 8): oldExists = True print( "[AutoTimer] We found a timer based on time guessing") newEntry = rtimer break elif timer.avoidDuplicateDescription >= 1 \ and not rtimer.disabled: if self.checkDuplicates(timer, name, rtimer.name, shortdesc, rtimer.description, extdesc, rtimer.extdesc): # if searchForDuplicateDescription > 1 then check short description oldExists = True print( "[AutoTimer] We found a timer (similar service) with same description, skipping event" ) break # We found no timer we want to edit if newEntry is None: # But there is a match if oldExists: continue # We want to search for possible doubles if timer.avoidDuplicateDescription >= 2: for rtimer in chain.from_iterable(itervalues(timerdict)): if not rtimer.disabled: if self.checkDuplicates(timer, name, rtimer.name, shortdesc, rtimer.description, extdesc, rtimer.extdesc): oldExists = True print( "[AutoTimer] We found a timer (any service) with same description, skipping event" ) break if oldExists: continue if timer.checkCounter(timestamp): print( "[AutoTimer] Not adding new timer because counter is depleted." ) continue # Append to timerlist and abort if simulating timers.append((name, begin, end, serviceref, timer.name)) if simulateOnly: continue if newEntry is not None: # Abort if we don't want to modify timers or timer is repeated if config.plugins.autotimer.refresh.value == "none" or newEntry.repeated: print( "[AutoTimer] Won't modify existing timer because either no modification allowed or repeated timer" ) continue if hasattr(newEntry, "isAutoTimer"): newEntry.log( 501, "[AutoTimer] AutoTimer %s modified this automatically generated timer." % (timer.name)) else: if config.plugins.autotimer.refresh.value != "all": print( "[AutoTimer] Won't modify existing timer because it's no timer set by us" ) continue newEntry.log( 501, "[AutoTimer] Warning, AutoTimer %s messed with a timer which might not belong to it: %s ." % (timer.name, newEntry.name)) modified += 1 self.modifyTimer(newEntry, name, shortdesc, begin, end, serviceref, eit) newEntry.log( 501, "[AutoTimer] AutoTimer modified timer: %s ." % (newEntry.name)) else: newEntry = RecordTimerEntry(ServiceReference(serviceref), begin, end, name, shortdesc, eit) newEntry.log( 500, "[AutoTimer] Try to add new timer based on AutoTimer %s." % (timer.name)) # Mark this entry as AutoTimer (only AutoTimers will have this Attribute set) # It is only temporarily, after a restart it will be lost, # because it won't be stored in the timer xml file newEntry.isAutoTimer = True # Apply afterEvent if timer.hasAfterEvent(): afterEvent = timer.getAfterEventTimespan(localtime(end)) if afterEvent is None: afterEvent = timer.getAfterEvent() if afterEvent is not None: newEntry.afterEvent = afterEvent newEntry.dirname = dirname or timer.destination newEntry.justplay = timer.justplay newEntry.vpsplugin_enabled = timer.vps_enabled newEntry.vpsplugin_overwrite = timer.vps_overwrite newEntry.conflict_detection = timer.conflict_detection tags = timer.tags[:] if config.plugins.autotimer.add_autotimer_to_tags.value: tags.append('AutoTimer') if config.plugins.autotimer.add_name_to_tags.value: tagname = timer.name.strip() if tagname: tagname = tagname[0].upper() + tagname[1:].replace( " ", "_") tags.append(tagname) newEntry.tags = tags if oldExists: # XXX: this won't perform a sanity check, but do we actually want to do so? recordHandler.timeChanged(newEntry) #if renameTimer is not None and timer.series_labeling: # renameTimer(newEntry, name, evtBegin, evtEnd) else: conflictString = "" if similarTimer: conflictString = similardict[eit].conflictString newEntry.log( 504, "[AutoTimer] Try to add similar Timer because of conflicts with %s." % (conflictString)) # Try to add timer conflicts = recordHandler.record(newEntry) if conflicts: # Maybe use newEntry.log conflictString += ' / '.join([ "%s (%s)" % (x.name, strftime("%Y%m%d %H%M", localtime(x.begin))) for x in conflicts ]) print("[AutoTimer] conflict with %s detected" % (conflictString)) if config.plugins.autotimer.addsimilar_on_conflict.value: # We start our search right after our actual index # Attention we have to use a copy of the list, because we have to append the previous older matches lepgm = len(epgmatches) for i in xrange(lepgm): servicerefS, eitS, nameS, beginS, durationS, shortdescS, extdescS = epgmatches[ (i + idx + 1) % lepgm] if self.checkDuplicates(timer, name, nameS, shortdesc, shortdescS, extdesc, extdescS, force=True): # Check if the similar is already known if eitS not in similardict: print("[AutoTimer] Found similar Timer: " + name) # Store the actual and similar eit and conflictString, so it can be handled later newEntry.conflictString = conflictString similardict[eit] = newEntry similardict[eitS] = newEntry similarTimer = True if beginS <= evtBegin: # Event is before our actual epgmatch so we have to append it to the epgmatches list epgmatches.append( (servicerefS, eitS, nameS, beginS, durationS, shortdescS, extdescS)) # If we need a second similar it will be found the next time else: similarTimer = False newEntry = similardict[eitS] break if conflicts is None: timer.decrementCounter() new += 1 newEntry.extdesc = extdesc timerdict[serviceref].append(newEntry) #if renameTimer is not None and timer.series_labeling: # renameTimer(newEntry, name, evtBegin, evtEnd) # Similar timers are in new timers list and additionally in similar timers list if similarTimer: similars.append( (name, begin, end, serviceref, timer.name)) similardict.clear() # Don't care about similar timers elif not similarTimer: conflicting.append( (name, begin, end, serviceref, timer.name)) if config.plugins.autotimer.disabled_on_conflict.value: newEntry.log( 503, "[AutoTimer] Timer disabled because of conflicts with %s." % (conflictString)) newEntry.disabled = True # We might want to do the sanity check locally so we don't run it twice - but I consider this workaround a hack anyway conflicts = recordHandler.record(newEntry) if sp_showResult is not None: sp_showResult() return (new, modified)