import NavigationInstance from time import localtime, mktime, gmtime, time from enigma import iServiceInformation, eServiceCenter, eServiceReference, getBestPlayableServiceReference from timer import TimerEntry import RecordTimer from Tools.CIHelper import cihelper from Components.config import config class TimerSanityCheck: def __init__(self, timerlist, newtimer=None): self.localtimediff = 25 * 3600 - mktime(gmtime(25 * 3600)) self.timerlist = timerlist self.newtimer = newtimer self.simultimer = [] self.rep_eventlist = [] self.nrep_eventlist = [] self.bflag = -1 self.eflag = 1 def check(self, ext_timer=None): if ext_timer and isinstance(ext_timer, RecordTimer.RecordTimerEntry): self.newtimer = ext_timer self.simultimer = [] if self.newtimer: if not self.newtimer.conflict_detection or (self.newtimer.service_ref and '%3a//' in self.newtimer.service_ref.ref.toString()): print "[TimerSanityCheck] Exception - timer does not have to be checked!" return True self.simultimer = [self.newtimer] return self.checkTimerlist() def getSimulTimerList(self): return self.simultimer def doubleCheck(self): if self.newtimer and self.newtimer.service_ref and self.newtimer.service_ref.ref.valid(): self.simultimer = [self.newtimer] for timer in self.timerlist: if timer == self.newtimer: return True if self.newtimer.begin >= timer.begin and self.newtimer.end <= timer.end: if timer.justplay and not self.newtimer.justplay: continue if timer.service_ref.ref.flags & eServiceReference.isGroup: if self.newtimer.service_ref.ref.flags & eServiceReference.isGroup and timer.service_ref.ref.getPath() == self.newtimer.service_ref.ref.getPath(): return True continue getUnsignedDataRef1 = timer.service_ref.ref.getUnsignedData getUnsignedDataRef2 = self.newtimer.service_ref.ref.getUnsignedData for x in (1, 2, 3, 4): if getUnsignedDataRef1(x) != getUnsignedDataRef2(x): break else: return True return False def checkTimerlist(self, ext_timer=None): #with special service for external plugins # Entries in eventlist # timeindex # BeginEndFlag 1 for begin, -1 for end # index -1 for the new Timer, 0..n index of the existing timers # count of running timers serviceHandler = eServiceCenter.getInstance() # create a list with all start and end times # split it into recurring and singleshot timers ################################################################################## # process the new timer self.rep_eventlist = [] self.nrep_eventlist = [] if ext_timer and isinstance(ext_timer, RecordTimer.RecordTimerEntry): self.newtimer = ext_timer #GML:1 - A timer which has already ended (happens during start-up check) can't clash!! # # NOTE: that when adding a timer it also cannot clash with: # o any timers which run before the latest period of no timers running # before the timer to be added starts # o any timers which run after the first period of no timers running # after the timer to be added ends # Code to handle this needs to be added (it is *NOT* here yet!) # if (self.newtimer is not None) and (self.newtimer.end < time()): # does not conflict return True if not self.newtimer or not self.newtimer.service_ref or not self.newtimer.service_ref.ref.valid(): print "[TimerSanityCheck] Error - timer not valid!" return False if self.newtimer.disabled or not self.newtimer.conflict_detection or '%3a//' in self.newtimer.service_ref.ref.toString(): print "[TimerSanityCheck] Exception - timer does not have to be checked!" return True curtime = localtime(time()) if curtime.tm_year > 1970 and self.newtimer.end < time(): print "[TimerSanityCheck] timer is finished!" return True rflags = self.newtimer.repeated rflags = ((rflags & 0x7F) >> 3) | ((rflags & 0x07) << 4) if rflags: begin = self.newtimer.begin % 86400 # map to first day if (self.localtimediff > 0) and ((begin + self.localtimediff) > 86400): rflags = ((rflags >> 1) & 0x3F) | ((rflags << 6) & 0x40) elif (self.localtimediff < 0) and (begin < self.localtimediff): rflags = ((rflags << 1) & 0x7E) | ((rflags >> 6) & 0x01) while rflags: # then arrange on the week if rflags & 1: self.rep_eventlist.append((begin, -1)) begin += 86400 rflags >>= 1 else: self.nrep_eventlist.extend([(self.newtimer.begin, self.bflag, -1), (self.newtimer.end, self.eflag, -1)]) ################################################################################## # now process existing timers self.check_timerlist = [] idx = 0 for timer in self.timerlist: if timer != self.newtimer: if timer.disabled or not timer.conflict_detection or not timer.service_ref or '%3a//' in timer.service_ref.ref.toString() or timer.state == TimerEntry.StateEnded: continue if timer.repeated: rflags = timer.repeated rflags = ((rflags & 0x7F) >> 3) | ((rflags & 0x07) << 4) begin = timer.begin % 86400 # map all to first day if (self.localtimediff > 0) and ((begin + self.localtimediff) > 86400): rflags = ((rflags >> 1) & 0x3F) | ((rflags << 6) & 0x40) elif (self.localtimediff < 0) and (begin < self.localtimediff): rflags = ((rflags << 1) & 0x7E) | ((rflags >> 6) & 0x01) while rflags: if rflags & 1: self.rep_eventlist.append((begin, idx)) begin += 86400 rflags >>= 1 else: self.nrep_eventlist.extend([(timer.begin, self.bflag, idx), (timer.end, self.eflag, idx)]) self.check_timerlist.append(timer) idx += 1 ################################################################################ # journalize timer repeations if self.nrep_eventlist: interval_begin = min(self.nrep_eventlist)[0] interval_end = max(self.nrep_eventlist)[0] offset_0 = interval_begin - (interval_begin % 604800) weeks = (interval_end - offset_0) / 604800 if (interval_end - offset_0) % 604800: weeks += 1 for cnt in range(int(weeks)): for event in self.rep_eventlist: if event[1] == -1: # -1 is the identifier of the changed timer event_begin = self.newtimer.begin event_end = self.newtimer.end else: event_begin = self.check_timerlist[event[1]].begin event_end = self.check_timerlist[event[1]].end new_event_begin = event[0] + offset_0 + (cnt * 604800) # summertime correction new_lth = localtime(new_event_begin).tm_hour new_event_begin += 3600 * (localtime(event_begin).tm_hour - new_lth) new_event_end = new_event_begin + (event_end - event_begin) if event[1] == -1: if new_event_begin >= self.newtimer.begin: # is the soap already running? self.nrep_eventlist.extend([(new_event_begin, self.bflag, event[1]), (new_event_end, self.eflag, event[1])]) else: if new_event_begin >= self.check_timerlist[event[1]].begin: # is the soap already running? self.nrep_eventlist.extend([(new_event_begin, self.bflag, event[1]), (new_event_end, self.eflag, event[1])]) else: offset_0 = 345600 # the Epoch begins on Thursday for cnt in (0, 1): # test two weeks to take care of Sunday-Monday transitions for event in self.rep_eventlist: if event[1] == -1: # -1 is the identifier of the changed timer event_begin = self.newtimer.begin event_end = self.newtimer.end else: event_begin = self.check_timerlist[event[1]].begin event_end = self.check_timerlist[event[1]].end new_event_begin = event[0] + offset_0 + (cnt * 604800) new_event_end = new_event_begin + (event_end - event_begin) self.nrep_eventlist.extend([(new_event_begin, self.bflag, event[1]), (new_event_end, self.eflag, event[1])]) ################################################################################ # order list chronological self.nrep_eventlist.sort() ################################################################################## # detect overlapping timers and overlapping times fakeRecList = [] ConflictTimer = None ConflictTunerType = None newTimerTunerType = None cnt = 0 idx = 0 overlaplist = [] is_ci_use = 0 is_ci_timer_conflict = 0 ci_timer = False if config.misc.use_ci_assignment.value and cihelper.ServiceIsAssigned(self.newtimer.service_ref.ref) and not (self.newtimer.record_ecm and not self.newtimer.descramble): ci_timer = self.newtimer ci_timer_begin = ci_timer.begin ci_timer_end = ci_timer.end ci_timer_dur = ci_timer_end - ci_timer_begin ci_timer_events = [] for ev in self.nrep_eventlist: if ev[2] == -1: ci_timer_events.append((ev[0], ev[0] + ci_timer_dur)) for event in self.nrep_eventlist: cnt += event[1] if event[2] == -1: # new timer timer = self.newtimer else: timer = self.check_timerlist[event[2]] if event[1] == self.bflag: tunerType = [] ref = timer.service_ref and timer.service_ref.ref timer_ref = timer.service_ref if ref and ref.flags & eServiceReference.isGroup and timer.isRunning(): timer_ref = getBestPlayableServiceReference(timer.service_ref.ref, eServiceReference()) fakeRecService = NavigationInstance.instance.recordService(timer_ref, True) if fakeRecService: fakeRecResult = fakeRecService.start(True) else: fakeRecResult = -1 # TODO #if fakeRecResult == -6 and len(NavigationInstance.instance.getRecordings(True)) < 2: # print "[TimerSanityCheck] less than two timers in the simulated recording list - timer conflict is not plausible - ignored !" # fakeRecResult = 0 if not fakeRecResult: # tune okay if hasattr(fakeRecService, 'frontendInfo'): feinfo = fakeRecService.frontendInfo() if feinfo and hasattr(feinfo, 'getFrontendData'): tunerType.append(feinfo.getFrontendData().get("tuner_type", -1)) feinfo = None else: # tune failed.. so we must go another way to get service type (DVB-S, DVB-T, DVB-C) def getServiceType(ref): # helper function to get a service type of a service reference serviceInfo = serviceHandler.info(ref) serviceInfo = serviceInfo and serviceInfo.getInfoObject(ref, iServiceInformation.sTransponderData) return -1 if serviceInfo is None else serviceInfo.get("tuner_type", -1) if ref and ref.flags & eServiceReference.isGroup: # service group ? serviceList = serviceHandler.list(ref) # get all alternative services if serviceList: for ref in serviceList.getContent("R"): # iterate over all group service references type = getServiceType(ref) if not type in tunerType: # just add single time tunerType.append(type) elif ref: tunerType.append(getServiceType(ref)) if event[2] == -1: # new timer newTimerTunerType = tunerType overlaplist.append((fakeRecResult, timer, tunerType)) fakeRecList.append((timer, fakeRecService)) if fakeRecResult: if ConflictTimer is None: # just take care of the first conflict ConflictTimer = timer ConflictTunerType = tunerType elif event[1] == self.eflag: for fakeRec in fakeRecList: if timer == fakeRec[0] and fakeRec[1]: NavigationInstance.instance.stopRecordService(fakeRec[1]) fakeRecList.remove(fakeRec) fakeRec = None for entry in overlaplist: if entry[1] == timer: overlaplist.remove(entry) else: print "[TimerSanityCheck] bug: unknown flag!" if ci_timer and timer != ci_timer and cihelper.ServiceIsAssigned(timer.service_ref.ref) and not (timer.record_ecm and not timer.descramble): if event[1] == self.bflag: timer_begin = event[0] timer_end = event[0] + (timer.end - timer.begin) else: timer_end = event[0] timer_begin = event[0] - (timer.end - timer.begin) for ci_ev in ci_timer_events: if (ci_ev[0] >= timer_begin and ci_ev[0] <= timer_end) or (ci_ev[1] >= timer_begin and ci_ev[1] <= timer_end): if ci_timer.service_ref.ref != timer.service_ref.ref: is_ci_timer_conflict = 1 break if is_ci_timer_conflict == 1: if ConflictTimer is None: ConflictTimer = timer ConflictTunerType = tunerType self.nrep_eventlist[idx] = (event[0], event[1], event[2], cnt, overlaplist[:]) # insert a duplicate into current overlaplist fakeRecService = None fakeRecResult = None idx += 1 if ConflictTimer is None: print "[TimerSanityCheck] conflict not found!" return True ################################################################################## # we have detected a conflict, now we must figure out the involved timers if self.newtimer is not ConflictTimer: # the new timer is not the conflicting timer? for event in self.nrep_eventlist: if len(event[4]) > 1: # entry in overlaplist of this event?? kt = False nt = False for entry in event[4]: if entry[1] is ConflictTimer: kt = True if entry[1] is self.newtimer: nt = True if nt and kt: ConflictTimer = self.newtimer ConflictTunerType = newTimerTunerType break self.simultimer = [ConflictTimer] for event in self.nrep_eventlist: if len(event[4]) > 1: # entry in overlaplist of this event?? for entry in event[4]: if entry[1] is ConflictTimer: break else: continue for entry in event[4]: if not entry[1] in self.simultimer: for x in entry[2]: if x in ConflictTunerType: self.simultimer.append(entry[1]) break if len(self.simultimer) < 2: print "[TimerSanityCheck] possible bug: unknown conflict!" return True print "[TimerSanityCheck] conflict detected!" return False