def KeyOk(self): sel = self["list"].getCurrent()[0] if (sel == "searchEPGe2"): if self.epgserchplugin: self.epgserchplugin(session=self.session, servicelist=self) elif (sel == "searchEPG"): if config.dbp.usevkeyboard.value: self.session.openWithCallback(self.beginSearch, VirtualKeyBoard, title = _("Enter event to search:"), text = "") else: self.session.openWithCallback(self.beginSearch,InputBox, title = _("Enter event to search:"), windowTitle = _("Search in EPG cache"), text="") elif (sel == "searchEPGLast"): self.session.open(NEpgSearchLast) elif (sel == "downloadEPG"): if self.checkDevice(): self.ref = self.session.nav.getCurrentlyPlayingServiceReference() if config.nemepg.downskyit.value: self.downloadItEPG() elif config.nemepg.downskyuk.value: self.downloadUkEPG() elif (sel == "e2LoaderEpg") or (sel == "e2LoaderEpgI"): self.downIMode = { 'e2LoaderEpg':False, 'e2LoaderEpgI':True }[sel] msg = _('Do you want download EPG\nusing e2_loadepg?') self.epgDBox = self.session.openWithCallback(self.downEPG,MessageBox, msg, MessageBox.TYPE_YESNO) self.epgDBox.setTitle(_("Download EPG")) elif (sel == "reloadEPG"): if self.e2Loader: searchPaths = ['/tmp/%s','/media/usb/%s','/media/cf/%s','/media/hdd/%s'] for path in searchPaths: epgFile = (path % 'ext.epg.dat') if fileExists(epgFile): system("mv " + epgFile + " " + config.nemepg.path.value + "/epg.dat") msg = _('Load EPG data in Enigma cache from:\n%s/epg.dat.\nPlease Wait...') % config.nemepg.path.value self.epgRBox = self.session.open(MessageBox, msg, MessageBox.TYPE_INFO) self.epgRBox.setTitle(_("Loading EPG")) self.reloadEPGTimer.start(500, False) elif (sel == "eraseEPG"): msg = _('Erasing EPG Chache.\nPlease Wait...') self.epgCBox = self.session.open(MessageBox, msg, MessageBox.TYPE_INFO) self.epgCBox.setTitle(_("Erasing EPG cache")) self.clearEPGTimer.start(500, False) elif (sel == "backupEPG"): msg = _('Backup Enigma EPG data on:\n%s.\nPlease Wait...') % config.nemepg.path.value self.epgBBox = self.session.open(MessageBox, msg, MessageBox.TYPE_INFO) self.epgBBox.setTitle(_("Backing-up EPG")) self.saveEPGTimer.start(500, False) elif (sel == "createTIMER"): serviceref = ServiceReference(getSid(config.nemepg.skyitch.value)) begin = 1239332400 end = 1239333600 name = "Download EPG Daily" description = "Please do not remove this entry!" timer = RecordTimerEntry(serviceref, begin, end, name, description, 66, False, 2, 1) timer.repeated = 127 self.session.openWithCallback(self.finishedAdd, TimerEntry, timer) elif (sel == "openTIMER"): self.session.open(TimerEditList) elif (sel == "epglog"): self.session.open(dbpShowPanel, '/usr/log/crossepg.log' ,_('EPG Timer log')) elif (sel == "configEPG"): self.session.openWithCallback(self.saveConfig, NSetup, "epg")
def __init__( self, session, serviceref, begin, end, name, description, eit, disabled=False, justplay=False, afterEvent=AFTEREVENT.AUTO, checkOldTimers=False, dirname=None, tags=None, ): self.session = session RecordTimerEntry.__init__( self, serviceref, begin, end, name, description, eit, disabled=False, justplay=False, afterEvent=AFTEREVENT.AUTO, checkOldTimers=False, dirname=None, tags=None, )
def addTimer( session, serviceref, begin, end, name, description, disabled, justplay, afterevent, dirname, tags, repeated, logentries=None, eit=0, ): serviceref = unquote(serviceref) rt = session.nav.RecordTimer print "mao1", dirname if not dirname: dirname = preferredTimerPath() print "mao2", dirname try: timer = RecordTimerEntry( ServiceReference(serviceref), begin, end, name, description, eit, disabled, justplay, afterevent, dirname=dirname, tags=tags, ) timer.repeated = repeated if logentries: timer.log_entries = logentries conflicts = rt.record(timer) if conflicts: errors = [] for conflict in conflicts: errors.append(conflict.name) return {"result": False, "message": "Conflicting Timer(s) detected! %s" % " / ".join(errors)} except Exception, e: print e return {"result": False, "message": "Could not add timer '%s'!" % name}
def recordNow(session, infinite): rt = session.nav.RecordTimer serviceref = session.nav.getCurrentlyPlayingServiceReference().toString() try: event = session.nav.getCurrentService().info().getEvent(0) except Exception: event = None if not event and not infinite: return { "result": False, "message": "No event found! Not recording!" } if event: (begin, end, name, description, eit) = parseEvent(event) begin = time() msg = "Instant record for current Event started" else: name = "instant record" description = "" eit = 0 if infinite: begin = time() end = begin + 3600 * 10 msg = "Infinite Instant recording started" timer = RecordTimerEntry( ServiceReference(serviceref), begin, end, name, description, eit, False, False, 0, dirname=preferredInstantRecordPath() ) timer.dontSave = True if rt.record(timer): return { "result": False, "message": "Timer conflict detected! Not recording!" } return { "result": True, "message": msg }
def recordNow(self, param): if not config.misc.recording_allowed.value: return (False, _("Recording is currently disabled!")) limitEvent = True if param == "undefinitely" or param == "infinite": ret = (True, "Infinite Instant recording started") limitEvent = False else: ret = ( True, "Instant record for current Event started" ) serviceref = ServiceReference(self.session.nav.getCurrentlyPlayingServiceReference().toString()) event = None try: service = self.session.nav.getCurrentService() event = service.info().getEvent(0) except Exception: print "[Webcomponents.Timer] recordNow Exception!" begin = time() end = begin + 3600 * 10 name = "instant record" description = "" eventid = 0 if event is not None: curEvent = parseEvent(event) name = curEvent[2] description = curEvent[3] eventid = curEvent[4] if limitEvent: end = curEvent[1] else: if limitEvent: ret = ( False, "No event found! Not recording!" ) if ret[0]: location = preferredInstantRecordPath() timer = RecordTimerEntry(serviceref, begin, end, name, description, eventid, False, False, 0, dirname=location) timer.dontSave = True recRet = self.recordtimer.record(timer) if recRet is not None: # a conflict is rather unlikely, but this can also indicate a non-recordable service ret = (False, "Timer conflict detected! Not recording!" ) return ret
def program_seek_vps_multiple_closed(self, retval): self.program_seek_vps_multiple_started = -1 self.found_vps_multiple = sorted(self.found_vps_multiple) for evt_begin, evt_id, evt in self.found_vps_multiple: # eigenen Timer überprüfen, wenn Zeiten nicht überschrieben werden dürfen if not self.timer.vpsplugin_overwrite and evt_begin <= self.timer.end: self.next_events.append(evt_id) self.timer.log(0, "[VPS] add event_id "+ str(evt_id)) else: canbeadded = True evt_begin += 60 evt_end = evt.getBeginTime() + evt.getDuration() - 60 now = time() for checktimer in self.session.nav.RecordTimer.timer_list: if checktimer == self.timer: continue if (checktimer.begin - now) > 3600*24: break if checktimer.service_ref.ref.toCompareString() == self.timer.service_ref.ref.toCompareString() or checktimer.service_ref.ref.toCompareString() == self.rec_ref.toCompareString(): if checktimer.begin <= evt_begin and checktimer.end >= evt_end: if not checktimer.vpsplugin_enabled or not checktimer.vpsplugin_overwrite: canbeadded = False # manuell angelegter Timer mit VPS if checktimer.vpsplugin_enabled and checktimer.name == "" and checktimer.vpsplugin_time is not None: checktimer.eit = evt_id checktimer.name = evt.getEventName() checktimer.description = evt.getShortDescription() checktimer.vpsplugin_time = None checktimer.log(0, "[VPS] changed timer (found same PDC-Time as in other VPS-recording)") canbeadded = False break if canbeadded: newevent_data = parseEvent(evt) newEntry = RecordTimerEntry(ServiceReference(self.rec_ref), *newevent_data) newEntry.vpsplugin_enabled = True newEntry.vpsplugin_overwrite = True newEntry.log(0, "[VPS] added this timer (found same PDC-Time as in other VPS-recording)") # Wenn kein Timer-Konflikt auftritt, wird der Timer angelegt. NavigationInstance.instance.RecordTimer.record(newEntry)
def copyTimer(self, start, duration): starttime = start - config.recording.margin_before.value * 60 endtime = start + duration + config.recording.margin_after.value * 60 self.new_timer_copy = RecordTimerEntry(ServiceReference(self.rec_ref), starttime, endtime, self.timer.name, self.timer.description, self.timer.eit, False, False, AFTEREVENT.AUTO, False, self.timer.dirname, self.timer.tags) self.new_timer_copy.vpsplugin_enabled = True self.new_timer_copy.vpsplugin_overwrite = self.timer.vpsplugin_overwrite self.new_timer_copy.log(0, "[VPS] added this timer") NavigationInstance.instance.RecordTimer.record(self.new_timer_copy)
def recordNow(self, param): limitEvent = True if param == "undefinitely" or param == "infinite": ret = (True, "Infinite Instant recording started") limitEvent = False else: ret = (True, "Instant record for current Event started") serviceref = ServiceReference(self.session.nav.getCurrentlyPlayingServiceReference().toString()) event = None try: service = self.session.nav.getCurrentService() event = service.info().getEvent(0) except Exception: print "[Webcomponents.Timer] recordNow Exception!" begin = time() end = begin + 3600 * 10 name = "instant record" description = "" eventid = 0 if event is not None: curEvent = parseEvent(event) name = curEvent[2] description = curEvent[3] eventid = curEvent[4] if limitEvent: end = curEvent[1] else: if limitEvent: ret = (False, "No event found! Not recording!") if ret[0]: location = config.movielist.last_videodir.value timer = RecordTimerEntry( serviceref, begin, end, name, description, eventid, False, False, 0, dirname=location ) timer.dontSave = True self.recordtimer.record(timer) return ret
def addTimer(session, serviceref, begin, end, name, description, disabled, justplay, afterevent, dirname, tags, repeated, vpsinfo=None, logentries=None, eit=0, always_zap=-1): serviceref = unquote(serviceref) rt = session.nav.RecordTimer print "mao1", dirname if not dirname: dirname = preferredTimerPath() print "mao2", dirname try: timer = RecordTimerEntry( ServiceReference(serviceref), begin, end, name, description, eit, disabled, justplay, afterevent, dirname=dirname, tags=tags) timer.repeated = repeated if logentries: timer.log_entries = logentries conflicts = rt.record(timer) if conflicts: errors = [] for conflict in conflicts: errors.append(conflict.name) return { "result": False, "message": _("Conflicting Timer(s) detected! %s") % " / ".join(errors) } #VPS if vpsinfo is not None: timer.vpsplugin_enabled = vpsinfo["vpsplugin_enabled"] timer.vpsplugin_overwrite = vpsinfo["vpsplugin_overwrite"] timer.vpsplugin_time = vpsinfo["vpsplugin_time"] if always_zap <> -1: if hasattr(timer,"always_zap"): timer.always_zap = always_zap == 1 except Exception, e: print e return { "result": False, "message": _("Could not add timer '%s'!") % name }
def getTimers(self): self._timer_list = [] self._processed_timers = [] baseurl = self.getBaseUrl() print "[GBIpboxRemoteTimer] get remote timer list" try: httprequest = urllib2.urlopen(baseurl + '/web/timerlist') xmldoc = minidom.parseString(httprequest.read()) timers = xmldoc.getElementsByTagName('e2timer') for timer in timers: serviceref = ServiceReference(getValueFromNode(timer, 'e2servicereference')) begin = int(getValueFromNode(timer, 'e2timebegin')) end = int(getValueFromNode(timer, 'e2timeend')) name = getValueFromNode(timer, 'e2name') description = getValueFromNode(timer, 'e2description') eit = int(getValueFromNode(timer, 'e2eit')) disabled = int(getValueFromNode(timer, 'e2disabled')) justplay = int(getValueFromNode(timer, 'e2justplay')) afterevent = int(getValueFromNode(timer, 'e2afterevent')) repeated = int(getValueFromNode(timer, 'e2repeated')) location = getValueFromNode(timer, 'e2location') tags = getValueFromNode(timer, 'e2tags').split(" ") entry = RecordTimerEntry(serviceref, begin, end, name, description, eit, disabled, justplay, afterevent, dirname = location, tags = tags, descramble = 1, record_ecm = 0, isAutoTimer = 0, always_zap = 0) entry.repeated = repeated entry.orig = RecordTimerEntry(serviceref, begin, end, name, description, eit, disabled, justplay, afterevent, dirname = location, tags = tags, descramble = 1, record_ecm = 0, isAutoTimer = 0, always_zap = 0) entry.orig.repeated = repeated if entry.shouldSkip() or entry.state == TimerEntry.StateEnded or (entry.state == TimerEntry.StateWaiting and entry.disabled): insort(self._processed_timers, entry) else: insort(self._timer_list, entry) except Exception, e: print "[GBIpboxRemoteTimer]", e
def UPDT(self, args): # <id> <settings> args = args.split(None, 1) if len(args) != 2: payload = "%d argument error" % (CODE_SYNTAX,) return self.sendLine(payload) try: timerId = int(args[0]) except ValueError: payload = "%d argument error" % (CODE_SYNTAX,) return self.sendLine(payload) list = self.getTimerList() if timerId < 1: payload = "%d argument error" % (CODE_SYNTAX,) return self.sendLine(payload) if len(list) >= timerId: oldTimer = list[timerId - 1] else: oldTimer = None try: flags, channelid, datestring, beginstring, endstring, priority, lifetime, name, description = args[1].split(':') flags = int(flags) service_ref = ServiceReference(self.channelList[int(channelid)-1]) datestruct = strptime(datestring, '%Y-%m-%d') timestruct = strptime(beginstring, '%H%M') begin = mktime((datestruct.tm_year, datestruct.tm_mon, datestruct.tm_mday, timestruct.tm_hour, timestruct.tm_min, 0, datestruct.tm_wday, datestruct.tm_yday, -1)) timestruct = strptime(endstring, '%H%M') end = mktime((datestruct.tm_year, datestruct.tm_mon, datestruct.tm_mday, timestruct.tm_hour, timestruct.tm_min, 0, datestruct.tm_wday, datestruct.tm_yday, -1)) del datestruct, timestruct except ValueError as e: payload = "%d argument error" % (CODE_SYNTAX,) return self.sendLine(payload) except KeyError as e: payload = "%d argument error" % (CODE_SYNTAX,) return self.sendLine(payload) if end < begin: end += 86400 # Add 1 day, beware - this is evil and might not work correctly due to dst timer = RecordTimerEntry(service_ref, begin, end, name, description, 0, disabled=flags & 1 == 0) if oldTimer: recordTimer.removeEntry(oldTimer) timer.justplay = oldTimer.justplay timer.afterEvent = oldTimer.afterEvent timer.dirname = oldTimer.dirname timer.tags = oldTimer.tags timer.log_entries = oldTimer.log_entries conflict = recordTimer.record(timer) if conflict is None: return self.sendTimerLine(timer, timerId, last=True) else: payload = "%d timer conflict detected, original timer lost." % (CODE_ERR_LOCAL,) return self.sendLine(payload)
def standbyTimeout(self): print "goto deep3" from RecordTimer import RecordTimerEntry RecordTimerEntry.TryQuitMainloop()
def parseTimer(self, timer, epgcache, serviceHandler, recordHandler, checkEvtLimit, evtLimit, timers, conflicting, similars, skipped, existing, timerdict, moviedict, taskname, simulateOnly=False): new = 0 modified = 0 # enable multiple timer if services or bouquets specified (eg. recording the same event on sd service and hd service) enable_multiple_timer = ( (timer.services and 's' in config.plugins.autotimer.enable_multiple_timer.value or False) or (timer.bouquets and 'b' in config.plugins.autotimer.enable_multiple_timer.value or False)) # 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.replace('\xc2\x86', '').replace('\xc2\x87', '') if timer.encoding != 'UTF-8': try: match = match.decode('UTF-8').encode(timer.encoding) except UnicodeDecodeError: pass self.isIPTV = bool( [service for service in timer.services if ":http" in service]) # As well as description, also allow timers on individual IPTV streams if timer.searchType == "description" or self.isIPTV: epgmatches = [] casesensitive = timer.searchCase == "sensitive" if not casesensitive: match = match.lower() test = [] if timer.services: test = [(service, 0, -1, -1) for service in timer.services] elif timer.bouquets: for bouquet in timer.bouquets: services = serviceHandler.list(eServiceReference(bouquet)) if services: while True: service = services.getNext() if not service.valid(): break playable = not (service.flags & (eServiceReference.isMarker | eServiceReference.isDirectory)) if playable: test.append((service.toString(), 0, -1, -1)) else: # Get all bouquets bouquetlist = [] refstr = '1:134:1:0:0:0:0:0:0:0:FROM BOUQUET \"bouquets.tv\" ORDER BY bouquet' bouquetroot = eServiceReference(refstr) mask = eServiceReference.isDirectory if config.usage.multibouquet.value: bouquets = serviceHandler.list(bouquetroot) if bouquets: while True: s = bouquets.getNext() if not s.valid(): break if s.flags & mask: info = serviceHandler.info(s) if info: bouquetlist.append(s) else: info = serviceHandler.info(bouquetroot) if info: bouquetlist.append(bouquetroot) if bouquetlist: for bouquet in bouquetlist: if not bouquet.valid(): continue if bouquet.flags & eServiceReference.isDirectory: services = serviceHandler.list(bouquet) if services: while True: service = services.getNext() if not service.valid(): break playable = not ( service.flags & (eServiceReference.isMarker | eServiceReference.isDirectory)) if playable: 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 timer.searchType == "description": 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: # IPTV streams (if not "description" search) if timer.searchType == 'exact' and match == (name if casesensitive else name.lower()) or \ timer.searchType == 'partial' and match in (name if casesensitive else name.lower()) or \ timer.searchType == 'start' and (name if casesensitive else name.lower()).startswith(match): epgmatches.append((serviceref, eit, name, begin, duration, shortdesc, extdesc)) else: # Search EPG, default to empty list if timer.searchType in typeMap: EPG_searchType = typeMap[timer.searchType] else: EPG_searchType = typeMap["partial"] epgmatches = epgcache.search( ('RITBDSE', 3000, EPG_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 preveit = False for idx, (serviceref, eit, name, begin, duration, shortdesc, extdesc) in enumerate(epgmatches): eserviceref = eServiceReference(serviceref) evt = epgcache.lookupEventId(eserviceref, eit) evtBegin = begin evtEnd = end = begin + duration if not evt: msg = "[AutoTimer] Could not create Event!" print(msg) skipped.append( (name, begin, end, str(serviceref), timer.name, msg)) 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() # If event starts in less than 60 seconds skip it # if begin < time() + 60: # print("[AutoTimer] Skipping " + name + " because it starts in less than 60 seconds") # skipped += 1 # 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: msg = "[AutoTimer] Skipping an event because of maximum days in future is reached" # print(msg) skipped.append( (name, begin, end, serviceref, timer.name, msg)) continue # If the timer actually has a timespan set it will be: # start[[hr], [min]], end[[hr], [min]], daySpan # (if it has none the timespan will be (None,)) # (see calculateDayspan() in AutoTimerComponent.py) where daySpan is # True if the timespan "ends before it starts" (so passes over # midnight). # If we have a timer for which daySpan is true and the day-offset of the # begin time for the programme/broadcast we are checking is before that # of the autotimer timespan start then we need to bring the dayofweek # check forward by 1 day when checking it. (i.e. we pretend the # recording starts a day before it does, but *just* for the dayofweek # filter check). # e.g. # Monday AT for 23:00 to 02:00 should match a programme on # Tuesday at 01:00. # The rest of the checks stay the same (which is why we don't need to # check for the autotimer timespan end being after the begin time for # the programme). # tdow = timestamp.tm_wday if (timer.timespan[0] != None) and timer.timespan[2]: begin_offset = 60 * timestamp.tm_hour + timestamp.tm_min timer_offset = 60 * timer.timespan[0][0] + timer.timespan[ 0][1] if begin_offset < timer_offset: tdow = (tdow - 1) % 7 dayofweek = str(tdow) # 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) )) or timer.checkFilter(name, shortdesc, extdesc, dayofweek): msg = "[AutoTimer] Skipping an event because of filter check" # print(msg) skipped.append((name, begin, end, serviceref, timer.name, msg)) continue if timer.hasOffset(): # Apply custom Offset begin, end = timer.applyOffset(begin, end) offsetBegin = timer.offset[0] offsetEnd = timer.offset[1] else: # Apply E2 Offset begin -= config.recording.margin_before.value * 60 end += config.recording.margin_after.value * 60 offsetBegin = config.recording.margin_before.value * 60 offsetEnd = config.recording.margin_after.value * 60 # Overwrite endtime if requested if timer.justplay and not timer.setEndtime: end = begin # Eventually change service to alternative if timer.overrideAlternatives: serviceref = timer.getAlternative(serviceref) # Append to timerlist and abort if simulating timers.append((name, begin, end, serviceref, timer.name)) if simulateOnly: continue # 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.checkSimilarity(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: msg = "[AutoTimer] Skipping an event because movie already exists" # print(msg) skipped.append( (name, begin, end, serviceref, timer.name, msg)) continue # Initialize newEntry = None oldExists = False # 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 or ( config.plugins.autotimer.try_guessing.getValue() and timeSimilarityPercent(rtimer, evtBegin, evtEnd, timer) > 80): oldExists = True # Abort if we don't want to modify timers or timer is repeated if config.plugins.autotimer.refresh.value == "none" or rtimer.repeated: # print("[AutoTimer] Won't modify existing timer because either no modification allowed or repeated timer") break if eit == preveit: break try: # protect against vps plugin not being present vps_changed = rtimer.vpsplugin_enabled != timer.vps_enabled or rtimer.vpsplugin_overwrite != timer.vps_overwrite except AttributeError: vps_changed = False if (evtBegin - offsetBegin != rtimer.begin ) or (evtEnd + offsetEnd != rtimer.end) or ( shortdesc != rtimer.description) or vps_changed: if rtimer.isAutoTimer and eit == rtimer.eit: print( "[AutoTimer] AutoTimer %s modified this automatically generated timer." % (timer.name)) # rtimer.log(501, "[AutoTimer] AutoTimer %s modified this automatically generated timer." % (timer.name)) preveit = eit else: if config.plugins.autotimer.refresh.getValue( ) != "all": print( "[AutoTimer] Won't modify existing timer because it's no timer set by us" ) break rtimer.log( 501, "[AutoTimer] Warning, AutoTimer %s messed with a timer which might not belong to it: %s ." % (timer.name, rtimer.name)) newEntry = rtimer modified += 1 self.modifyTimer(rtimer, name, shortdesc, begin, end, serviceref, eit) # rtimer.log(501, "[AutoTimer] AutoTimer modified timer: %s ." % (rtimer.name)) break else: # print("[AutoTimer] Skipping timer because it has not changed.") existing.append( (name, begin, end, serviceref, timer.name)) break elif timer.avoidDuplicateDescription >= 1 and not rtimer.disabled: if self.checkSimilarity(timer, name, rtimer.name, shortdesc, rtimer.description, extdesc, rtimer.extdesc): print( "[AutoTimer] We found a timer with similar description, skipping event" ) oldExists = True 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 for rtimer in chain.from_iterable(itervalues(timerdict)): if not rtimer.disabled: if self.checkDoubleTimers(timer, name, rtimer.name, begin, rtimer.begin, end, rtimer.end, serviceref, str(rtimer.service_ref), enable_multiple_timer): oldExists = True print( "[AutoTimer] We found a timer with same StartTime, skipping event" ) break if timer.avoidDuplicateDescription >= 2: if self.checkSimilarity(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 newEntry = RecordTimerEntry(ServiceReference(serviceref), begin, end, name, shortdesc, eit) newEntry.log( 500, "[AutoTimer] Try to add new timer based on AutoTimer %s." % (timer.name)) newEntry.log(509, "[AutoTimer] Timer start on: %s" % ctime(begin)) # Mark this entry as AutoTimer (only AutoTimers will have this Attribute set) newEntry.isAutoTimer = True newEntry.autoTimerId = timer.id # 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 = timer.destination newEntry.justplay = timer.justplay newEntry.vpsplugin_enabled = timer.vps_enabled newEntry.vpsplugin_overwrite = timer.vps_overwrite if hasattr(timer, 'always_zap') and hasattr( newEntry, 'always_zap'): newEntry.always_zap = timer.always_zap 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) if renameTimer is not None and timer.series_labeling: renameTimer(newEntry, name, evtBegin, evtEnd) else: conflictString = "" if similarTimer: conflictString = similardict[eit].conflictString msg = "[AutoTimer] Try to add similar Timer because of conflicts with %s." % ( conflictString) print(msg) newEntry.log(504, msg) # Try to add timer conflicts = recordHandler.record(newEntry) if conflicts and not timer.hasOffset( ) and not config.recording.margin_before.value and not config.recording.margin_after.value and len( conflicts) > 1: change_end = change_begin = False conflict_begin = conflicts[1].begin conflict_end = conflicts[1].end if conflict_begin == newEntry.end: newEntry.end -= 30 change_end = True elif newEntry.begin == conflict_end: newEntry.begin += 30 change_begin = True if change_end or change_begin: conflicts = recordHandler.record(newEntry) if conflicts: if change_end: newEntry.end += 30 elif change_begin: newEntry.begin -= 30 else: print( "[AutoTimer] The conflict is resolved by offset time begin/end (30 sec) for %s." % newEntry.name) 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.checkSimilarity(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: msg = "[AutoTimer] Timer disabled because of conflicts with %s." % ( conflictString) print(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) self.result = (new, modified) self.completed.append(taskname) sleep(0.5)
def addTimer(session, serviceref, begin, end, name, description, disabled, justplay, afterevent, dirname, tags, repeated, vpsinfo=None, logentries=None, eit=0, always_zap=-1, pipzap=-1, allow_duplicate=1, autoadjust=-1): rt = session.nav.RecordTimer if not dirname: dirname = preferredTimerPath() # IPTV Fix serviceref = serviceref.replace('%253a', '%3a') try: timer = RecordTimerEntry(ServiceReference(serviceref), begin, end, name, description, eit, disabled, justplay, afterevent, dirname=dirname, tags=tags) timer.repeated = repeated if logentries: timer.log_entries = logentries conflicts = rt.record(timer) if conflicts: errors = [] conflictinfo = [] for conflict in conflicts: errors.append(conflict.name) conflictinfo.append({ "serviceref": str(conflict.service_ref), "servicename": conflict.service_ref.getServiceName().replace( '\xc2\x86', '').replace('\xc2\x87', ''), "name": conflict.name, "begin": conflict.begin, "end": conflict.end, "realbegin": strftime("%d.%m.%Y %H:%M", (localtime(float(conflict.begin)))), "realend": strftime("%d.%m.%Y %H:%M", (localtime(float(conflict.end)))) }) return { "result": False, "message": _("Conflicting Timer(s) detected! %s") % " / ".join(errors), "conflicts": conflictinfo } # VPS if vpsinfo is not None: timer.vpsplugin_enabled = vpsinfo["vpsplugin_enabled"] timer.vpsplugin_overwrite = vpsinfo["vpsplugin_overwrite"] timer.vpsplugin_time = vpsinfo["vpsplugin_time"] if always_zap != -1: if hasattr(timer, "always_zap"): timer.always_zap = always_zap == 1 if hasattr(timer, "zapbeforerecord"): timer.zapbeforerecord = always_zap == 1 if hasattr(timer, "autoadjust"): if autoadjust == -1: autoadjust = config.recording.adjust_time_to_event.value and 1 or 0 autoadjust = autoadjust if hasattr(timer, "allow_duplicate"): allow_duplicate = allow_duplicate if pipzap != -1: if hasattr(timer, "pipzap"): timer.pipzap = pipzap == 1 except Exception as e: print(str(e)) return { "result": False, "message": _("Could not add timer '%s'!") % name } return {"result": True, "message": _("Timer '%s' added") % name}
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 allow_modify = True # Eventually change service to alternative if timer.overrideAlternatives: serviceref = timer.getAlternative(serviceref) if timer.series_labeling and sp_getSeasonEpisode is not None: allow_modify = False #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)) allow_modify = True 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)) allow_modify = True 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 if ServiceRecordingSettings: begin -= ServiceRecordingSettings.instance.getMarginBefore( eserviceref) end += ServiceRecordingSettings.instance.getMarginAfter( eserviceref) else: 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] rend = rtimer.end - timer.offset[1] 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 # if set option for check/save timer in filterlist and only if not found an existing timer isnewFilterEntry = False if (config.plugins.autotimer.series_save_filter.value or timer.series_save_filter) and oldExists == False: # only if use series_labeling and if sp_getSeasonEpisode was succesful if timer.series_labeling and sp_getSeasonEpisode is not None: if sp and type(sp) in (tuple, list) and len(sp) == 4: ret = self.addToFilterfile(str(sp[0]), begin, simulateOnly) if ret: if simulateOnly: doLog( "only simulate - new Timer would be saved in autotimer_filter" ) else: doLog("new Timer saved in autotimer_filter") isnewFilterEntry = True else: 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) if newEntry.begin != begin or newEntry.end != end or newEntry.name != name: modified += 1 #self.addToSearchLogfile(newEntry,"#", simulateOnly) if allow_modify: modified_for_searchlog = True if newEntry.begin != begin or newEntry.end != end or newEntry.name != name else False self.modifyTimer(newEntry, name, shortdesc, begin, end, serviceref, eit) if modified_for_searchlog: self.addToSearchLogfile(newEntry, "#", simulateOnly) msg = "[AutoTimer] AutoTimer modified timer: %s ." % ( newEntry.name) doLog(msg) newEntry.log(501, msg) else: msg = "[AutoTimer] AutoTimer modification not allowed for timer: %s ." % ( newEntry.name) doLog(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 if isnewFilterEntry: self.addToSearchLogfile(newEntry, "++", simulateOnly) else: self.addToSearchLogfile(newEntry, "+", simulateOnly) 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)) self.addToSearchLogfile(newEntry, "x", simulateOnly) 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) return (new, modified)
def addTimer(session, serviceref, begin, end, name, description, disabled, justplay, afterevent, dirname, tags, repeated, vpsinfo=None, logentries=None, eit=0, always_zap=-1): rt = session.nav.RecordTimer if not dirname: dirname = preferredTimerPath() try: timer = RecordTimerEntry(ServiceReference(serviceref), begin, end, name, description, eit, disabled, justplay, afterevent, dirname=dirname, tags=tags) timer.repeated = repeated if logentries: timer.log_entries = logentries conflicts = rt.record(timer) if conflicts: errors = [] conflictinfo = [] for conflict in conflicts: errors.append(conflict.name) conflictinfo.append({ "serviceref": str(conflict.service_ref), "servicename": conflict.service_ref.getServiceName().replace( '\xc2\x86', '').replace('\xc2\x87', ''), "name": conflict.name, "begin": conflict.begin, "end": conflict.end, "realbegin": strftime("%d.%m.%Y %H:%M", (localtime(float(conflict.begin)))), "realend": strftime("%d.%m.%Y %H:%M", (localtime(float(conflict.end)))) }) return { "result": False, "message": _("Conflicting Timer(s) detected! %s") % " / ".join(errors), "conflicts": conflictinfo } #VPS if vpsinfo is not None: timer.vpsplugin_enabled = vpsinfo["vpsplugin_enabled"] timer.vpsplugin_overwrite = vpsinfo["vpsplugin_overwrite"] timer.vpsplugin_time = vpsinfo["vpsplugin_time"] if always_zap <> -1: if hasattr(timer, "always_zap"): timer.always_zap = always_zap == 1 except Exception, e: print e return { "result": False, "message": _("Could not add timer '%s'!") % name }
def editTimer(self, param): print "[WebComponents.Timer] editTimer" #OK first we need to parse all of your Parameters #For some of them (like afterEvent or justplay) we can use default values #for others (the serviceReference or the Begin/End time of the timer #we have to quit if they are not set/have illegal values if 'sRef' not in param: return (False, "Missing Parameter: sRef") service_ref = ServiceReference(param['sRef']) repeated = int(param.get('repeated') or 0) if 'begin' not in param: return (False, "Missing Parameter: begin") begin = int(float(param['begin'])) if 'end' not in param: return (False, "Missing Parameter: end") end = int(float(param['end'])) tm = time() if tm <= begin: pass elif tm > begin and tm < end and repeated == 0: begin = time() elif repeated == 0: return (False, "Illegal Parameter value for Parameter begin : '%s'" % begin) if 'name' not in param: return (False, "Missing Parameter: name") name = param['name'] if 'description' not in param: return (False, "Missing Parameter: description") description = param['description'].replace("\n", " ") eit = param.get("eit", None) if eit is None or eit.strip() == "": eit = 0 else: try: eit = int(eit) except ValueError: return (False, "Illegal Parameter value for Parameter edit : '%s'" % eit) print "[WebComponents.Sources.Timer]: eit=%d" % eit if eit != 0: #check if the given event exists, if it doesn't return an error epgcache = eEPGCache.getInstance() event = epgcache.lookupEventId(eServiceReference(param['sRef']), eit) if event is None: return (False, "Event with id %d not found" % eit) eit = event.getEventId() disabled = False #Default to: Enabled if 'disabled' in param: if param['disabled'] == "1": disabled = True else: #TODO - maybe we can give the user some useful hint here pass justplay = False #Default to: Record if 'justplay' in param: if param['justplay'] == "1": justplay = True afterEvent = 3 #Default to Afterevent: Auto if 'afterevent' in param: if (param['afterevent'] == "0") or (param['afterevent'] == "1") or (param['afterevent'] == "2"): afterEvent = int(param['afterevent']) dirname = preferredTimerPath() if 'dirname' in param and param['dirname']: dirname = param['dirname'] tags = [] if 'tags' in param and param['tags']: tags = unescape(param['tags']).split(' ') delold = 0 if 'deleteOldOnSave' in param: delold = int(param['deleteOldOnSave']) #Try to edit an existing Timer if delold: if 'channelOld' in param and param['channelOld']: channelOld = ServiceReference(param['channelOld']) else: return (False, "Missing Parameter: channelOld") # We do need all of the following Parameters, too, for being able of finding the Timer. # Therefore so we can neither use default values in this part nor can we # continue if a parameter is missing if 'beginOld' not in param: return (False, "Missing Parameter: beginOld") beginOld = int(param['beginOld']) if 'endOld' not in param: return (False, "Missing Parameter: endOld") endOld = int(param['endOld']) #let's try to find the timer try: for timer in self.recordtimer.timer_list + self.recordtimer.processed_timers: if str(timer.service_ref) == str(channelOld): if int(timer.begin) == beginOld: if int( timer.end ) == endOld: #we've found the timer we've been searching for #set the new data timer.service_ref = service_ref timer.begin = begin timer.end = end timer.name = name timer.description = description timer.eit = eit timer.disabled = disabled timer.justplay = justplay timer.afterEvent = afterEvent timer.dirname = dirname timer.tags = tags timer.repeated = repeated timer.processRepeated() #sanity check timersanitycheck = TimerSanityCheck( self.session.nav.RecordTimer.timer_list, timer) conflicts = None if not timersanitycheck.check(): conflicts = timersanitycheck.getSimulTimerList( ) if conflicts is not None: for x in conflicts: if x.setAutoincreaseEnd(entry): self.session.nav.RecordTimer.timeChanged( x) if not timersanitycheck.check(): conflicts = timersanitycheck.getSimulTimerList( ) if conflicts is None: self.recordtimer.timeChanged( timer) #go and save it print "[WebComponents.Timer] editTimer: Timer changed!" return (True, "Timer '%s' changed" % (timer.name)) else: print "[WebComponents.Timer] editTimer conflicting Timers: %s" % ( conflicts) msg = "" for t in conflicts: msg = "%s / %s" % (msg, t.name) return ( False, "Conflicting Timer(s) detected! %s" % (msg)) except Exception as e: #obviously some value was not good, return an error print e return (False, "Changing the timer for '%s' failed!" % name) return ( False, "Could not find timer '%s' with given start and end time!" % name) #Try adding a new Timer try: #Create a new instance of recordtimerentry timer = RecordTimerEntry(service_ref, begin, end, name, description, eit, disabled, justplay, afterEvent, dirname=dirname, tags=tags) timer.repeated = repeated #add the new timer conflicts = self.recordtimer.record(timer) if conflicts is None: return (True, "Timer '%s' added" % (timer.name)) else: print "[WebComponents.Timer] editTimer conflicting Timers: %s" % ( conflicts) msg = "" for timer in conflicts: msg = "%s / %s" % (msg, timer.name) return (False, "Conflicting Timer(s) detected! %s" % (msg)) except Exception, e: #something went wrong, most possibly one of the given paramater-values was wrong print "[WebComponents.Timer] editTimer exception: %s" % (e) return (False, "Could not add timer '%s'!" % name)
def timerAdd(self): cur = self["list"].getCurrent() event = cur[0] if event is None: return serviceref = cur[1] isRecordEvent = isRepeat = firstNextRepeatEvent = isRunning = False eventid = event.getEventId() begin = event.getBeginTime() end = begin + event.getDuration() refstr = ':'.join(serviceref.ref.toString().split(':')[:11]) for timer in self.session.nav.RecordTimer.getAllTimersList(): needed_ref = ':'.join( timer.service_ref.ref.toString().split(':')[:11]) == refstr if needed_ref and timer.eit == eventid and ( begin < timer.begin <= end or timer.begin <= begin <= timer.end): isRecordEvent = True break elif needed_ref and timer.repeated and self.session.nav.RecordTimer.isInRepeatTimer( timer, event): isRecordEvent = True break if isRecordEvent: isRepeat = timer.repeated prev_state = timer.state isRunning = prev_state in (1, 2) title_text = isRepeat and _( "Attention, this is repeated timer!\n") or "" firstNextRepeatEvent = isRepeat and ( begin < timer.begin <= end or timer.begin <= begin <= timer.end) and not timer.justplay menu = [(_("Delete timer"), "delete"), (_("Edit timer"), "edit")] buttons = ["red", "green"] if not isRunning: if firstNextRepeatEvent and timer.isFindRunningEvent( ) and not timer.isFindNextEvent(): menu.append((_("Options disable timer"), "disablerepeat")) else: menu.append((_("Disable timer"), "disable")) buttons.append("yellow") elif prev_state == 2 and firstNextRepeatEvent: menu.append( (_("Options disable timer"), "disablerepeatrunning")) buttons.append("yellow") menu.append((_("Timer Overview"), "timereditlist")) def timerAction(choice): if choice is not None: if choice[1] == "delete": self.removeTimer(timer) elif choice[1] == "edit": self.session.openWithCallback(self.finishedEdit, TimerEntry, timer) elif choice[1] == "disable": self.disableTimer(timer, prev_state) elif choice[1] == "timereditlist": self.session.open(TimerEditList) elif choice[1] == "disablerepeatrunning": self.disableTimer(timer, prev_state, repeat=True, record=True) elif choice[1] == "disablerepeat": self.disableTimer(timer, prev_state, repeat=True) self.session.openWithCallback( timerAction, ChoiceBox, title=title_text + _("Select action for timer '%s'.") % timer.name, list=menu, keys=buttons) else: newEntry = RecordTimerEntry(serviceref, checkOldTimers=True, dirname=preferredTimerPath(), *parseEvent(event)) newEntry.justplay = config.recording.timer_default_type.value == "zap" newEntry.always_zap = config.recording.timer_default_type.value == "zap+record" self.session.openWithCallback(self.finishedAdd, TimerEntry, newEntry)
def recordNow(session, infinite): rt = session.nav.RecordTimer serviceref = session.nav.getCurrentlyPlayingServiceReference().toString() try: event = session.nav.getCurrentService().info().getEvent(0) except Exception: event = None if not event and not infinite: return { "result": False, "message": _("No event found! Not recording!") } if event: (begin, end, name, description, eit) = parseEvent(event) begin = time() msg = _("Instant record for current Event started") else: name = "instant record" description = "" eit = 0 if infinite: begin = time() end = begin + 3600 * 10 msg = _("Infinite Instant recording started") timer = RecordTimerEntry( ServiceReference(serviceref), begin, end, name, description, eit, False, False, 0, dirname=preferredInstantRecordPath() ) timer.dontSave = True if rt.record(timer): return { "result": False, "message": _("Timer conflict detected! Not recording!") } nt = { "serviceref": str(timer.service_ref), "servicename": timer.service_ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', ''), "eit": timer.eit, "name": timer.name, "begin": timer.begin, "end": timer.end, "duration": timer.end - timer.begin } return { "result": True, "message": msg, "newtimer": nt }
def parseEPG(self, simulateOnly = False): if NavigationInstance.instance is None: print("[AutoTimer] Navigation is not available, can't parse EPG") return (0, 0, 0, [], [], []) total = 0 new = 0 modified = 0 timers = [] conflicting = [] similar = defaultdict(list) # Contains the the marked similar eits and the conflicting strings similars = [] # Contains the added similar timers # NOTE: the config option specifies "the next X days" which means today (== 1) + X delta = timedelta(days = config.plugins.autotimer.maxdaysinfuture.value + 1) evtLimit = mktime((date.today() + delta).timetuple()) checkEvtLimit = delta.days > 1 del delta # Read AutoTimer configuration self.readXml() # Get E2 instances epgcache = eEPGCache.getInstance() serviceHandler = eServiceCenter.getInstance() recordHandler = NavigationInstance.instance.RecordTimer # Save Recordings in a dict to speed things up a little # We include processed timers as we might search for duplicate descriptions # The recordict is always filled #Question: It might be better to name it timerdict #Question: Move to a separate function getTimerDict() #Note: It is also possible to use RecordTimer isInTimer(), but we won't get the timer itself on a match recorddict = defaultdict(list) for timer in chain(recordHandler.timer_list, recordHandler.processed_timers): if timer and timer.service_ref: if timer.eit is not None: event = epgcache.lookupEventId(timer.service_ref.ref, timer.eit) extdesc = event and event.getExtendedDescription() or '' timer.extdesc = extdesc elif not hasattr(timer, 'extdesc'): timer.extdesc = '' recorddict[str(timer.service_ref)].append(timer) # Create dict of all movies in all folders used by an autotimer to compare with recordings # The moviedict will be filled only if one AutoTimer is configured to avoid duplicate description for any recordings #Question: It might be better to name it recorddict moviedict = defaultdict(list) # Iterate Timer for timer in self.getEnabledTimerList(): # 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 if timer.encoding != 'UTF-8': try: match = match.decode('UTF-8').encode(timer.encoding) except UnicodeDecodeError: pass if timer.searchType == "description": test = [] epgmatches = [] casesensitive = timer.searchCase == "sensitive" if not casesensitive: match = match.lower() #if timer.services or timer.bouquets: # Service filter defined # Search only using the specified services for service in timer.services: test.append( (service, 0, -1, -1 ) ) mask = (eServiceReference.isMarker | eServiceReference.isDirectory) 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: #else: # No service filter defined # Search within all services - could be very slow # Get all bouquets bouquetlist = [] refstr = '1:134:1:0:0:0:0:0:0:0:FROM BOUQUET \"bouquets.tv\" ORDER BY bouquet' bouquetroot = eServiceReference(refstr) mask = eServiceReference.isDirectory if config.usage.multibouquet.value: bouquets = serviceHandler.list(bouquetroot) if bouquets: while True: s = bouquets.getNext() if not s.valid(): break if s.flags & mask: info = serviceHandler.info(s) if info: bouquetlist.append((info.getName(s), s)) else: info = serviceHandler.info(bouquetroot) if info: bouquetlist.append((info.getName(bouquetroot), bouquetroot)) # Get all services 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 ) ) 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', 1000, typeMap[timer.searchType], match, caseMap[timer.searchCase])) or [] # Sort list of tuples by begin time 'B' epgmatches.sort(key=itemgetter(3)) # Reset the the marked similar servicerefs similar.clear() # Loop over all EPG matches for idx, ( serviceref, eit, name, begin, duration, shortdesc, extdesc ) in enumerate( epgmatches ): # Loop over all EPG matches for idx, ( serviceref, eit, name, begin, duration, shortdesc, extdesc ) in enumerate( epgmatches ): #Question: Do we need this? #Question: Move to separate function getRealService() 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 # 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 similar: 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 to 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) \ )) or 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 # Eventually change service to alternative if timer.overrideAlternatives: serviceref = timer.getAlternative(serviceref) total += 1 # Append to timerlist and abort if simulating timers.append((name, begin, end, serviceref, timer.name)) if simulateOnly: continue # Check for existing recordings in directory if timer.avoidDuplicateDescription == 3: # Reset movie Exists movieExists = False if dest and dest not in moviedict: #Question: Move to a separate function getRecordDict() print("[AutoTimer] listing of movies in " + dest + " failed") event = info.getEvent(movieref) if event is None: continue "shortdesc": info.getInfoString(movieref, iServiceInformation.sDescription), "extdesc": event.getExtendedDescription() or '' # XXX: does event.getExtendedDescription() actually return None on no description or an empty string? for movieinfo in moviedict.get(dest, ()): if movieinfo.get("name") == name \ and movieinfo.get("shortdesc") == shortdesc: # Some channels indicate replays in the extended descriptions # If the similarity percent is higher then 0.8 it is a very close match extdescM = movieinfo.get("extdesc") if ( len(extdesc) == len(extdescM) and extdesc == extdescM ) \ or ( 0.8 < SequenceMatcher(lambda x: x == " ",extdesc, extdescM).ratio() ): print("[AutoTimer] We found a matching recorded movie, skipping event:", name) movieExists = True break if movieExists: continue # Initialize newEntry = None oldExists = False # 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 recorddict.get(serviceref, ()): if rtimer.eit == eit or config.plugins.autotimer.try_guessing.value and getTimeDiff(rtimer, evtBegin, evtEnd) > ((duration/10)*8): oldExists = True # Abort if we don't want to modify timers or timer is repeated if config.plugins.autotimer.refresh.value == "none" or rtimer.repeated: print("[AutoTimer] Won't modify existing timer because either no modification allowed or repeated timer") break if hasattr(rtimer, "isAutoTimer"): rtimer.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") break rtimer.log(501, "[AutoTimer] Warning, AutoTimer %s messed with a timer which might not belong to it." % (timer.name)) newEntry = rtimer modified += 1 # Modify values saved in timer newEntry.name = name newEntry.description = shortdesc newEntry.begin = int(begin) newEntry.end = int(end) newEntry.service_ref = ServiceReference(serviceref) break elif timer.avoidDuplicateDescription >= 1 \ and not rtimer.disabled \ and rtimer.name == name \ and rtimer.description == shortdesc: # Some channels indicate replays in the extended descriptions # If the similarity percent is higher then 0.8 it is a very close match if ( len(extdesc) == len(rtimer.extdesc) and extdesc == rtimer.extdesc ) \ or ( 0.8 < SequenceMatcher(lambda x: x == " ",extdesc, rtimer.extdesc).ratio() ): 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(recorddict) ): if not rtimer.disabled \ and rtimer.name == name \ and rtimer.description == shortdesc: # Some channels indicate replays in the extended descriptions # If the similarity percent is higher then 0.8 it is a very close match if ( len(extdesc) == len(rtimer.extdesc) and extdesc == rtimer.extdesc ) \ or ( 0.8 < SequenceMatcher(lambda x: x == " ",extdesc, rtimer.extdesc).ratio() ): 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 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 = timer.destination newEntry.justplay = timer.justplay newEntry.tags = timer.tags newEntry.vpsplugin_enabled = timer.vps_enabled newEntry.vpsplugin_overwrite = timer.vps_overwrite 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 = similar[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: 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 conflicts and 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 shortdesc == shortdescS: # Some channels indicate replays in the extended descriptions # If the similarity percent is higher then 0.8 it is a very close match if ( len(extdesc) == len(extdescS) and extdesc == extdescS ) \ or ( 0.8 < SequenceMatcher(lambda x: x == " ",extdesc, extdescS).ratio() ): # Check if the similar is already known if eitS not in similar: print("[AutoTimer] Found similar Timer: " + name) # Store the actual and similar eit and conflictString, so it can be handled later newEntry.conflictString = conflictString similar[eit] = newEntry similar[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 break else: similarTimer = False newEntry = similar[eitS] break if conflicts is None: timer.decrementCounter() new += 1 newEntry.extdesc = extdesc recorddict[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)) similar.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) return (total, new, modified, timers, conflicting, similars) # Supporting functions def addDirectoryToMovieDict(self, moviedict, dest, serviceHandler): movielist = serviceHandler.list(eServiceReference("2:0:1:0:0:0:0:0:0:0:" + dest)) if movielist is None: print("[AutoTimer] listing of movies in " + dest + " failed") else: append = moviedict[dest].append while 1: movieref = movielist.getNext() if not movieref.valid(): break if movieref.flags & eServiceReference.mustDescent: continue info = serviceHandler.info(movieref) if info is None: continue event = info.getEvent(movieref) if event is None: continue append({ "name": info.getName(movieref), "shortdesc": info.getInfoString(movieref, iServiceInformation.sDescription), "extdesc": event.getExtendedDescription() or '' # XXX: does event.getExtendedDescription() actually return None on no description or an empty string? }) def checkSimilarity(self, timer, name1, name2, shortdesc1, shortdesc2, extdesc1, extdesc2): foundTitle = (name1 == name2) foundShort = (shortdesc1 == shortdesc2) if timer.searchForDuplicateDescription > 0 else True foundExt = True # NOTE: only check extended if short description already is a match because otherwise # it won't evaluate to True anyway if timer.searchForDuplicateDescription == 2 and foundShort: # Some channels indicate replays in the extended descriptions # If the similarity percent is higher then 0.8 it is a very close match foundExt = ( len(extdesc1) == len(extdesc2) and extdesc1 == extdesc2 ) \ or ( 0.8 < SequenceMatcher(lambda x: x == " ",extdesc1, extdesc2).ratio() ) return (total, new, modified, timers, conflicting, similars)
def addTimer(session, serviceref, begin, end, name, description, disabled, justplay, afterevent, dirname, tags, repeated, vpsinfo=None, logentries=None, eit=0, always_zap=-1): rt = session.nav.RecordTimer if not dirname: dirname = preferredTimerPath() try: timer = RecordTimerEntry( ServiceReference(serviceref), begin, end, name, description, eit, disabled, justplay, afterevent, dirname=dirname, tags=tags) timer.repeated = repeated if logentries: timer.log_entries = logentries conflicts = rt.record(timer) if conflicts: errors = [] conflictinfo = [] for conflict in conflicts: errors.append(conflict.name) conflictinfo.append({ "serviceref": str(conflict.service_ref), "servicename": conflict.service_ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', ''), "name": conflict.name, "begin" : conflict.begin, "end" : conflict.end, "realbegin":strftime("%d.%m.%Y %H:%M", (localtime(float(conflict.begin)))), "realend":strftime("%d.%m.%Y %H:%M", (localtime(float(conflict.end)))) }) return { "result": False, "message": _("Conflicting Timer(s) detected! %s") % " / ".join(errors), "conflicts" : conflictinfo } #VPS if vpsinfo is not None: timer.vpsplugin_enabled = vpsinfo["vpsplugin_enabled"] timer.vpsplugin_overwrite = vpsinfo["vpsplugin_overwrite"] timer.vpsplugin_time = vpsinfo["vpsplugin_time"] if always_zap <> -1: if hasattr(timer,"always_zap"): timer.always_zap = always_zap == 1 except Exception, e: print e return { "result": False, "message": _("Could not add timer '%s'!") % name }
def parseTimer(self, timer, epgcache, serviceHandler, recordHandler, checkEvtLimit, evtLimit, timers, conflicting, similars, skipped, timerdict, moviedict, simulateOnly=False): new = 0 modified = 0 # enable multiple timer if services or bouquets specified (eg. recording the same event on sd service and hd service) enable_multiple_timer = ((timer.services and 's' in config.plugins.autotimer.enable_multiple_timer.value or False) or (timer.bouquets and 'b' in config.plugins.autotimer.enable_multiple_timer.value or False)) # Workaround to allow search for umlauts if we know the encoding 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 == "favoritedesc": epgmatches = [] casesensitive = timer.searchCase == "sensitive" if not casesensitive: match = match.lower() test = [] if timer.services or timer.bouquets: if timer.services: test = [(service, 0, -1, -1) for service in timer.services] if timer.bouquets: for bouquet in timer.bouquets: services = serviceHandler.list(eServiceReference(bouquet)) if services: while True: service = services.getNext() if not service.valid(): break playable = not (service.flags & (eServiceReference.isMarker | eServiceReference.isDirectory)) or (service.flags & eServiceReference.isNumberedMarker) if playable: test.append((service.toString(), 0, -1, -1)) else: # 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 and not s.flags & eServiceReference.isInvisible: info = serviceHandler.info(s) if info: bouquetlist.append(s) else: service_types_tv = '1:7:1:0:0:0:0:0:0:0:(type == 1) || (type == 17) || (type == 22) || (type == 25) || (type == 31) || (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(bouquetroot) if bouquetlist: for bouquet in bouquetlist: if not bouquet.valid(): break if bouquet.flags & eServiceReference.isDirectory: services = serviceHandler.list(bouquet) if services: while True: service = services.getNext() if not service.valid(): break playable = not (service.flags & (eServiceReference.isMarker | eServiceReference.isDirectory)) or (service.flags & eServiceReference.isNumberedMarker) if playable: 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()) or match in (name if casesensitive else name.lower()): epgmatches.append((serviceref, eit, name, begin, duration, shortdesc, extdesc)) else: # Search EPG, default to empty list epgmatches = epgcache.search(('RITBDSE', int(config.plugins.autotimer.max_search_events_match.value), 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() # timer destination dir dest = timer.destination or config.usage.default_path.value evtBegin = begin evtEnd = end = begin + duration doLog("[AutoTimer] possible epgmatch %s" % (name)) doLog("[AutoTimer] Serviceref %s" % serviceref) eserviceref = eServiceReference(serviceref) evt = epgcache.lookupEventId(eserviceref, eit) if not evt: doLog("[AutoTimer] Could not create Event!") skipped.append((name, begin, end, 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("[AutoTimer] Serviceref2 %s" % serviceref) # 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 and timer.descShortEqualExt and extdesc: 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) # Update dayofweek when programmes that cross midnight and have a dayofweek filter end_timestamp = localtime(end) end_dayofweek = str(end_timestamp.tm_wday) if end_dayofweek != dayofweek: dayofweek = end_dayofweek # 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 oldEntry = None oldExists = False allow_modify = True newAT = None # Eventually change service to alternative if timer.overrideAlternatives: serviceref = timer.getAlternative(serviceref) if timer.series_labeling and sp_getSeasonEpisode is not None: allow_modify = False doLog("[AutoTimer SeriesPlugin] Request name, desc, path %s %s %s" % (name, shortdesc, dest)) sp = sp_getSeasonEpisode(serviceref, name, evtBegin, evtEnd, shortdesc, dest, True) if sp and type(sp) in (tuple, list) and len(sp) > 3: name = sp[0] or name shortdesc = sp[1] or shortdesc dest = sp[2] or dest doLog(str(sp[3])) allow_modify = True doLog("[AutoTimer SeriesPlugin] 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("[AutoTimer SeriesPlugin] Request name, desc, path %s %s %s" % (timer.name, shortdesc, dest)) sp = sp_getSeasonEpisode(serviceref, timer.name, evtBegin, evtEnd, shortdesc, dest, True) if sp and type(sp) in (tuple, list) and len(sp) > 3: name = sp[0] or name shortdesc = sp[1] or shortdesc dest = sp[2] or dest doLog(str(sp[3])) allow_modify = True doLog("[AutoTimer SeriesPlugin] Returned name, desc, path %s %s %s" % (name, shortdesc, dest)) 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) offsetBegin = timer.offset[0] offsetEnd = timer.offset[1] else: # Apply E2 Offset begin -= config.recording.margin_before.value * 60 end += config.recording.margin_after.value * 60 offsetBegin = config.recording.margin_before.value * 60 offsetEnd = config.recording.margin_after.value * 60 # Overwrite endtime if requested if timer.justplay and not timer.setEndtime: end = begin evtEnd = evtBegin # 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.checkSimilarity(timer, name, movieinfo.get("name"), shortdesc, movieinfo.get("shortdesc"), extdesc, movieinfo.get("extdesc"), isMovie=True): 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, ()): try: # protect against vps plugin not being present vps_changed = hasVps and (rtimer.vpsplugin_enabled != timer.vps_enabled or rtimer.vpsplugin_overwrite != timer.vps_overwrite) except: vps_changed = False time_changed = (evtBegin - offsetBegin != rtimer.begin) or (evtEnd + offsetEnd != rtimer.end) desc_changed = (timer.avoidDuplicateDescription >= 1 and shortdesc and rtimer.description and shortdesc != rtimer.description) or (timer.avoidDuplicateDescription >= 2 and extdesc and rtimer.extdesc and extdesc != rtimer.extdesc) if rtimer.eit == eit: oldExists = True doLog("[AutoTimer] We found a timer based on eit") if time_changed or desc_changed or vps_changed: newEntry = rtimer oldEntry = [rtimer.name, rtimer.description, rtimer.extdesc, rtimer.begin, rtimer.end, rtimer.service_ref, rtimer.eit, rtimer.disabled] break elif config.plugins.autotimer.try_guessing.value: if timeSimilarityPercent(rtimer, evtBegin, evtEnd, timer) > 80: oldExists = True doLog("[AutoTimer] We found a timer based on time guessing") if time_changed or desc_changed or vps_changed: newEntry = rtimer oldEntry = [rtimer.name, rtimer.description, rtimer.extdesc, rtimer.begin, rtimer.end, rtimer.service_ref, rtimer.eit, rtimer.disabled] break if oldExists is None and timer.avoidDuplicateDescription >= 1 and not rtimer.disabled: # searchForDuplicateDescription is 1 - check short description / searchForDuplicateDescription is 2 - check extended description if self.checkSimilarity(timer, name, rtimer.name, shortdesc, rtimer.description, extdesc, rtimer.extdesc): 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 for rtimer in chain.from_iterable(itervalues(timerdict)): if not rtimer.disabled: if self.checkDoubleTimers(timer, name, rtimer.name, begin, rtimer.begin, end, rtimer.end, serviceref, rtimer.service_ref.ref.toString(), enable_multiple_timer): oldExists = True print("[AutoTimer] We found a timer with same start time, skipping event") break if timer.avoidDuplicateDescription >= 2: if self.checkSimilarity(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 # if set option for check/save timer in filterlist and only if not found an existing timer isnewFilterEntry = False if (config.plugins.autotimer.series_save_filter.value or timer.series_save_filter) and not oldExists: if timer.series_labeling and sp_getSeasonEpisode is not None: if sp and type(sp) in (tuple, list) and len(sp) == 4: ret = self.addToFilterfile(str(sp[0]), begin, simulateOnly) if sp and type(sp) in (tuple, list) and len(sp) > 3: filter_title = str(sp[0]) if len(sp) > 4: filter_title = "{series:s} - S{season:02d}E{rawepisode:s} - {title:s}".format(**sp[4]) ret = self.addToFilterfile(filter_title, begin, simulateOnly, str(sp[0])) if ret: if simulateOnly: doLog("[AutoTimer SeriesPlugin] only simulate - new Timer would be saved in autotimer_filter") else: doLog("[AutoTimer SeriesPlugin] new Timer saved in autotimer_filter") isnewFilterEntry = True else: 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 "autotimer" in newEntry.flags: 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("[AutoTimer] 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) changed = newEntry.begin != begin or newEntry.end != end or newEntry.name != name if allow_modify: if oldExists and newEntry.service_ref.ref.toString() == serviceref and newEntry.eit == eit and newEntry.name == name and newEntry.begin < begin and newEntry.end < end and (0 < begin - newEntry.end <= 600): begin = newEntry.begin doLog("[AutoTimer] This same eit and different times end - update only end") if self.modifyTimer(newEntry, name, shortdesc, begin, end, serviceref, eit, base_timer=timer): msg = "[AutoTimer] AutoTimer modified timer: %s ." % (newEntry.name) doLog(msg) newEntry.log(501, msg) if changed: self.addToSearchLogfile(newEntry, "#", simulateOnly) modified += 1 else: msg = "[AutoTimer] AutoTimer modification not allowed for timer %s because conflicts or double timer." % (newEntry.name) doLog(msg) if oldEntry: self.setOldTimer(newEntry, oldEntry) doLog("[AutoTimer] conflict for modification timer %s detected return to old timer" % (newEntry.name)) continue else: msg = "[AutoTimer] AutoTimer modification not allowed for timer: %s ." % (newEntry.name) doLog(msg) continue else: newEntry = RecordTimerEntry(ServiceReference(serviceref), begin, end, name, shortdesc, eit) newAT = True msg = "[AutoTimer] Try to add new timer based on AutoTimer %s." % (timer.name) doLog(msg) newEntry.log(500, msg) msg = "[AutoTimer] Timer start on: %s" % ctime(begin) doLog(msg) newEntry.log(509, msg) # Mark this entry as AutoTimer newEntry.flags.add("autotimer") # Mark this entry as timer name newEntry.flags.add(stringToXML(timer.name)) # 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 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: if 'AutoTimer' not in tags: 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(" ", "_") if tagname not in tags: tags.append(tagname) newEntry.tags = tags if oldExists and newAT is None: if self.isResolvedConflict(newEntry): recordHandler.timeChanged(newEntry) else: if oldEntry: self.setOldTimer(newEntry, oldEntry) doLog("[AutoTimer] rechecking - conflict for timer %s detected return to old timer" % (newEntry.name)) continue elif newAT: newAT = newEntry 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) # add new timer in AT timer list atDoubleTimer = False refstr = ':'.join(newEntry.service_ref.ref.toString().split(':')[:11]) for at in addNewTimers: needed_ref = ':'.join(at.service_ref.ref.toString().split(':')[:11]) == refstr if needed_ref and at.eit == newEntry.eit and (newEntry.begin < at.begin <= newEntry.end or at.begin <= newEntry.begin <= at.end): atDoubleTimer = True break if atDoubleTimer: doLog("[AutoTimer] ignore double new auto timer %s." % newEntry.name) continue else: addNewTimers.append(newEntry) # Try to add timer conflicts = recordHandler.record(newEntry) if conflicts and not timer.hasOffset() and not config.recording.margin_before.value and not config.recording.margin_after.value and len(conflicts) > 1: change_end = change_begin = False conflict_begin = conflicts[1].begin conflict_end = conflicts[1].end if conflict_begin == newEntry.end: newEntry.end -= 30 change_end = True elif newEntry.begin == conflict_end: newEntry.begin += 30 change_begin = True if change_end or change_begin: conflicts = recordHandler.record(newEntry) if conflicts: if change_end: newEntry.end += 30 elif change_begin: newEntry.begin -= 30 else: doLog("[AutoTimer] The conflict is resolved by offset time begin/end (30 sec) for %s." % newEntry.name) 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.checkSimilarity(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() if newEntry in (recordHandler.timer_list[:] + recordHandler.processed_timers[:]): new += 1 if isnewFilterEntry: self.addToSearchLogfile(newEntry, "++", simulateOnly) else: self.addToSearchLogfile(newEntry, "+", simulateOnly) 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() else: doLog("[AutoTimer] ignore double timer %s." % newEntry.name) # 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 if newEntry in (recordHandler.timer_list[:] + recordHandler.processed_timers[:]): recordHandler.timeChanged(newEntry) else: # 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) elif newAT != newEntry and newEntry in (recordHandler.timer_list[:] + recordHandler.processed_timers[:]): if not self.isResolvedConflict(newEntry): newEntry.disabled = True recordHandler.timeChanged(newEntry) doLog("[AutoTimer] Unknown conflict, disable this timer %s." % newEntry.name) return (new, modified)
def editTimer(self, param): print "[WebComponents.Timer] editTimer" #OK first we need to parse all of your Parameters #For some of them (like afterEvent or justplay) we can use default values #for others (the serviceReference or the Begin/End time of the timer #we have to quit if they are not set/have illegal values if 'sRef' not in param: return (False, "Missing Parameter: sRef") service_ref = ServiceReference(param['sRef']) repeated = int(param.get('repeated') or 0) if 'begin' not in param: return (False, "Missing Parameter: begin") begin = int(float(param['begin'])) if 'end' not in param: return (False, "Missing Parameter: end") end = int(float(param['end'])) tm = time() if tm <= begin: pass elif tm > begin and tm < end and repeated == 0: begin = time() elif repeated == 0: return (False, "Illegal Parameter value for Parameter begin : '%s'" % begin) if 'name' not in param: return (False, "Missing Parameter: name") name = param['name'] if 'description' not in param: return (False, "Missing Parameter: description") description = param['description'].replace("\n", " ") disabled = False #Default to: Enabled if 'disabled' in param: if param['disabled'] == "1": disabled = True else: #TODO - maybe we can give the user some useful hint here pass justplay = False #Default to: Record if 'justplay' in param: if param['justplay'] == "1": justplay = True afterEvent = 3 #Default to Afterevent: Auto if 'afterevent' in param: if (param['afterevent'] == "0") or (param['afterevent'] == "1") or (param['afterevent'] == "2"): afterEvent = int(param['afterevent']) dirname = config.movielist.last_timer_videodir.value if 'dirname' in param and param['dirname']: dirname = param['dirname'] tags = [] if 'tags' in param and param['tags']: tags = unescape(param['tags']).split(' ') delold = 0 if 'deleteOldOnSave' in param: delold = int(param['deleteOldOnSave']) #Try to edit an existing Timer if delold: if 'channelOld' in param and param['channelOld']: channelOld = ServiceReference(param['channelOld']) else: return (False, "Missing Parameter: channelOld") # We do need all of the following Parameters, too, for being able of finding the Timer. # Therefore so we can neither use default values in this part nor can we # continue if a parameter is missing if 'beginOld' not in param: return (False, "Missing Parameter: beginOld") beginOld = int(param['beginOld']) if 'endOld' not in param: return (False, "Missing Parameter: endOld") endOld = int(param['endOld']) #let's try to find the timer try: for timer in self.recordtimer.timer_list + self.recordtimer.processed_timers: if str(timer.service_ref) == str(channelOld): if int(timer.begin) == beginOld: if int(timer.end) == endOld: #we've found the timer we've been searching for #Let's apply the new values timer.service_ref = service_ref timer.begin = int(begin) timer.end = int(end) timer.name = name timer.description = description timer.disabled = disabled timer.justplay = justplay timer.afterEvent = afterEvent timer.repeated = repeated timer.dirname = dirname timer.tags = tags #send the changed timer back to enigma2 and hope it's good self.session.nav.RecordTimer.timeChanged(timer) print "[WebComponents.Timer] editTimer: Timer changed!" return (True, "Timer %s has been changed!" % (timer.name)) except Exception: #obviously some value was not good, return an error return (False, "Changing the timer for '%s' failed!" % name) return ( False, "Could not find timer '%s' with given start and end time!" % name) #Try adding a new Timer try: #Create a new instance of recordtimerentry timer = RecordTimerEntry(service_ref, begin, end, name, description, 0, disabled, justplay, afterEvent, dirname=dirname, tags=tags) timer.repeated = repeated #add the new timer self.recordtimer.record(timer) return (True, "Timer added successfully!") except Exception: #something went wrong, most possibly one of the given paramater-values was wrong return (False, "Could not add timer '%s'!" % name) return (False, "Unexpected Error")
def getInfo(session=None, need_fullinfo=False): # TODO: get webif versione somewhere! info = {} global STATICBOXINFO if not (STATICBOXINFO is None or need_fullinfo): return STATICBOXINFO info['brand'] = getBoxBrand() info['model'] = getBoxType() info['platform'] = boxbranding.getMachineBuild() try: info['procmodel'] = getBoxProc() except: # noqa: E722 info['procmodel'] = boxbranding.getMachineProcModel() try: info['procmodeltype'] = getBoxProcType() except: # noqa: E722 info['procmodeltype'] = None try: info['lcd'] = getLcd() except: # noqa: E722 info['lcd'] = 0 try: info['grabpip'] = getGrabPip() except: # noqa: E722 info['grabpip'] = 0 cpu = about.getCPUInfoString() info['chipset'] = cpu info['cpubrand'] = about.getCPUBrand() info['socfamily'] = boxbranding.getSoCFamily() info['cpuarch'] = about.getCPUArch() if config.OpenWebif.about_benchmark.value is True: info['cpubenchmark'] = about.getCPUBenchmark() else: info['cpubenchmark'] = _("Disabled in configuration") info['flashtype'] = about.getFlashType() memFree = 0 for line in open("/proc/meminfo", 'r'): parts = line.split(':') key = parts[0].strip() if key == "MemTotal": info['mem1'] = parts[1].strip().replace("kB", _("kB")) elif key in ("MemFree", "Buffers", "Cached"): memFree += int(parts[1].strip().split(' ', 1)[0]) info['mem2'] = "%s %s" % (memFree, _("kB")) info['mem3'] = _("%s free / %s total") % (info['mem2'], info['mem1']) info['uptime'] = about.getBoxUptime() info["webifver"] = OPENWEBIFVER info['imagedistro'] = boxbranding.getImageDistro() info['oever'] = boxbranding.getImageBuild() info['visionversion'] = boxbranding.getVisionVersion() info['visionrevision'] = boxbranding.getVisionRevision() info['visionmodule'] = about.getVisionModule() if fileExists("/etc/openvision/multiboot"): multibootflag = open("/etc/openvision/multiboot", "r").read().strip() if multibootflag == "1": info['multiboot'] = _("Yes") else: info['multiboot'] = _("No") else: info['multiboot'] = _("Yes") info['enigmaver'] = getEnigmaVersionString() info['driverdate'] = about.getDriverInstalledDate() info['kernelver'] = boxbranding.getKernelVersion() info['dvbapitype'] = about.getDVBAPI() info['gstreamerversion'] = about.getGStreamerVersionString(cpu) info['ffmpegversion'] = about.getFFmpegVersionString() info['pythonversion'] = about.getPythonVersionString() try: info['hwserial'] = getHWSerial() except: # noqa: E722 info['hwserial'] = None if (info['hwserial'] is None or info['hwserial'] == "unknown"): info['hwserial'] = about.getCPUSerial() try: info['boxrctype'] = getBoxRCType() except: # noqa: E722 info['boxrctype'] = None if (info['boxrctype'] is None or info['boxrctype'] == "unknown"): if fileExists("/usr/bin/remotecfg"): info['boxrctype'] = _("Amlogic remote") elif fileExists("/usr/sbin/lircd"): info['boxrctype'] = _("LIRC remote") info['ovrctype'] = boxbranding.getRCType() info['ovrcname'] = boxbranding.getRCName() info['ovrcidnum'] = boxbranding.getRCIDNum() info['transcoding'] = boxbranding.getHaveTranscoding() info['multitranscoding'] = boxbranding.getHaveMultiTranscoding() info['displaytype'] = boxbranding.getDisplayType() info['updatedatestring'] = about.getUpdateDateString() info['enigmadebuglvl'] = eGetEnigmaDebugLvl() info['imagearch'] = boxbranding.getImageArch() info['imagefolder'] = boxbranding.getImageFolder() info['imagefilesystem'] = boxbranding.getImageFileSystem() info['feedsurl'] = boxbranding.getFeedsUrl() info['developername'] = boxbranding.getDeveloperName() info['builddatestring'] = about.getBuildDateString() info['imagefpu'] = boxbranding.getImageFPU() info['havemultilib'] = boxbranding.getHaveMultiLib() try: info['fp_version'] = getFPVersion() except: # noqa: E722 info['fp_version'] = None info['tuners'] = [] for i in list(range(0, nimmanager.getSlotCount())): print( "[OpenWebif] -D- tuner '%d' '%s' '%s'" % (i, nimmanager.getNimName(i), nimmanager.getNim(i).getSlotName())) info['tuners'].append({ "name": nimmanager.getNim(i).getSlotName(), "type": nimmanager.getNimName(i) + " (" + nimmanager.getNim(i).getFriendlyType() + ")", "rec": "", "live": "" }) info['ifaces'] = [] ifaces = iNetwork.getConfiguredAdapters() for iface in ifaces: info['ifaces'].append({ "name": iNetwork.getAdapterName(iface), "friendlynic": getFriendlyNICChipSet(iface), "linkspeed": getLinkSpeed(iface), "mac": iNetwork.getAdapterAttribute(iface, "mac"), "dhcp": iNetwork.getAdapterAttribute(iface, "dhcp"), "ipv4method": getIPv4Method(iface), "ip": formatIp(iNetwork.getAdapterAttribute(iface, "ip")), "mask": formatIp(iNetwork.getAdapterAttribute(iface, "netmask")), "v4prefix": sum([ bin(int(x)).count('1') for x in formatIp( iNetwork.getAdapterAttribute(iface, "netmask")).split('.') ]), "gw": formatIp(iNetwork.getAdapterAttribute(iface, "gateway")), "ipv6": getAdapterIPv6(iface)['addr'], "ipmethod": getIPMethod(iface), "firstpublic": getAdapterIPv6(iface)['firstpublic'] }) info['hdd'] = [] for hdd in harddiskmanager.hdd: dev = hdd.findMount() if dev: stat = os.statvfs(dev) free = stat.f_bavail * stat.f_frsize / 1048576. else: free = -1 if free <= 1024: free = "%i %s" % (free, _("MB")) else: free = free / 1024. free = "%.1f %s" % (free, _("GB")) size = hdd.diskSize() * 1000000 / 1048576. if size > 1048576: size = "%.1f %s" % ((size / 1048576.), _("TB")) elif size > 1024: size = "%.1f %s" % ((size / 1024.), _("GB")) else: size = "%d %s" % (size, _("MB")) iecsize = hdd.diskSize() # Harddisks > 1000 decimal Gigabytes are labelled in TB if iecsize > 1000000: iecsize = (iecsize + 50000) // float(100000) / 10 # Omit decimal fraction if it is 0 if (iecsize % 1 > 0): iecsize = "%.1f %s" % (iecsize, _("TB")) else: iecsize = "%d %s" % (iecsize, _("TB")) # Round harddisk sizes beyond ~300GB to full tens: 320, 500, 640, 750GB elif iecsize > 300000: iecsize = "%d %s" % (((iecsize + 5000) // 10000 * 10), _("GB")) # ... be more precise for media < ~300GB (Sticks, SSDs, CF, MMC, ...): 1, 2, 4, 8, 16 ... 256GB elif iecsize > 1000: iecsize = "%d %s" % (((iecsize + 500) // 1000), _("GB")) else: iecsize = "%d %s" % (iecsize, _("MB")) info['hdd'].append({ "model": hdd.model(), "capacity": size, "labelled_capacity": iecsize, "free": free, "mount": dev, "friendlycapacity": _("%s free / %s total") % (free, size + ' ("' + iecsize + '")') }) info['shares'] = [] autofiles = ('/etc/auto.network', '/etc/auto.network_vti') for autofs in autofiles: if fileExists(autofs): method = "autofs" for line in open(autofs).readlines(): if not line.startswith('#'): # Replace escaped spaces that can appear inside credentials with underscores # Not elegant but we wouldn't want to expose credentials on the OWIF anyways tmpline = line.replace("\ ", "_") tmp = tmpline.split() if not len(tmp) == 3: continue name = tmp[0].strip() type = "unknown" if "cifs" in tmp[1]: # Linux still defaults to SMBv1 type = "SMBv1.0" settings = tmp[1].split(",") for setting in settings: if setting.startswith("vers="): type = setting.replace("vers=", "SMBv") elif "nfs" in tmp[1]: type = "NFS" # Default is r/w mode = _("r/w") settings = tmp[1].split(",") for setting in settings: if setting == "ro": mode = _("r/o") uri = tmp[2] parts = [] parts = tmp[2].split(':') if parts[0] == "": server = uri.split('/')[2] uri = uri.strip()[1:] else: server = parts[0] ipaddress = None if server: # Will fail on literal IPs try: # Try IPv6 first, as will Linux if has_ipv6: tmpaddress = None tmpaddress = getaddrinfo(server, 0, AF_INET6) if tmpaddress: ipaddress = "[" + list( tmpaddress)[0][4][0] + "]" # Use IPv4 if IPv6 fails or is not present if ipaddress is None: tmpaddress = None tmpaddress = getaddrinfo(server, 0, AF_INET) if tmpaddress: ipaddress = list(tmpaddress)[0][4][0] except: # noqa: E722 pass friendlyaddress = server if ipaddress is not None and not ipaddress == server: friendlyaddress = server + " (" + ipaddress + ")" info['shares'].append({ "name": name, "method": method, "type": type, "mode": mode, "path": uri, "host": server, "ipaddress": ipaddress, "friendlyaddress": friendlyaddress }) # TODO: fstab info['EX'] = '' if session: try: # gets all current stream clients for images using eStreamServer # TODO: merge eStreamServer and streamList # TODO: get tuner info for streams # TODO: get recoding/timer info if more than one info['streams'] = [] try: from enigma import eStreamServer streamServer = eStreamServer.getInstance() if streamServer is not None: for x in streamServer.getConnectedClients(): servicename = ServiceReference( x[1]).getServiceName() or "(unknown service)" if int(x[2]) == 0: strtype = "S" else: strtype = "T" info['streams'].append({ "ref": x[1], "name": servicename, "ip": x[0], "type": strtype }) except Exception as error: print("[OpenWebif] -D- no eStreamServer %s" % error) recs = NavigationInstance.instance.getRecordings() if recs: # only one stream and only TV from Plugins.Extensions.OpenWebif.controllers.stream import streamList s_name = '' # s_cip = '' print("[OpenWebif] -D- streamList count '%d'" % len(streamList)) if len(streamList) == 1: from Screens.ChannelSelection import service_types_tv # from enigma import eEPGCache # epgcache = eEPGCache.getInstance() serviceHandler = eServiceCenter.getInstance() services = serviceHandler.list( eServiceReference('%s ORDER BY name' % (service_types_tv))) channels = services and services.getContent("SN", True) s = streamList[0] srefs = s.ref.toString() for channel in channels: if srefs == channel[0]: s_name = channel[1] + ' (' + s.clientIP + ')' break print("[OpenWebif] -D- s_name '%s'" % s_name) # only for debug for stream in streamList: srefs = stream.ref.toString() print("[OpenWebif] -D- srefs '%s'" % srefs) sname = '' timers = [] for timer in NavigationInstance.instance.RecordTimer.timer_list: if timer.isRunning() and not timer.justplay: timers.append( removeBad(timer.service_ref.getServiceName())) print("[OpenWebif] -D- timer '%s'" % timer.service_ref.getServiceName()) # TODO: more than one recording if len(timers) == 1: sname = timers[0] if sname == '' and s_name != '': sname = s_name print("[OpenWebif] -D- recs count '%d'" % len(recs)) for rec in recs: feinfo = rec.frontendInfo() frontendData = feinfo and feinfo.getAll(True) if frontendData is not None: cur_info = feinfo.getTransponderData(True) if cur_info: nr = frontendData['tuner_number'] info['tuners'][nr]['rec'] = getOrbitalText( cur_info) + ' / ' + sname service = session.nav.getCurrentService() if service is not None: sname = service.info().getName() feinfo = service.frontendInfo() frontendData = feinfo and feinfo.getAll(True) if frontendData is not None: cur_info = feinfo.getTransponderData(True) if cur_info: nr = frontendData['tuner_number'] info['tuners'][nr]['live'] = getOrbitalText( cur_info) + ' / ' + sname except Exception as error: info['EX'] = error info['timerpipzap'] = False info['timerautoadjust'] = False try: timer = RecordTimerEntry('', 0, 0, '', '', 0) if hasattr(timer, "pipzap"): info['timerpipzap'] = True if hasattr(timer, "autoadjust"): info['timerautoadjust'] = True except Exception as error: print("[OpenWebif] -D- RecordTimerEntry check %s" % error) STATICBOXINFO = info return info
def processTimers(self, timers): update_queue = [] for iceTimer in timers: # print "[IceTV] iceTimer:", iceTimer try: action = iceTimer.get("action", "").encode("utf8") state = iceTimer.get("state", "").encode("utf8") name = iceTimer.get("name", "").encode("utf8") start = int( timegm( strptime(iceTimer["start_time"].split("+")[0], "%Y-%m-%dT%H:%M:%S"))) duration = 60 * int(iceTimer["duration_minutes"]) channel_id = long(iceTimer["channel_id"]) ice_timer_id = iceTimer["id"].encode("utf8") if action == "forget": for timer in _session.nav.RecordTimer.timer_list: if timer.ice_timer_id == ice_timer_id: # print "[IceTV] removing timer:", timer _session.nav.RecordTimer.removeEntry(timer) break else: self.deleteTimer(ice_timer_id) elif state == "completed": continue # Completely ignore completed timers - the server should not be sending those back to us anyway. elif channel_id in self.channel_service_map: completed = False for timer in _session.nav.RecordTimer.processed_timers: if timer.ice_timer_id == ice_timer_id: # print "[IceTV] completed timer:", timer iceTimer["state"] = "completed" iceTimer["message"] = "Done" update_queue.append(iceTimer) completed = True updated = False if not completed: for timer in _session.nav.RecordTimer.timer_list: if timer.ice_timer_id == ice_timer_id: # print "[IceTV] updating timer:", timer if self.updateTimer( timer, name, start - config.recording.margin_before.value * 60, start + duration + config.recording.margin_after.value * 60, self.channel_service_map[channel_id]): if not self.modifyTimer(timer): iceTimer["state"] = "failed" iceTimer[ "message"] = "Failed to update the timer" update_queue.append(iceTimer) else: self.onTimerChanged(timer) updated = True created = False if not completed and not updated: channels = self.channel_service_map[channel_id] # print "[IceTV] channel_id %s maps to" % channel_id, channels db = eDVBDB.getInstance() for channel in channels: serviceref = ServiceReference( "1:0:1:%x:%x:%x:EEEE0000:0:0:0:" % (channel[2], channel[1], channel[0])) if db.isValidService(channel[1], channel[0], channel[2]): # print "[IceTV] %s is valid" % str(serviceref), serviceref.getServiceName() recording = RecordTimerEntry( serviceref, start - config.recording.margin_before.value * 60, start + duration + config.recording.margin_after.value * 60, name, "", None, ice_timer_id=ice_timer_id) conflicts = _session.nav.RecordTimer.record( recording) if conflicts is None: iceTimer["state"] = "pending" iceTimer["message"] = "Added" created = True break else: names = [r.name for r in conflicts] iceTimer["state"] = "failed" iceTimer[ "message"] = "Timer conflict: " + ", ".join( names) update_queue.append(iceTimer) # print "[IceTV] Timer conflict:", conflicts self.addLog("Timer %s conflicts with %s" % (name, ", ".join(names))) else: iceTimer["state"] = "failed" iceTimer["message"] = "No matching service" update_queue.append(iceTimer) if not completed and not updated and not created: iceTimer["state"] = "failed" update_queue.append(iceTimer) else: iceTimer["state"] = "failed" iceTimer[ "message"] = "No valid service mapping for channel_id %d" % channel_id update_queue.append(iceTimer) except (IOError, RuntimeError, KeyError) as ex: print "[IceTV] Can not process iceTimer:", ex # Send back updated timer states res = True try: self.putTimers(update_queue) self.addLog("Timers updated OK") except KeyError as ex: print "[IceTV] ", str(ex) res = False except (IOError, RuntimeError) as ex: msg = "Can not update timers: " + str(ex) if hasattr(ex, "response") and hasattr(ex.response, "text"): msg += "\n%s" % str(ex.response.text).strip() self.addLog(msg) res = False return res
def parseEPG(self, simulateOnly = False): if NavigationInstance.instance is None: print "[AutoTimer] Navigation is not available, can't parse EPG" return (0, 0, 0, [], []) total = 0 new = 0 modified = 0 timers = [] conflicting = [] self.readXml() # Save Recordings in a dict to speed things up a little # We include processed timers as we might search for duplicate descriptions recorddict = {} for timer in NavigationInstance.instance.RecordTimer.timer_list + NavigationInstance.instance.RecordTimer.processed_timers: recorddict.setdefault(str(timer.service_ref), []).append(timer) # Iterate Timer for timer in self.getEnabledTimerList(): # Workaround to allow search for umlauts if we know the encoding match = timer.match if timer.encoding != 'UTF-8': try: match = match.decode('UTF-8').encode(timer.encoding) except UnicodeDecodeError: pass # Search EPG, default to empty list epgcache = eEPGCache.getInstance() ret = epgcache.search(('RI', 500, typeMap[timer.searchType], match, caseMap[timer.searchCase])) or () for serviceref, eit in ret: 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() # Gather Information name = evt.getEventName() description = evt.getShortDescription() begin = evt.getBeginTime() duration = evt.getDuration() end = begin + duration # If event starts in less than 60 seconds skip it if begin < time() + 60: continue # Convert begin time timestamp = localtime(begin) # Update timer timer.update(begin, timestamp) # Check Duration, Timespan and Excludes if timer.checkServices(serviceref) \ or timer.checkDuration(duration) \ or timer.checkTimespan(timestamp) \ or timer.checkFilter(name, description, evt.getExtendedDescription(), str(timestamp.tm_wday)): 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 # Eventually change service to alternative if timer.overrideAlternatives: serviceref = timer.getAlternative(serviceref) total += 1 # Append to timerlist and abort if simulating timers.append((name, begin, end, serviceref, timer.name)) if simulateOnly: continue # Initialize newEntry = None oldExists = False # 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 recorddict.get(serviceref, ()): if rtimer.eit == eit or config.plugins.autotimer.try_guessing.value and getTimeDiff(rtimer, begin, end) > ((duration/10)*8): oldExists = True # Abort if we don't want to modify timers or timer is repeated if config.plugins.autotimer.refresh.value == "none" or rtimer.repeated: print "[AutoTimer] Won't modify existing timer because either no modification allowed or repeated timer" break if hasattr(rtimer, "isAutoTimer"): print "[AutoTimer] Modifying existing AutoTimer!" else: if config.plugins.autotimer.refresh.value != "all": print "[AutoTimer] Won't modify existing timer because it's no timer set by us" break print "[AutoTimer] Warning, we're messing with a timer which might not have been set by us" newEntry = rtimer modified += 1 # Modify values saved in timer newEntry.name = name newEntry.description = description newEntry.begin = int(begin) newEntry.end = int(end) newEntry.service_ref = ServiceReference(serviceref) break elif timer.avoidDuplicateDescription == 1 and not rtimer.disabled and rtimer.name == name and rtimer.description == description: oldExists = True print "[AutoTimer] We found a timer 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: # I thinks thats the fastest way to do this, though it's a little ugly try: for list in recorddict.values(): for rtimer in list: if not rtimer.disabled and rtimer.name == name and rtimer.description == description: raise AutoTimerIgnoreTimerException("We found a timer with same description, skipping event") except AutoTimerIgnoreTimerException, etite: print etite continue if timer.checkCounter(timestamp): continue print "[AutoTimer] Adding an event." newEntry = RecordTimerEntry(ServiceReference(serviceref), begin, end, name, description, eit) # Mark this entry as AutoTimer (only AutoTimers will have this Attribute set) 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 = timer.destination newEntry.justplay = timer.justplay newEntry.tags = timer.tags if oldExists: # XXX: this won't perform a sanity check, but do we actually want to do so? NavigationInstance.instance.RecordTimer.timeChanged(newEntry) else: conflicts = NavigationInstance.instance.RecordTimer.record(newEntry) if conflicts and config.plugins.autotimer.disabled_on_conflict.value: 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 = NavigationInstance.instance.RecordTimer.record(newEntry) conflicting.append((name, begin, end, serviceref, timer.name)) if conflicts is None: timer.decrementCounter() new += 1 recorddict.setdefault(serviceref, []).append(newEntry) else: conflicting.append((name, begin, end, serviceref, timer.name))
timestruct.tm_hour, timestruct.tm_min, 0, datestruct.tm_wday, datestruct.tm_yday, -1)) del datestruct, timestruct except ValueError, e: payload = "%d argument error" % (CODE_SYNTAX, ) return self.sendLine(payload) except KeyError, e: payload = "%d argument error" % (CODE_SYNTAX, ) return self.sendLine(payload) if end < begin: end += 86400 # Add 1 day, beware - this is evil and might not work correctly due to dst timer = RecordTimerEntry(service_ref, begin, end, name, description, 0, disabled=flags & 1 == 0) if oldTimer: recordTimer.removeEntry(oldTimer) timer.justplay = oldTimer.justplay timer.afterEvent = oldTimer.afterEvent timer.dirname = oldTimer.dirname timer.tags = oldTimer.tags timer.log_entries = oldTimer.log_entries conflict = recordTimer.record(timer) if conflict is None: return self.sendTimerLine(timer, timerId, last=True) else:
def check_and_add_event(self, neweventid): if not config.plugins.vps.allow_seeking_multiple_pdc.value: return epgcache = eEPGCache.getInstance() evt = epgcache.lookupEventId(self.rec_ref, neweventid) if evt: evt_begin = evt.getBeginTime() + 60 evt_end = evt.getBeginTime() + evt.getDuration() - 60 if evt_begin < self.timer.begin: return for checktimer in self.session.nav.RecordTimer.timer_list: if checktimer == self.timer: continue if (checktimer.begin - evt_begin) > 3600 * 2: break compareString = checktimer.service_ref.ref.toCompareString() if compareString == self.timer.service_ref.ref.toCompareString( ) or compareString == self.rec_ref.toCompareString(): if checktimer.eit == neweventid: return if checktimer.begin <= evt_begin and checktimer.end >= evt_end: if checktimer.vpsplugin_enabled is None or not checktimer.vpsplugin_enabled: return # manuell angelegter Timer mit VPS if checktimer.name == "" and checktimer.vpsplugin_time is not None: checktimer.eit = neweventid checktimer.name = evt.getEventName() checktimer.description = evt.getShortDescription() checktimer.vpsplugin_time = None checktimer.log( 0, "[VPS] changed timer (found same PDC-Time as in other VPS-recording)" ) return # eigenen Timer überprüfen, wenn Zeiten nicht überschrieben werden dürfen if not self.timer.vpsplugin_overwrite and evt_begin <= self.timer.end: check_already_existing = [ x for (x, y) in self.next_events if y == neweventid ] if len(check_already_existing) > 0: start = check_already_existing.pop() if start == evt_begin: return else: self.next_events.remove((start, neweventid)) self.timer.log( 0, "[VPS] delete event_id " + str(neweventid) + " because of delay " + str(evt_begin - start)) self.next_events.append((evt_begin, neweventid)) self.next_events = sorted(self.next_events) self.timer.log(0, "[VPS] add event_id " + str(neweventid)) else: newevent_data = parseEvent(evt) newEntry = RecordTimerEntry(ServiceReference(self.rec_ref), *newevent_data) newEntry.vpsplugin_enabled = True newEntry.vpsplugin_overwrite = True newEntry.dirname = self.timer.dirname newEntry.log( 0, "[VPS] added this timer (found same PDC-Time as in other VPS-recording)" ) # Wenn kein Timer-Konflikt auftritt, wird der Timer angelegt. res = NavigationInstance.instance.RecordTimer.record(newEntry) self.timer.log(0, "[VPS] added another timer, res " + str(res))
def editTimer(self, param): print "[WebComponents.Timer] editTimer" #OK first we need to parse all of your Parameters #For some of them (like afterEvent or justplay) we can use default values #for others (the serviceReference or the Begin/End time of the timer #we have to quit if they are not set/have illegal values if 'sRef' not in param: return ( False, _("Missing Parameter: sRef") ) service_ref = ServiceReference(param['sRef']) repeated = int(param.get('repeated') or 0) if 'begin' not in param: return ( False, _("Missing Parameter: begin") ) begin = int(float(param['begin'])) if 'end' not in param: return ( False, _("Missing Parameter: end") ) end = int(float(param['end'])) tm = int( time() ) if tm <= begin: pass elif tm > begin and tm < end and repeated == 0: pass elif repeated == 0: return ( False, _("Illegal Parameter value for Parameter begin : '%s'") % begin ) if 'applyMargin' in param: if param['applyMargin'] == "1": begin -= config.recording.margin_before.value * 60 end += config.recording.margin_after.value * 60 if 'name' not in param: return ( False, _("Missing Parameter: name") ) name = param['name'] if 'description' not in param: return ( False, _("Missing Parameter: description") ) description = param['description'].replace("\n", " ") eit = param.get("eit", None) if eit is None or eit.strip() == "": eit = 0 else: try: eit = int(eit) except ValueError: return ( False, _("Illegal Parameter value for Parameter eit : '%s'") % eit ) print "[WebComponents.Sources.Timer]: eit=%d" %eit if eit != 0: #check if the given event exists, if it doesn't the timer may be already running epgcache = eEPGCache.getInstance() event = epgcache.lookupEventId(eServiceReference(param['sRef']), eit) if event: eit = event.getEventId() #TODO add some subservice handling else: print "[WebComponents.Sources.Timer] event not found, will be ignored" disabled = False #Default to: Enabled if 'disabled' in param: if param['disabled'] == "1": disabled = True else: #TODO - maybe we can give the user some useful hint here pass justplay = False #Default to: Record if 'justplay' in param: if param['justplay'] == "1": justplay = True if not justplay and not config.misc.recording_allowed.value: return (False, _("Recording is currently disabled!")) afterEvent = 3 #Default to Afterevent: Auto if 'afterevent' in param: if (param['afterevent'] == "0") or (param['afterevent'] == "1") or (param['afterevent'] == "2"): afterEvent = int(param['afterevent']) dirname = preferredTimerPath() if 'dirname' in param and param['dirname']: dirname = param['dirname'] tags = [] if 'tags' in param and param['tags']: tags = unescape(param['tags']).split(' ') delold = 0 if 'deleteOldOnSave' in param: delold = int(param['deleteOldOnSave']) #Try to edit an existing Timer if delold: if 'channelOld' in param and param['channelOld']: channelOld = ServiceReference(param['channelOld']) else: return ( False, _("Missing Parameter: channelOld") ) # We do need all of the following Parameters, too, for being able of finding the Timer. # Therefore so we can neither use default values in this part nor can we # continue if a parameter is missing if 'beginOld' not in param: return ( False, _("Missing Parameter: beginOld") ) beginOld = int(param['beginOld']) if 'endOld' not in param: return ( False, _("Missing Parameter: endOld") ) endOld = int(param['endOld']) #let's try to find the timer try: for timer in self.recordtimer.timer_list + self.recordtimer.processed_timers: if str(timer.service_ref) == str(channelOld): if int(timer.begin) == beginOld: if int(timer.end) == endOld: #we've found the timer we've been searching for #set the new data timer.service_ref = service_ref timer.begin = begin timer.end = end timer.name = name timer.description = description timer.eit = eit timer.disabled = disabled timer.justplay = justplay timer.afterEvent = afterEvent timer.dirname = dirname timer.tags = tags timer.repeated = repeated timer.processRepeated() #sanity check timersanitycheck = TimerSanityCheck(self.session.nav.RecordTimer.timer_list, timer) conflicts = None if not timersanitycheck.check(): conflicts = timersanitycheck.getSimulTimerList() if conflicts is not None: for x in conflicts: if x.setAutoincreaseEnd(entry): self.session.nav.RecordTimer.timeChanged(x) if not timersanitycheck.check(): conflicts = timersanitycheck.getSimulTimerList() if conflicts is None: self.recordtimer.timeChanged(timer) #go and save it print "[WebComponents.Timer] editTimer: Timer changed!" return ( True, "Timer '%s' changed" %(timer.name) ) else: print "[WebComponents.Timer] editTimer conflicting Timers: %s" %(conflicts) msg = "" for t in conflicts: msg = "%s / %s" %(msg, t.name) return (False, _("Conflicting Timer(s) detected! %s") %(msg)) except Exception as e: #obviously some value was not good, return an error import traceback print traceback.format_exc() return ( False, _("Changing the timer for '%s' failed!") % name ) return ( False, _("Could not find timer '%s' with given start and end time!") % name ) #Try adding a new Timer try: #Create a new instance of recordtimerentry timer = RecordTimerEntry(service_ref, begin, end, name, description, eit, disabled, justplay, afterEvent, dirname=dirname, tags=tags) timer.repeated = repeated #add the new timer conflicts = self.recordtimer.record(timer) if conflicts is None: return ( True, _("Timer '%s' added") %(timer.name) ) else: print "[WebComponents.Timer] editTimer conflicting Timers: %s" %(conflicts) msg = "" for timer in conflicts: msg = "%s / %s" %(msg, timer.name) return (False, _("Conflicting Timer(s) detected! %s") %(msg)) except Exception, e: #something went wrong, most possibly one of the given paramater-values was wrong print "[WebComponents.Timer] editTimer exception: %s" %(e) return ( False, _("Could not add timer '%s'!") % name )
def activate(self): next_state = self.state + 1 self.log(5, "activating state %d" % next_state) if next_state == self.StatePrepared: if self.tryPrepare(): self.log(6, "prepare ok, waiting for begin") # create file to "reserve" the filename # because another recording at the same time on another service can try to record the same event # i.e. cable / sat.. then the second recording needs an own extension... when we create the file # here than calculateFilename is happy if not self.justplay: open(self.Filename + ".ts", "w").close() # fine. it worked, resources are allocated. self.next_activation = self.begin self.backoff = 0 return True self.log(7, "prepare failed") if self.first_try_prepare: self.first_try_prepare = False cur_ref = NavigationInstance.instance.getCurrentlyPlayingServiceReference( ) if cur_ref and not cur_ref.getPath(): if not config.recording.asktozap.value: self.log(8, "asking user to zap away") if config.plugins.merlinEpgCenter.showTimerMessages.value: self.session.openWithCallback( self.failureCB, MessageBox, _("A timer failed to record!\nDisable TV and try again?\n" ), timeout=20) else: # zap without asking self.log(9, "zap without asking") if config.plugins.merlinEpgCenter.showTimerMessages.value: self.session.open( MessageBox, _("In order to record a timer, the TV was switched to the recording service!\n" ), type=MessageBox.TYPE_INFO, timeout=config.merlin2. timeout_message_channel_switch.value) self.failureCB(True) elif cur_ref: self.log( 8, "currently running service is not a live service.. so stop it makes no sense" ) else: self.log( 8, "currently no service running... so we dont need to stop it" ) return False elif next_state == self.StateRunning: # if this timer has been cancelled, just go to "end" state. if self.cancelled: return True if self.justplay: if Screens.Standby.inStandby: self.log(11, "wakeup and zap") #set service to zap after standby Screens.Standby.inStandby.prev_running_service = self.service_ref.ref #wakeup standby Screens.Standby.inStandby.Power() else: self.log(11, "zapping") NavigationInstance.instance.playService( self.service_ref.ref) return True else: self.log(11, "start recording") record_res = self.record_service.start() if record_res: self.log(13, "start record returned %d" % record_res) self.do_backoff() # retry self.begin = time() + self.backoff return False return True elif next_state == self.StateEnded: old_end = self.end if self.setAutoincreaseEnd(): self.log( 12, "autoincrase recording %d minute(s)" % int( (self.end - old_end) / 60)) self.state -= 1 return True self.log(12, "stop recording") if not self.justplay: NavigationInstance.instance.stopRecordService( self.record_service) self.record_service = None if self.afterEvent == AFTEREVENT.STANDBY: if not Screens.Standby.inStandby and config.plugins.merlinEpgCenter.showTimerMessages.value: # not already in standby self.session.openWithCallback( self.sendStandbyNotification, MessageBox, _("A finished record timer wants to set your\nDreambox to standby. Do that now?" ), timeout=20) elif self.afterEvent == AFTEREVENT.DEEPSTANDBY: if not Screens.Standby.inTryQuitMainloop: # not a shutdown messagebox is open if Screens.Standby.inStandby: # in standby RecordTimerEntry.TryQuitMainloop( ) # start shutdown handling without screen elif config.plugins.merlinEpgCenter.showTimerMessages.value: self.session.openWithCallback( self.sendTryQuitMainloopNotification, MessageBox, _("A finished record timer wants to shut down\nyour Dreambox. Shutdown now?" ), timeout=20) return True
def finish(self, *args, **kwargs): if self.showPendingServicesMessageShown: self.msg.close() print("[EPGRefresh] Debug: Refresh finished!") if config.plugins.epgrefresh.enablemessage.value: Notifications.AddPopup(_("EPG refresh finished."), MessageBox.TYPE_INFO, 4, ENDNOTIFICATIONID, domain=NOTIFICATIONDOMAIN) epgrefreshtimer.cleanup() self.maybeStopAdapter() if config.plugins.epgrefresh.epgsave.value: Notifications.AddPopup(_("EPG refresh save."), MessageBox.TYPE_INFO, 4, ENDNOTIFICATIONID, domain=NOTIFICATIONDOMAIN) from enigma import eEPGCache myEpg = None myEpg = eEPGCache.getInstance() myEpg.save() force_auto_shutdown = self.session.nav.wasTimerWakeup() and \ config.plugins.epgrefresh.afterevent.value == "auto" and \ Screens.Standby.inStandby and config.misc.standbyCounter.value == 1 and \ config.misc.prev_wakeup_time.value == config.plugins.epgrefresh.wakeup_time.value if not self.forcedScan: # shutdown if config.plugins.epgrefresh.afterevent.value == "standby" or force_auto_shutdown: if not Screens.Standby.inTryQuitMainloop: if (not config.plugins.epgrefresh.dontshutdownonabort.value and self.doStopRunningRefresh ) or not self.doStopRunningRefresh: self.forcedScan = False if Screens.Standby.inStandby: RecordTimerEntry.TryQuitMainloop() else: Notifications.AddNotificationWithCallback( self.sendTryQuitMainloopNotification, MessageBox, _("EPGRefresh wants to shut down\nyour Dreambox. Shutdown now?" ), timeout=10, domain=NOTIFICATIONDOMAIN) # idle elif config.plugins.epgrefresh.afterevent.value == "idle": if not Screens.Standby.inStandby: Notifications.AddNotificationWithCallback( self.sendStandbyNotification, MessageBox, _("EPGRefresh wants to set your\nDreambox to idle. Do that now?" ), timeout=10, domain=NOTIFICATIONDOMAIN) self.doStopRunningRefresh = False self.forcedScan = False self.isrunning = False self._nextTodo()
def addTimer(session, serviceref, begin, end, name, description, disabled, justplay, afterevent, dirname, tags, repeated, vpsinfo=None, logentries=None, eit=0, always_zap=-1): rt = session.nav.RecordTimer print "mao1", dirname if not dirname: dirname = preferredTimerPath() print "mao2", dirname try: timer = RecordTimerEntry(ServiceReference(serviceref), begin, end, name, description, eit, disabled, justplay, afterevent, dirname=dirname, tags=tags) timer.repeated = repeated if logentries: timer.log_entries = logentries conflicts = rt.record(timer) if conflicts: errors = [] for conflict in conflicts: errors.append(conflict.name) return { "result": False, "message": _("Conflicting Timer(s) detected! %s") % " / ".join(errors) } #VPS if vpsinfo is not None: timer.vpsplugin_enabled = vpsinfo["vpsplugin_enabled"] timer.vpsplugin_overwrite = vpsinfo["vpsplugin_overwrite"] timer.vpsplugin_time = vpsinfo["vpsplugin_time"] if always_zap <> -1: if hasattr(timer, "always_zap"): timer.always_zap = always_zap == 1 except Exception, e: print e return { "result": False, "message": _("Could not add timer '%s'!") % name }
def parseEPG(self, simulateOnly=False): if NavigationInstance.instance is None: print "[AutoTimer] Navigation is not available, can't parse EPG" return (0, 0, 0, [], []) total = 0 new = 0 modified = 0 timers = [] conflicting = [] self.readXml() # Save Recordings in a dict to speed things up a little # We include processed timers as we might search for duplicate descriptions recorddict = {} for timer in NavigationInstance.instance.RecordTimer.timer_list + NavigationInstance.instance.RecordTimer.processed_timers: recorddict.setdefault(str(timer.service_ref), []).append(timer) # Iterate Timer for timer in self.getEnabledTimerList(): # Workaround to allow search for umlauts if we know the encoding match = timer.match if timer.encoding != 'UTF-8': try: match = match.decode('UTF-8').encode(timer.encoding) except UnicodeDecodeError: pass # Search EPG, default to empty list epgcache = eEPGCache.getInstance() ret = epgcache.search(('RI', 500, typeMap[timer.searchType], match, caseMap[timer.searchCase])) or () for serviceref, eit in ret: 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() # Gather Information name = evt.getEventName() description = evt.getShortDescription() begin = evt.getBeginTime() duration = evt.getDuration() end = begin + duration # If event starts in less than 60 seconds skip it if begin < time() + 60: continue # Convert begin time timestamp = localtime(begin) # Update timer timer.update(begin, timestamp) # Check Duration, Timespan and Excludes if timer.checkServices(serviceref) \ or timer.checkDuration(duration) \ or timer.checkTimespan(timestamp) \ or timer.checkFilter(name, description, evt.getExtendedDescription(), str(timestamp.tm_wday)): 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 # Eventually change service to alternative if timer.overrideAlternatives: serviceref = timer.getAlternative(serviceref) total += 1 # Append to timerlist and abort if simulating timers.append((name, begin, end, serviceref, timer.name)) if simulateOnly: continue # Initialize newEntry = None oldExists = False # 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 recorddict.get(serviceref, ()): if rtimer.eit == eit or config.plugins.autotimer.try_guessing.value and getTimeDiff( rtimer, begin, end) > ((duration / 10) * 8): oldExists = True # Abort if we don't want to modify timers or timer is repeated if config.plugins.autotimer.refresh.value == "none" or rtimer.repeated: print "[AutoTimer] Won't modify existing timer because either no modification allowed or repeated timer" break if hasattr(rtimer, "isAutoTimer"): print "[AutoTimer] Modifying existing AutoTimer!" else: if config.plugins.autotimer.refresh.value != "all": print "[AutoTimer] Won't modify existing timer because it's no timer set by us" break print "[AutoTimer] Warning, we're messing with a timer which might not have been set by us" newEntry = rtimer modified += 1 # Modify values saved in timer newEntry.name = name newEntry.description = description newEntry.begin = int(begin) newEntry.end = int(end) newEntry.service_ref = ServiceReference(serviceref) break elif timer.avoidDuplicateDescription == 1 and not rtimer.disabled and rtimer.name == name and rtimer.description == description: oldExists = True print "[AutoTimer] We found a timer 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: # I thinks thats the fastest way to do this, though it's a little ugly try: for list in recorddict.values(): for rtimer in list: if not rtimer.disabled and rtimer.name == name and rtimer.description == description: raise AutoTimerIgnoreTimerException( "We found a timer with same description, skipping event" ) except AutoTimerIgnoreTimerException, etite: print etite continue if timer.checkCounter(timestamp): continue print "[AutoTimer] Adding an event." newEntry = RecordTimerEntry(ServiceReference(serviceref), begin, end, name, description, eit) # Mark this entry as AutoTimer (only AutoTimers will have this Attribute set) 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 = timer.destination newEntry.justplay = timer.justplay newEntry.tags = timer.tags if oldExists: # XXX: this won't perform a sanity check, but do we actually want to do so? NavigationInstance.instance.RecordTimer.timeChanged( newEntry) else: conflicts = NavigationInstance.instance.RecordTimer.record( newEntry) if conflicts and config.plugins.autotimer.disabled_on_conflict.value: 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 = NavigationInstance.instance.RecordTimer.record( newEntry) conflicting.append( (name, begin, end, serviceref, timer.name)) if conflicts is None: timer.decrementCounter() new += 1 recorddict.setdefault(serviceref, []).append(newEntry) else: conflicting.append( (name, begin, end, serviceref, timer.name))
def getInfo(session=None, need_fullinfo=False): # TODO: get webif versione somewhere! info = {} global STATICBOXINFO if not (STATICBOXINFO is None or need_fullinfo): return STATICBOXINFO info['brand'] = getMachineBrand() info['model'] = getMachineName() info['boxtype'] = getBoxType() info['machinebuild'] = getMachineBuild() try: # temporary due OE-A info['lcd'] = getLcd() except: # noqa: E722 info['lcd'] = 0 try: # temporary due OE-A info['grabpip'] = getGrabPip() except: # noqa: E722 info['grabpip'] = 0 chipset = "unknown" if fileExists("/etc/.box"): f = open("/etc/.box", 'r') model = f.readline().strip().lower() f.close() if model.startswith("ufs") or model.startswith("ufc"): if model in ("ufs910", "ufs922", "ufc960"): chipset = "SH4 @266MHz" else: chipset = "SH4 @450MHz" elif model in ("topf", "tf7700hdpvr"): chipset = "SH4 @266MHz" elif model.startswith("azbox"): f = open("/proc/stb/info/model", 'r') model = f.readline().strip().lower() f.close() if model == "me": chipset = "SIGMA 8655" elif model == "minime": chipset = "SIGMA 8653" else: chipset = "SIGMA 8634" elif model.startswith("spark"): if model == "spark7162": chipset = "SH4 @540MHz" else: chipset = "SH4 @450MHz" elif fileExists("/proc/stb/info/azmodel"): f = open("/proc/stb/info/model", 'r') model = f.readline().strip().lower() f.close() if model == "me": chipset = "SIGMA 8655" elif model == "minime": chipset = "SIGMA 8653" else: chipset = "SIGMA 8634" elif fileExists("/proc/stb/info/model"): f = open("/proc/stb/info/model", 'r') model = f.readline().strip().lower() f.close() if model == "tf7700hdpvr": chipset = "SH4 @266MHz" elif model == "nbox": chipset = "STi7100 @266MHz" elif model == "arivalink200": chipset = "STi7109 @266MHz" elif model in ("adb2850", "adb2849", "dsi87"): chipset = "STi7111 @450MHz" elif model in ("sagemcom88", "esi88"): chipset = "STi7105 @450MHz" elif model.startswith("spark"): if model == "spark7162": chipset = "STi7162 @540MHz" else: chipset = "STi7111 @450MHz" elif model == "dm800": chipset = "bcm7401" elif model == "dm800se": chipset = "bcm7405" elif model == "dm500hd": chipset = "bcm7405" elif model == "dm7020hd": chipset = "bcm7405" elif model == "dm8000": chipset = "bcm7400" elif model == "dm820": chipset = "bcm7435" elif model == "dm7080": chipset = "bcm7435" elif model == "dm520": chipset = "bcm73625" elif model == "dm525": chipset = "bcm73625" elif model == "dm900": chipset = "bcm7252S" elif model == "dm920": chipset = "bcm7252S" if fileExists("/proc/stb/info/chipset"): f = open("/proc/stb/info/chipset", 'r') chipset = f.readline().strip() f.close() info['chipset'] = chipset memFree = 0 for line in open("/proc/meminfo", 'r'): parts = line.split(':') key = parts[0].strip() if key == "MemTotal": info['mem1'] = parts[1].strip().replace("kB", _("kB")) elif key in ("MemFree", "Buffers", "Cached"): memFree += int(parts[1].strip().split(' ', 1)[0]) info['mem2'] = "%s %s" % (memFree, _("kB")) info['mem3'] = _("%s free / %s total") % (info['mem2'], info['mem1']) try: f = open("/proc/uptime", "r") uptime = int(float(f.readline().split(' ', 2)[0].strip())) f.close() uptimetext = '' if uptime > 86400: d = uptime / 86400 uptime = uptime % 86400 uptimetext += '%dd ' % d uptimetext += "%d:%.2d" % (uptime / 3600, (uptime % 3600) / 60) except: # noqa: E722 uptimetext = "?" info['uptime'] = uptimetext info["webifver"] = OPENWEBIFVER info['imagedistro'] = getImageDistro() info['friendlyimagedistro'] = getFriendlyImageDistro() info['oever'] = getOEVersion() info['imagever'] = getImageVersion() ib = getImageBuild() if ib: info['imagever'] = info['imagever'] + "." + ib info['enigmaver'] = getEnigmaVersionString() info['driverdate'] = getDriverDate() info['kernelver'] = about.getKernelVersionString() try: from Tools.StbHardware import getFPVersion except ImportError: from Tools.DreamboxHardware import getFPVersion try: info['fp_version'] = getFPVersion() except: # noqa: E722 info['fp_version'] = None friendlychipsetdescription = _("Chipset") friendlychipsettext = info['chipset'].replace("bcm", "Broadcom ") if friendlychipsettext in ("7335", "7356", "7362", "73625", "7424", "7425", "7429"): friendlychipsettext = "Broadcom " + friendlychipsettext if not (info['fp_version'] is None or info['fp_version'] == 0): friendlychipsetdescription = friendlychipsetdescription + " (" + _( "Frontprocessor Version") + ")" friendlychipsettext = friendlychipsettext + " (" + str( info['fp_version']) + ")" info['friendlychipsetdescription'] = friendlychipsetdescription info['friendlychipsettext'] = friendlychipsettext info['tuners'] = [] for i in list(range(0, nimmanager.getSlotCount())): print( "[OpenWebif] -D- tuner '%d' '%s' '%s'" % (i, nimmanager.getNimName(i), nimmanager.getNim(i).getSlotName())) info['tuners'].append({ "name": nimmanager.getNim(i).getSlotName(), "type": nimmanager.getNimName(i) + " (" + nimmanager.getNim(i).getFriendlyType() + ")", "rec": "", "live": "" }) info['ifaces'] = [] ifaces = iNetwork.getConfiguredAdapters() for iface in ifaces: info['ifaces'].append({ "name": iNetwork.getAdapterName(iface), "friendlynic": getFriendlyNICChipSet(iface), "linkspeed": getLinkSpeed(iface), "mac": iNetwork.getAdapterAttribute(iface, "mac"), "dhcp": iNetwork.getAdapterAttribute(iface, "dhcp"), "ipv4method": getIPv4Method(iface), "ip": formatIp(iNetwork.getAdapterAttribute(iface, "ip")), "mask": formatIp(iNetwork.getAdapterAttribute(iface, "netmask")), "v4prefix": sum([ bin(int(x)).count('1') for x in formatIp( iNetwork.getAdapterAttribute(iface, "netmask")).split('.') ]), "gw": formatIp(iNetwork.getAdapterAttribute(iface, "gateway")), "ipv6": getAdapterIPv6(iface)['addr'], "ipmethod": getIPMethod(iface), "firstpublic": getAdapterIPv6(iface)['firstpublic'] }) info['hdd'] = [] for hdd in harddiskmanager.hdd: dev = hdd.findMount() if dev: stat = os.statvfs(dev) free = stat.f_bavail * stat.f_frsize / 1048576. else: free = -1 if free <= 1024: free = "%i %s" % (free, _("MB")) else: free = free / 1024. free = "%.1f %s" % (free, _("GB")) size = hdd.diskSize() * 1000000 / 1048576. if size > 1048576: size = "%.1f %s" % ((size / 1048576.), _("TB")) elif size > 1024: size = "%.1f %s" % ((size / 1024.), _("GB")) else: size = "%d %s" % (size, _("MB")) iecsize = hdd.diskSize() # Harddisks > 1000 decimal Gigabytes are labelled in TB if iecsize > 1000000: iecsize = (iecsize + 50000) // float(100000) / 10 # Omit decimal fraction if it is 0 if (iecsize % 1 > 0): iecsize = "%.1f %s" % (iecsize, _("TB")) else: iecsize = "%d %s" % (iecsize, _("TB")) # Round harddisk sizes beyond ~300GB to full tens: 320, 500, 640, 750GB elif iecsize > 300000: iecsize = "%d %s" % (((iecsize + 5000) // 10000 * 10), _("GB")) # ... be more precise for media < ~300GB (Sticks, SSDs, CF, MMC, ...): 1, 2, 4, 8, 16 ... 256GB elif iecsize > 1000: iecsize = "%d %s" % (((iecsize + 500) // 1000), _("GB")) else: iecsize = "%d %s" % (iecsize, _("MB")) info['hdd'].append({ "model": hdd.model(), "capacity": size, "labelled_capacity": iecsize, "free": free, "mount": dev, "friendlycapacity": _("%s free / %s total") % (free, size + ' ("' + iecsize + '")') }) info['shares'] = [] autofiles = ('/etc/auto.network', '/etc/auto.network_vti') for autofs in autofiles: if fileExists(autofs): method = "autofs" for line in open(autofs).readlines(): if not line.startswith('#'): # Replace escaped spaces that can appear inside credentials with underscores # Not elegant but we wouldn't want to expose credentials on the OWIF anyways tmpline = line.replace("\ ", "_") tmp = tmpline.split() if not len(tmp) == 3: continue name = tmp[0].strip() type = "unknown" if "cifs" in tmp[1]: # Linux still defaults to SMBv1 type = "SMBv1.0" settings = tmp[1].split(",") for setting in settings: if setting.startswith("vers="): type = setting.replace("vers=", "SMBv") elif "nfs" in tmp[1]: type = "NFS" # Default is r/w mode = _("r/w") settings = tmp[1].split(",") for setting in settings: if setting == "ro": mode = _("r/o") uri = tmp[2] parts = [] parts = tmp[2].split(':') if parts[0] == "": server = uri.split('/')[2] uri = uri.strip()[1:] else: server = parts[0] ipaddress = None if server: # Will fail on literal IPs try: # Try IPv6 first, as will Linux if has_ipv6: tmpaddress = None tmpaddress = getaddrinfo(server, 0, AF_INET6) if tmpaddress: ipaddress = "[" + list( tmpaddress)[0][4][0] + "]" # Use IPv4 if IPv6 fails or is not present if ipaddress is None: tmpaddress = None tmpaddress = getaddrinfo(server, 0, AF_INET) if tmpaddress: ipaddress = list(tmpaddress)[0][4][0] except: # noqa: E722 pass friendlyaddress = server if ipaddress is not None and not ipaddress == server: friendlyaddress = server + " (" + ipaddress + ")" info['shares'].append({ "name": name, "method": method, "type": type, "mode": mode, "path": uri, "host": server, "ipaddress": ipaddress, "friendlyaddress": friendlyaddress }) # TODO: fstab info['transcoding'] = TRANSCODING info['EX'] = '' if session: try: # gets all current stream clients for images using eStreamServer # TODO: merge eStreamServer and streamList # TODO: get tuner info for streams # TODO: get recoding/timer info if more than one info['streams'] = [] try: from enigma import eStreamServer streamServer = eStreamServer.getInstance() if streamServer is not None: for x in streamServer.getConnectedClients(): servicename = ServiceReference( x[1]).getServiceName() or "(unknown service)" if int(x[2]) == 0: strtype = "S" else: strtype = "T" info['streams'].append({ "ref": x[1], "name": servicename, "ip": x[0], "type": strtype }) except Exception as error: print("[OpenWebif] -D- no eStreamServer %s" % error) recs = NavigationInstance.instance.getRecordings() if recs: # only one stream and only TV from Plugins.Extensions.OpenWebif.controllers.stream import streamList s_name = '' # s_cip = '' print("[OpenWebif] -D- streamList count '%d'" % len(streamList)) if len(streamList) == 1: from Screens.ChannelSelection import service_types_tv # from enigma import eEPGCache # epgcache = eEPGCache.getInstance() serviceHandler = eServiceCenter.getInstance() services = serviceHandler.list( eServiceReference('%s ORDER BY name' % (service_types_tv))) channels = services and services.getContent("SN", True) s = streamList[0] srefs = s.ref.toString() for channel in channels: if srefs == channel[0]: s_name = channel[1] + ' (' + s.clientIP + ')' break print("[OpenWebif] -D- s_name '%s'" % s_name) # only for debug for stream in streamList: srefs = stream.ref.toString() print("[OpenWebif] -D- srefs '%s'" % srefs) sname = '' timers = [] for timer in NavigationInstance.instance.RecordTimer.timer_list: if timer.isRunning() and not timer.justplay: timers.append( removeBad(timer.service_ref.getServiceName())) print("[OpenWebif] -D- timer '%s'" % timer.service_ref.getServiceName()) # TODO: more than one recording if len(timers) == 1: sname = timers[0] if sname == '' and s_name != '': sname = s_name print("[OpenWebif] -D- recs count '%d'" % len(recs)) for rec in recs: feinfo = rec.frontendInfo() frontendData = feinfo and feinfo.getAll(True) if frontendData is not None: cur_info = feinfo.getTransponderData(True) if cur_info: nr = frontendData['tuner_number'] info['tuners'][nr]['rec'] = getOrbitalText( cur_info) + ' / ' + sname service = session.nav.getCurrentService() if service is not None: sname = service.info().getName() feinfo = service.frontendInfo() frontendData = feinfo and feinfo.getAll(True) if frontendData is not None: cur_info = feinfo.getTransponderData(True) if cur_info: nr = frontendData['tuner_number'] info['tuners'][nr]['live'] = getOrbitalText( cur_info) + ' / ' + sname except Exception as error: info['EX'] = error info['timerpipzap'] = False info['timerautoadjust'] = False try: timer = RecordTimerEntry('', 0, 0, '', '', 0) if hasattr(timer, "pipzap"): info['timerpipzap'] = True if hasattr(timer, "autoadjust"): info['timerautoadjust'] = True except Exception as error: print("[OpenWebif] -D- RecordTimerEntry check %s" % error) STATICBOXINFO = info return info
def addTimer( session, serviceref, begin, end, name, description, disabled, justplay, afterevent, dirname, tags, repeated, logentries=None, eit=0, ): recordHandler = NavigationInstance.instance.RecordTimer msgTimeout = config.plugins.skyrecorder.msgtimeout.value if not dirname: try: dirname = config.plugins.skyrecorder.anytimefolder.value except Exception: dirname = preferredTimerPath() print "mao1", dirname try: timer = RecordTimerEntry( ServiceReference(serviceref), begin, end, name, description, eit, disabled, justplay, afterevent, dirname=dirname, tags=tags, ) timer.repeated = repeated if logentries: timer.log_entries = logentries print "rrgggg" conflicts = recordHandler.record(timer) if conflicts: errors = [] for conflict in conflicts: errors.append(conflict.name) print "duuuupppppppeeeeee" return {"result": False, "message": "Conflicting Timer(s) detected! %s" % " / ".join(errors)} except Exception, e: print "adupppeee" print e return {"result": False, "message": "Could not add timer '%s'!" % name}
def standbyTimeout(self): from RecordTimer import RecordTimerEntry RecordTimerEntry.TryQuitMainloop()
def check(self): # Simulation ggf. stoppen if self.timer.state > TimerEntry.StateWaiting and self.simulate_recordService: self.stop_simulation() # VPS wurde wieder deaktiviert oder Timer wurde beendet if self.timer is None or self.timer.state == TimerEntry.StateEnded or self.timer.cancelled: self.program_abort() self.stop_simulation() return -1 if self.timer.vpsplugin_enabled == False or config.plugins.vps.enabled.value == False: if self.activated_auto_increase: self.timer.autoincrease = False self.program_abort() self.stop_simulation() return -1 self.nextExecution = 180 if config.plugins.vps.initial_time.value < 2 and self.timer.vpsplugin_overwrite: initial_time = 120 else: initial_time = config.plugins.vps.initial_time.value * 60 if self.timer.vpsplugin_overwrite == True: if self.timer.state == TimerEntry.StateWaiting or self.timer.state == TimerEntry.StatePrepared: # Startzeit verschieben if (self.timer.begin - 60) < time(): if self.org_timer_begin == 0: self.org_timer_begin = self.timer.begin elif (self.org_timer_begin + self.max_extending_timer) < time(): # Sendung begann immer noch nicht -> abbrechen self.timer.abort() self.session.nav.RecordTimer.doActivate(self.timer) self.program_abort() self.stop_simulation() self.timer.log( 0, "[VPS] abort timer, waited enough to find Event-ID" ) return -1 self.timer.begin += 60 if (self.timer.end - self.timer.begin) < 300: self.timer.end += 180 # auf Timer-Konflikt prüfen timersanitycheck = TimerSanityCheck( self.session.nav.RecordTimer.timer_list, self.timer) if not timersanitycheck.check(): self.timer.abort() self.session.nav.RecordTimer.doActivate(self.timer) self.program_abort() self.stop_simulation() self.timer.log( 0, "[VPS] abort timer due to TimerSanityCheck") return -1 self.session.nav.RecordTimer.timeChanged(self.timer) if 30 < self.nextExecution: self.nextExecution = 30 # Programm starten if not self.program_running: if self.timer.state == TimerEntry.StateRunning: self.program_start() elif initial_time > 0: if (self.timer.begin - initial_time) <= time(): self.program_start() else: n = self.timer.begin - initial_time - time() if n < self.nextExecution: self.nextExecution = n if self.timer.state == TimerEntry.StateRunning: if self.activated_auto_increase and self.org_timer_end != 0 and ( self.org_timer_end + (4 * 3600)) < time(): # Aufnahme läuft seit 4 Stunden im Autoincrease -> abbrechen self.timer.autoincrease = False self.activated_auto_increase = False self.dont_restart_program = True self.program_abort() self.stop_simulation() self.timer.log(0, "[VPS] stop recording, too much autoincrease") try: if self.timer.vpsplugin_wasTimerWakeup: self.timer.vpsplugin_wasTimerWakeup = False if not Screens.Standby.inTryQuitMainloop: RecordTimerEntry.TryQuitMainloop(False) except: pass return self.nextExecution
def recordNow(session, infinite): rt = session.nav.RecordTimer serviceref = session.nav.getCurrentlyPlayingServiceReference().toString() try: event = session.nav.getCurrentService().info().getEvent(0) except Exception: event = None if not event and not infinite: return { "result": False, "message": "No event found! Not recording!" } if event: (begin, end, name, description, eit) = parseEvent(event) begin = time() msg = "Instant record for current Event started" else: name = "instant record" description = "" eit = 0 if infinite: begin = time() end = begin + 3600 * 10 msg = "Infinite Instant recording started" timer = RecordTimerEntry( ServiceReference(serviceref), begin, end, name, description, eit, False, False, 0, dirname=preferredInstantRecordPath() ) timer.dontSave = True if rt.record(timer): return { "result": False, "message": "Timer conflict detected! Not recording!" } nt = { "serviceref": str(timer.service_ref), "servicename": timer.service_ref.getServiceName().replace('\xc2\x86', '').replace('\xc2\x87', ''), "eit": timer.eit, "name": timer.name, "begin": timer.begin, "end": timer.end, "duration": timer.end - timer.begin } return { "result": True, "message": msg, "newtimer": nt }
def editTimer(self, param): print "[WebComponents.Timer] editTimer" # OK first we need to parse all of your Parameters # For some of them (like afterEvent or justplay) we can use default values # for others (the serviceReference or the Begin/End time of the timer # we have to quit if they are not set/have illegal values if "sRef" not in param: return (False, "Missing Parameter: sRef") service_ref = ServiceReference(param["sRef"]) repeated = int(param.get("repeated") or 0) if "begin" not in param: return (False, "Missing Parameter: begin") begin = int(float(param["begin"])) if "end" not in param: return (False, "Missing Parameter: end") end = int(float(param["end"])) tm = time() if tm <= begin: pass elif tm > begin and tm < end and repeated == 0: begin = time() elif repeated == 0: return (False, "Illegal Parameter value for Parameter begin : '%s'" % begin) if "name" not in param: return (False, "Missing Parameter: name") name = param["name"] if "description" not in param: return (False, "Missing Parameter: description") description = param["description"].replace("\n", " ") disabled = False # Default to: Enabled if "disabled" in param: if param["disabled"] == "1": disabled = True else: # TODO - maybe we can give the user some useful hint here pass justplay = False # Default to: Record if "justplay" in param: if param["justplay"] == "1": justplay = True afterEvent = 3 # Default to Afterevent: Auto if "afterevent" in param: if (param["afterevent"] == "0") or (param["afterevent"] == "1") or (param["afterevent"] == "2"): afterEvent = int(param["afterevent"]) dirname = config.movielist.last_timer_videodir.value if "dirname" in param and param["dirname"]: dirname = param["dirname"] tags = [] if "tags" in param and param["tags"]: tags = unescape(param["tags"]).split(" ") delold = 0 if "deleteOldOnSave" in param: delold = int(param["deleteOldOnSave"]) # Try to edit an existing Timer if delold: if "channelOld" in param and param["channelOld"]: channelOld = ServiceReference(param["channelOld"]) else: return (False, "Missing Parameter: channelOld") # We do need all of the following Parameters, too, for being able of finding the Timer. # Therefore so we can neither use default values in this part nor can we # continue if a parameter is missing if "beginOld" not in param: return (False, "Missing Parameter: beginOld") beginOld = int(param["beginOld"]) if "endOld" not in param: return (False, "Missing Parameter: endOld") endOld = int(param["endOld"]) # let's try to find the timer try: for timer in self.recordtimer.timer_list + self.recordtimer.processed_timers: if str(timer.service_ref) == str(channelOld): if int(timer.begin) == beginOld: if int(timer.end) == endOld: # we've found the timer we've been searching for # Let's apply the new values timer.service_ref = service_ref timer.begin = int(begin) timer.end = int(end) timer.name = name timer.description = description timer.disabled = disabled timer.justplay = justplay timer.afterEvent = afterEvent timer.repeated = repeated timer.dirname = dirname timer.tags = tags # send the changed timer back to enigma2 and hope it's good self.session.nav.RecordTimer.timeChanged(timer) print "[WebComponents.Timer] editTimer: Timer changed!" return (True, "Timer %s has been changed!" % (timer.name)) except Exception: # obviously some value was not good, return an error return (False, "Changing the timer for '%s' failed!" % name) return (False, "Could not find timer '%s' with given start and end time!" % name) # Try adding a new Timer try: # Create a new instance of recordtimerentry timer = RecordTimerEntry( service_ref, begin, end, name, description, 0, disabled, justplay, afterEvent, dirname=dirname, tags=tags, ) timer.repeated = repeated # add the new timer self.recordtimer.record(timer) return (True, "Timer added successfully!") except Exception: # something went wrong, most possibly one of the given paramater-values was wrong return (False, "Could not add timer '%s'!" % name) return (False, "Unexpected Error")
def parseTimer(self, timer, epgcache, serviceHandler, recordHandler, checkEvtLimit, evtLimit, timers, conflicting, similars, timerdict, moviedict, simulateOnly=False): new = 0 modified = 0 skipped = 0 # Precompute timer destination dir dest = timer.destination or config.usage.default_path.getValue() # Workaround to allow search for umlauts if we know the encoding 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 = [] refstr = '1:134:1:0:0:0:0:0:0:0:FROM BOUQUET \"bouquets.tv\" ORDER BY bouquet' bouquetroot = eServiceReference(refstr) mask = eServiceReference.isDirectory if config.usage.multibouquet.getValue(): bouquets = serviceHandler.list(bouquetroot) if bouquets: while True: s = bouquets.getNext() if not s.valid(): break if s.flags & mask: info = serviceHandler.info(s) if info: bouquetlist.append((info.getName(s), s)) else: info = serviceHandler.info(bouquetroot) if info: bouquetlist.append( (info.getName(bouquetroot), bouquetroot)) # Get all services 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)) 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', 3000, 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 preveit = False for idx, (serviceref, eit, name, begin, duration, shortdesc, extdesc) in enumerate(epgmatches): 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 " + name + " because it starts in less than 60 seconds") # skipped += 1 # 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) \ )) or 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.getValue() * 60 end += config.recording.margin_after.getValue() * 60 # Overwrite endtime if requested if timer.justplay and not timer.setEndtime: end = begin # Eventually change service to alternative if timer.overrideAlternatives: serviceref = timer.getAlternative(serviceref) # Append to timerlist and abort if simulating timers.append((name, begin, end, serviceref, timer.name)) if simulateOnly: continue # 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.checkSimilarity(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 # Initialize newEntry = None oldExists = False # 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 or config.plugins.autotimer.try_guessing.getValue() ) and getTimeDiff(rtimer, evtBegin, evtEnd) > ( (duration / 10) * 8): oldExists = True # Abort if we don't want to modify timers or timer is repeated if config.plugins.autotimer.refresh.getValue( ) == "none" or rtimer.repeated: print( "[AutoTimer] Won't modify existing timer because either no modification allowed or repeated timer" ) break if eit == preveit: break if (evtBegin - (config.recording.margin_before.getValue() * 60) != rtimer.begin) or ( evtEnd + (config.recording.margin_after.getValue() * 60) != rtimer.end): if rtimer.isAutoTimer and eit == rtimer.eit: print( "[AutoTimer] AutoTimer %s modified this automatically generated timer." % (timer.name)) rtimer.log( 501, "[AutoTimer] AutoTimer %s modified this automatically generated timer." % (timer.name)) preveit = eit else: if config.plugins.autotimer.refresh.getValue( ) != "all": print( "[AutoTimer] Won't modify existing timer because it's no timer set by us" ) break rtimer.log( 501, "[AutoTimer] Warning, AutoTimer %s messed with a timer which might not belong to it." % (timer.name)) newEntry = rtimer modified += 1 self.modifyTimer(rtimer, name, shortdesc, begin, end, serviceref) break else: print( "[AutoTimer] Skipping timer because it has not changed." ) skipped += 1 break elif timer.avoidDuplicateDescription >= 1 and not rtimer.disabled: if self.checkSimilarity(timer, name, rtimer.name, shortdesc, rtimer.description, extdesc, rtimer.extdesc): print( "[AutoTimer] We found a timer with similar description, skipping event" ) oldExists = True 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 for rtimer in chain.from_iterable(itervalues(timerdict)): if not rtimer.disabled: if self.checkDoubleTimers(timer, name, rtimer.name, begin, rtimer.begin, end, rtimer.end): oldExists = True # print("[AutoTimer] We found a timer with same StartTime, skipping event") break if timer.avoidDuplicateDescription >= 2: if self.checkSimilarity(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 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) 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 = timer.destination 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.getValue(): tags.append('AutoTimer') if config.plugins.autotimer.add_name_to_tags.getValue(): 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.getValue( ): # 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.checkSimilarity(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.getValue( ): 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) self.result = (new, modified, skipped) self.completed.append(timer.name) sleep(0.5)
def parseEPG(self, simulateOnly = False): if NavigationInstance.instance is None: print "[AutoTimer] Navigation is not available, can't parse EPG" return (0, 0, 0, [], []) total = 0 new = 0 modified = 0 timers = [] conflicting = [] # NOTE: the config option specifies "the next X days" which means today (== 1) + X delta = timedelta(days = config.plugins.autotimer.maxdaysinfuture.value + 1) evtLimit = mktime((date.today() + delta).timetuple()) checkEvtLimit = delta.days > 1 del delta self.readXml() # Save Recordings in a dict to speed things up a little # We include processed timers as we might search for duplicate descriptions recorddict = {} for rtimer in NavigationInstance.instance.RecordTimer.timer_list + NavigationInstance.instance.RecordTimer.processed_timers: if not rtimer.disabled: recorddict.setdefault(str(rtimer.service_ref), []).append(rtimer) # Iterate Timer for timer in self.getEnabledTimerList(): # 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 if timer.encoding != 'UTF-8': try: match = match.decode('UTF-8').encode(timer.encoding) except UnicodeDecodeError: pass # Search EPG, default to empty list ret = self.epgcache.search(('RI', 500, typeMap[timer.searchType], match, caseMap[timer.searchCase])) or () for serviceref, eit in ret: eserviceref = eServiceReference(serviceref) evt = self.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() # Gather Information evtInfo = self.normalizeEvent(evt) evtBegin = begin = evt.getBeginTime() duration = evt.getDuration() 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 # If maximum days in future is set then check time if checkEvtLimit: if begin > evtLimit: continue # Convert begin time timestamp = localtime(begin) # Update timer timer.update(begin, timestamp) # Check Duration, Timespan, Timeframe and Excludes if timer.checkServices(serviceref) \ or timer.checkDuration(duration) \ or timer.checkTimespan(timestamp) \ or timer.checkTimeframe(begin) \ or timer.checkFilter( evtInfo.name, evtInfo.shortDescription, evtInfo.extendedDescription, str(timestamp.tm_wday) ): 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 # Eventually change service to alternative if timer.overrideAlternatives: serviceref = timer.getAlternative(serviceref) total += 1 # Append to timerlist and abort if simulating timers.append((evtInfo.name, begin, end, serviceref, timer.name)) if simulateOnly: continue # Check for existing recordings in directory if timer.avoidDuplicateDescription == 3 and self.checkMovies(evtInfo, dest): continue # Initialize newEntry = None oldExists = False # 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 recorddict.get(serviceref, ()): if rtimer.eit == eit or config.plugins.autotimer.try_guessing.value and getTimeDiff(rtimer, begin, end) > ((duration/10)*8): oldExists = True # Abort if we don't want to modify timers or timer is repeated if config.plugins.autotimer.refresh.value == "none": print "[AutoTimer] Won't modify existing timer because no modification allowed" break if rtimer.repeated: print "[AutoTimer] Won't modify existing timer because repeated timer" break if hasattr(rtimer, "isAutoTimer"): rtimer.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" break rtimer.log(501, "[AutoTimer] Warning, AutoTimer %s messed with a timer which might not belong to it." % (timer.name,)) newEntry = rtimer modified += 1 # Modify values saved in timer newEntry.name = evtInfo.name newEntry.description = evtInfo.shortDescription newEntry.begin = int(begin) newEntry.end = int(end) newEntry.service_ref = ServiceReference(serviceref) break elif timer.avoidDuplicateDescription >= 1 and self.normalizeRecordTimer(rtimer) == evtInfo: oldExists = True print "[AutoTimer] We found a timer with same service and 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 and self.isEventInList(evtInfo, recorddict, self.normalizeRecordTimer): print "[AutoTimer] We found a timer with same description, skipping event" continue if timer.checkCounter(timestamp): print "[AutoTimer] Not adding new timer because counter is depleted." continue newEntry = RecordTimerEntry(ServiceReference(serviceref), begin, end, evtInfo.name, evtInfo.shortDescription, eit) newEntry.log(500, "[AutoTimer] Adding new timer based on AutoTimer %s." % (timer.name,)) # Mark this entry as AutoTimer (only AutoTimers will have this Attribute set) 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 = timer.destination newEntry.justplay = timer.justplay newEntry.tags = timer.tags newEntry.vpsplugin_enabled = timer.vps_enabled newEntry.vpsplugin_overwrite = timer.vps_overwrite if oldExists: # XXX: this won't perform a sanity check, but do we actually want to do so? NavigationInstance.instance.RecordTimer.timeChanged(newEntry) else: conflicts = NavigationInstance.instance.RecordTimer.record(newEntry) if conflicts and config.plugins.autotimer.disabled_on_conflict.value: conflictString = ' / '.join(["%s (%s)" % (x.name, strftime("%Y%m%d %H%M", localtime(x.begin))) for x in conflicts]) newEntry.log(503, "[AutoTimer] Timer disabled because of conflicts with %s." % (conflictString,)) del 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 = NavigationInstance.instance.RecordTimer.record(newEntry) conflicting.append((evtInfo.name, begin, end, serviceref, timer.name)) if conflicts is None: timer.decrementCounter() new += 1 recorddict.setdefault(serviceref, []).append(newEntry) else: conflicting.append((evtInfo.name, begin, end, serviceref, timer.name)) return (total, new, modified, timers, conflicting)
def UPDT(self, args): # <id> <settings> args = args.split(None, 1) if len(args) != 2: payload = "%d argument error" % (CODE_SYNTAX, ) return self.sendLine(payload) try: timerId = int(args[0]) except ValueError: payload = "%d argument error" % (CODE_SYNTAX, ) return self.sendLine(payload) list = self.getTimerList() if timerId < 1: payload = "%d argument error" % (CODE_SYNTAX, ) return self.sendLine(payload) if len(list) >= timerId: oldTimer = list[timerId - 1] else: oldTimer = None try: flags, channelid, datestring, beginstring, endstring, priority, lifetime, name, description = args[ 1].split(':') flags = int(flags) service_ref = ServiceReference(self.channelList[int(channelid) - 1]) datestruct = strptime(datestring, '%Y-%m-%d') timestruct = strptime(beginstring, '%H%M') begin = mktime( (datestruct.tm_year, datestruct.tm_mon, datestruct.tm_mday, timestruct.tm_hour, timestruct.tm_min, 0, datestruct.tm_wday, datestruct.tm_yday, -1)) timestruct = strptime(endstring, '%H%M') end = mktime( (datestruct.tm_year, datestruct.tm_mon, datestruct.tm_mday, timestruct.tm_hour, timestruct.tm_min, 0, datestruct.tm_wday, datestruct.tm_yday, -1)) del datestruct, timestruct except ValueError as e: payload = "%d argument error" % (CODE_SYNTAX, ) return self.sendLine(payload) except KeyError as e: payload = "%d argument error" % (CODE_SYNTAX, ) return self.sendLine(payload) if end < begin: end += 86400 # Add 1 day, beware - this is evil and might not work correctly due to dst timer = RecordTimerEntry(service_ref, begin, end, name, description, 0, disabled=flags & 1 == 0) if oldTimer: recordTimer.removeEntry(oldTimer) timer.justplay = oldTimer.justplay timer.afterEvent = oldTimer.afterEvent timer.dirname = oldTimer.dirname timer.tags = oldTimer.tags timer.log_entries = oldTimer.log_entries conflict = recordTimer.record(timer) if conflict is None: return self.sendTimerLine(timer, timerId, last=True) else: payload = "%d timer conflict detected, original timer lost." % ( CODE_ERR_LOCAL, ) return self.sendLine(payload)
def check_and_add_event(self, neweventid): if not config.plugins.vps.allow_seeking_multiple_pdc.value: return epgcache = eEPGCache.getInstance() evt = epgcache.lookupEventId(self.rec_ref, neweventid) if evt: evt_begin = evt.getBeginTime() + 60 evt_end = evt.getBeginTime() + evt.getDuration() - 60 if evt_begin < self.timer.begin: return for checktimer in self.session.nav.RecordTimer.timer_list: if checktimer == self.timer: continue if (checktimer.begin - evt_begin) > 3600*2: break compareString = checktimer.service_ref.ref.toCompareString() if compareString == self.timer.service_ref.ref.toCompareString() or compareString == self.rec_ref.toCompareString(): if checktimer.eit == neweventid: return if checktimer.begin <= evt_begin and checktimer.end >= evt_end: if checktimer.vpsplugin_enabled is None or not checktimer.vpsplugin_enabled: return # manuell angelegter Timer mit VPS if checktimer.name == "" and checktimer.vpsplugin_time is not None: checktimer.eit = neweventid checktimer.name = evt.getEventName() checktimer.description = evt.getShortDescription() checktimer.vpsplugin_time = None checktimer.log(0, "[VPS] changed timer (found same PDC-Time as in other VPS-recording)") return # eigenen Timer überprüfen, wenn Zeiten nicht überschrieben werden dürfen if not self.timer.vpsplugin_overwrite and evt_begin <= self.timer.end: check_already_existing = [x for (x,y) in self.next_events if y == neweventid] if len(check_already_existing) > 0: start = check_already_existing.pop() if start == evt_begin: return else: self.next_events.remove( (start, neweventid) ) self.timer.log(0, "[VPS] delete event_id "+ str(neweventid) +" because of delay "+ str(evt_begin - start)) self.next_events.append( (evt_begin, neweventid) ) self.next_events = sorted(self.next_events) self.timer.log(0, "[VPS] add event_id "+ str(neweventid)) else: newevent_data = parseEvent(evt) newEntry = RecordTimerEntry(ServiceReference(self.rec_ref), *newevent_data) newEntry.vpsplugin_enabled = True newEntry.vpsplugin_overwrite = True newEntry.dirname = self.timer.dirname newEntry.log(0, "[VPS] added this timer (found same PDC-Time as in other VPS-recording)") # Wenn kein Timer-Konflikt auftritt, wird der Timer angelegt. res = NavigationInstance.instance.RecordTimer.record(newEntry) self.timer.log(0, "[VPS] added another timer, res "+ str(res))
def editTimer(self, param): print "[WebComponents.Timer] editTimer" #OK first we need to parse all of your Parameters #For some of them (like afterEvent or justplay) we can use default values #for others (the serviceReference or the Begin/End time of the timer #we have to quit if they are not set/have illegal values if 'serviceref' in param: param['sRef'] = str(param['serviceref']) if 'sRef' not in param: return ( False, "Missing Parameter: sRef" ) service_ref = ServiceReference(param['sRef']) repeated = int(param.get('repeated') or 0) if 'begin' not in param: return ( False, "Missing Parameter: begin" ) begin = int(float(param['begin'])) if 'end' not in param: return ( False, "Missing Parameter: end" ) end = int(float(param['end'])) tm = time() if tm <= begin: pass elif tm > begin and tm < end and repeated == 0: begin = time() elif repeated == 0: return ( False, "Illegal Parameter value for Parameter begin : '%s'" % begin ) if 'name' not in param: return ( False, "Missing Parameter: name" ) name = param['name'] if 'description' not in param: return ( False, "Missing Parameter: description" ) description = param['description'].replace("\n", " ") disabled = False #Default to: Enabled if 'disabled' in param: if param['disabled'] == "1": disabled = True else: #TODO - maybe we can give the user some useful hint here pass justplay = False #Default to: Record if 'justplay' in param: if param['justplay'] == "1": justplay = True afterEvent = 3 #Default to Afterevent: Auto if 'afterevent' in param: if (param['afterevent'] == "0") or (param['afterevent'] == "1") or (param['afterevent'] == "2"): afterEvent = int(param['afterevent']) dirname = preferredTimerPath() if 'dirname' in param and param['dirname']: dirname = param['dirname'] tags = [] if 'tags' in param and param['tags']: tags = unescape(param['tags']).split(' ') delold = 0 if 'deleteOldOnSave' in param: delold = int(param['deleteOldOnSave']) #Try to edit an existing Timer if delold: if 'channelOld' in param and param['channelOld']: channelOld = ServiceReference(param['channelOld']) else: return ( False, "Missing Parameter: channelOld" ) # We do need all of the following Parameters, too, for being able of finding the Timer. # Therefore so we can neither use default values in this part nor can we # continue if a parameter is missing if 'beginOld' not in param: return ( False, "Missing Parameter: beginOld" ) beginOld = int(param['beginOld']) if 'endOld' not in param: return ( False, "Missing Parameter: endOld" ) endOld = int(param['endOld']) #let's try to find the timer try: for timer in self.__recordtimer.timer_list + self.__recordtimer.processed_timers: if str(timer.service_ref) == str(channelOld): if int(timer.begin) == beginOld: if int(timer.end) == endOld: #we've found the timer we've been searching for #Delete the old entry self.__recordtimer.removeEntry(timer) old = timer timer = RecordTimerEntry(service_ref, begin, end, name, description, 0, disabled, justplay, afterEvent, dirname=dirname, tags=tags) timer.repeated = repeated timer.log_entries = old.log_entries timer.processRepeated() #send the changed timer back to enigma2 and hope it's good conflicts = self.__recordtimer.record(timer) if conflicts is None: print "[WebComponents.Timer] editTimer: Timer changed!" return ( True, "Timer '%s' changed" %(timer.name) ) else: print "[WebComponents.Timer] editTimer conflicting Timers: %s" %(conflicts) msg = "" for timer in conflicts: msg = "%s / %s" %(msg, timer.name) return (False, "Conflicting Timer(s) detected! %s" %(msg)) except Exception: #obviously some value was not good, return an error return ( False, "Changing the timer for '%s' failed!" % name ) return ( False, "Could not find timer '%s' with given start and end time!" % name ) #Try adding a new Timer try: #Create a new instance of recordtimerentry timer = RecordTimerEntry(service_ref, begin, end, name, description, 0, disabled, justplay, afterEvent, dirname=dirname, tags=tags) timer.repeated = repeated #add the new timer conflicts = self.__recordtimer.record(timer) if conflicts is None: return ( True, "Timer '%s' added" %(timer.name) ) else: print "[WebComponents.Timer] editTimer conflicting Timers: %s" %(conflicts) msg = "" for timer in conflicts: msg = "%s / %s" %(msg, timer.name) return (False, "Conflicting Timer(s) detected! %s" %(msg)) except Exception, e: #something went wrong, most possibly one of the given paramater-values was wrong print "[WebComponents.Timer] editTimer exception: %s" %(e) return ( False, "Could not add timer '%s'!" % name )
service_ref = ServiceReference(self.channelList[int(channelid)-1]) datestruct = strptime(datestring, '%Y-%m-%d') timestruct = strptime(beginstring, '%H%M') begin = mktime((datestruct.tm_year, datestruct.tm_mon, datestruct.tm_mday, timestruct.tm_hour, timestruct.tm_min, 0, datestruct.tm_wday, datestruct.tm_yday, -1)) timestruct = strptime(endstring, '%H%M') end = mktime((datestruct.tm_year, datestruct.tm_mon, datestruct.tm_mday, timestruct.tm_hour, timestruct.tm_min, 0, datestruct.tm_wday, datestruct.tm_yday, -1)) del datestruct, timestruct except ValueError, e: payload = "%d argument error" % (CODE_SYNTAX,) return self.sendLine(payload) except KeyError, e: payload = "%d argument error" % (CODE_SYNTAX,) return self.sendLine(payload) if end < begin: end += 86400 # Add 1 day, beware - this is evil and might not work correctly due to dst timer = RecordTimerEntry(service_ref, begin, end, name, description, 0, disabled=flags & 1 == 0) if oldTimer: recordTimer.removeEntry(oldTimer) timer.justplay = oldTimer.justplay timer.afterEvent = oldTimer.afterEvent timer.dirname = oldTimer.dirname timer.tags = oldTimer.tags timer.log_entries = oldTimer.log_entries conflict = recordTimer.record(timer) if conflict is None: return self.sendTimerLine(timer, timerId, last=True) else: payload = "%d timer conflict detected, original timer lost." % (CODE_ERR_LOCAL,) return self.sendLine(payload)
def parseTimer(self, timer, epgcache, serviceHandler, recordHandler, checkEvtLimit, evtLimit, timers, conflicting, similars, timerdict, moviedict, simulateOnly=False): new = 0 modified = 0 skipped = 0 # Precompute timer destination dir dest = timer.destination or config.usage.default_path.value match = timer.match 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 = [] refstr = '1:134:1:0:0:0:0:0:0:0:FROM BOUQUET \"bouquets.tv\" ORDER BY bouquet' bouquetroot = eServiceReference(refstr) mask = eServiceReference.isDirectory if config.usage.multibouquet.value: bouquets = serviceHandler.list(bouquetroot) if bouquets: while True: s = bouquets.getNext() if not s.valid(): break if s.flags & mask: info = serviceHandler.info(s) if info: bouquetlist.append((info.getName(s), s)) else: info = serviceHandler.info(bouquetroot) if info: bouquetlist.append((info.getName(bouquetroot), bouquetroot)) # Get all services 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 ) ) 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', 3000, 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 preveit = False for idx, ( serviceref, eit, name, begin, duration, shortdesc, extdesc ) in enumerate( epgmatches ): 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 " + name + " because it starts in less than 60 seconds") # skipped += 1 # 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) \ )) or 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 # Eventually change service to alternative if timer.overrideAlternatives: serviceref = timer.getAlternative(serviceref) # Append to timerlist and abort if simulating timers.append((name, begin, end, serviceref, timer.name)) if simulateOnly: continue # 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.checkSimilarity(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 # Initialize newEntry = None oldExists = False # 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 or config.plugins.autotimer.try_guessing.getValue()) and getTimeDiff(rtimer, evtBegin, evtEnd) > ((duration/10)*8): oldExists = True # Abort if we don't want to modify timers or timer is repeated if config.plugins.autotimer.refresh.value == "none" or rtimer.repeated: print("[AutoTimer] Won't modify existing timer because either no modification allowed or repeated timer") break if eit == preveit: break if (evtBegin - (config.recording.margin_before.getValue() * 60) != rtimer.begin) or (evtEnd + (config.recording.margin_after.getValue() * 60) != rtimer.end) or (shortdesc != rtimer.description): if rtimer.isAutoTimer and eit == rtimer.eit: print ("[AutoTimer] AutoTimer %s modified this automatically generated timer." % (timer.name)) # rtimer.log(501, "[AutoTimer] AutoTimer %s modified this automatically generated timer." % (timer.name)) preveit = eit else: if config.plugins.autotimer.refresh.getValue() != "all": print("[AutoTimer] Won't modify existing timer because it's no timer set by us") break rtimer.log(501, "[AutoTimer] Warning, AutoTimer %s messed with a timer which might not belong to it: %s ." % (timer.name, rtimer.name)) newEntry = rtimer modified += 1 self.modifyTimer(rtimer, name, shortdesc, begin, end, serviceref, eit) # rtimer.log(501, "[AutoTimer] AutoTimer modified timer: %s ." % (rtimer.name)) break else: print ("[AutoTimer] Skipping timer because it has not changed.") skipped += 1 break elif timer.avoidDuplicateDescription >= 1 and not rtimer.disabled: if self.checkSimilarity(timer, name, rtimer.name, shortdesc, rtimer.description, extdesc, rtimer.extdesc ): print("[AutoTimer] We found a timer with similar description, skipping event") oldExists = True 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 for rtimer in chain.from_iterable( itervalues(timerdict) ): if not rtimer.disabled: if self.checkDoubleTimers(timer, name, rtimer.name, begin, rtimer.begin, end, rtimer.end ): oldExists = True # print("[AutoTimer] We found a timer with same StartTime, skipping event") break if timer.avoidDuplicateDescription >= 2: if self.checkSimilarity(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 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 = timer.destination 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: 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.checkSimilarity(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) self.result=(new, modified, skipped) self.completed.append(timer.name) sleep(0.5)
def timerAdd(self): cur = self['list'].getCurrent() event = cur[0] if event is None: return serviceref = cur[1] isRecordEvent = isRepeat = firstNextRepeatEvent = isRunning = False eventid = event.getEventId() begin = event.getBeginTime() duration = event.getDuration() end = begin + duration refstr = ':'.join(serviceref.ref.toString().split(':')[:11]) isRepeat = False prev_state = 0 for timer in self.session.nav.RecordTimer.timer_list: needed_ref = ':'.join( timer.service_ref.ref.toString().split(':')[:11]) == refstr if needed_ref and timer.eit == eventid and ( begin < timer.begin <= end or timer.begin <= begin <= timer.end): isRecordEvent = True break if not isRecordEvent: x = self.session.nav.RecordTimer.isInTimer(eventid, begin, duration, refstr, True) if x and x[1][0] in (2, 7, 12): isRecordEvent = True timer = x[3] if isRecordEvent: isRepeat = timer.repeated prev_state = timer.state isRunning = prev_state in (1, 2) title_text = isRepeat and _( 'Attention, this is repeated timer!\n') or '' firstNextRepeatEvent = isRepeat and ( begin < timer.begin <= end or timer.begin <= begin <= timer.end) and not timer.justplay menu = [(_('Delete timer'), 'delete'), (_('Edit timer'), 'edit')] buttons = ['red', 'green'] if not isRunning: if firstNextRepeatEvent and timer.isFindRunningEvent( ) and not timer.isFindNextEvent(): menu.append((_('Options disable timer'), 'disablerepeat')) else: menu.append((_('Disable timer'), 'disable')) buttons.append('yellow') elif prev_state == 2 and firstNextRepeatEvent: menu.append( (_('Options disable timer'), 'disablerepeatrunning')) buttons.append('yellow') menu.append((_('Timer Overview'), 'timereditlist')) def timerAction(choice): if choice is not None: if choice[1] == 'delete': self.removeTimer(timer) elif choice[1] == 'edit': self.session.openWithCallback(self.finishedEdit, TimerEntry, timer) elif choice[1] == 'disable': self.disableTimer(timer, prev_state) elif choice[1] == 'timereditlist': self.session.open(TimerEditList) elif choice[1] == 'disablerepeatrunning': self.disableTimer(timer, prev_state, repeat=True, record=True) elif choice[1] == 'disablerepeat': self.disableTimer(timer, prev_state, repeat=True) self.session.openWithCallback( timerAction, ChoiceBox, title=title_text + _("Select action for timer '%s'.") % timer.name, list=menu, keys=buttons) else: newEntry = RecordTimerEntry(serviceref, checkOldTimers=True, dirname=preferredTimerPath(), *parseEvent(event)) self.session.openWithCallback(self.finishedAdd, TimerEntry, newEntry)
def parseTimer(self, timer, epgcache, serviceHandler, recordHandler, checkEvtLimit, evtLimit, timers, conflicting, similars, skipped, timerdict, moviedict, simulateOnly=False): new = 0 modified = 0 # 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 and not s.flags & eServiceReference.isInvisible: 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 == 31) || (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', 2500, 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() # timer destination dir dest = timer.destination 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) serviceref = i.toString() doLog("[AutoTimer] Serviceref2 %s" % (str(serviceref))) # 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 and timer.descShortEqualExt: 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 oldEntry = None oldExists = False allow_modify = True newAT = None # Eventually change service to alternative if timer.overrideAlternatives: serviceref = timer.getAlternative(serviceref) if timer.series_labeling and sp_getSeasonEpisode is not None: allow_modify = False 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] or name shortdesc = sp[1] or shortdesc dest = sp[2] or dest doLog(str(sp[3])) allow_modify = True doLog("[AutoTimer SeriesPlugin] 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("[AutoTimer SeriesPlugin] 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])) allow_modify = True doLog("[AutoTimer SeriesPlugin] Returned name, desc, path %s %s %s" % (name, shortdesc, dest)) 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 oldEntry = 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) or timeSimilarityPercent(rtimer, evtBegin, evtEnd, timer) > 80: oldExists = True doLog("[AutoTimer] We found a timer based on time guessing") newEntry = rtimer oldEntry = 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("[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 "autotimer" in newEntry.flags: 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("[AutoTimer] 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) if allow_modify: if self.modifyTimer(newEntry, name, shortdesc, begin, end, serviceref, eit, base_timer=timer): msg = "[AutoTimer] AutoTimer modified timer: %s ." % (newEntry.name) doLog(msg) newEntry.log(501, msg) modified += 1 else: msg = "[AutoTimer] AutoTimer modification not allowed for timer %s because conflicts or double timer." % (newEntry.name) doLog(msg) if oldEntry: self.setOldTimer(newEntry, oldEntry) doLog("[AutoTimer] conflict for modification timer %s detected return to old timer" % (newEntry.name)) continue else: msg = "[AutoTimer] AutoTimer modification not allowed for timer: %s ." % (newEntry.name) doLog(msg) continue else: newEntry = RecordTimerEntry(ServiceReference(serviceref), begin, end, name, shortdesc, eit) newAT = True msg = "[AutoTimer] Try to add new timer based on AutoTimer %s." % (timer.name) doLog(msg) newEntry.log(500, msg) # Mark this entry as AutoTimer newEntry.flags.add("autotimer") # 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 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: if 'AutoTimer' not in tags: 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(" ", "_") if tagname not in tags: tags.append(tagname) newEntry.tags = tags if oldExists and newAT is None: if self.isResolvedConflict(newEntry): recordHandler.timeChanged(newEntry) else: if oldEntry: self.setOldTimer(newEntry, oldEntry) doLog("[AutoTimer] rechecking - conflict for timer %s detected return to old timer" % (newEntry.name)) continue elif newAT: newAT = newEntry 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) # add new timer in AT timer list atDoubleTimer = False refstr = ':'.join(newEntry.service_ref.ref.toString().split(':')[:11]) for at in addNewTimers: needed_ref = ':'.join(at.service_ref.ref.toString().split(':')[:11]) == refstr if needed_ref and at.eit == newEntry.eit and (newEntry.begin < at.begin <= newEntry.end or at.begin <= newEntry.begin <= at.end): atDoubleTimer = True break if atDoubleTimer: doLog("[AutoTimer] ignore double new auto timer %s." % newEntry.name) continue else: addNewTimers.append(newEntry) # 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() if newEntry in (recordHandler.timer_list[:] + recordHandler.processed_timers[:]): 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() else: doLog("[AutoTimer] ignore double timer %s." % newEntry.name) # 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 if newEntry in (recordHandler.timer_list[:] + recordHandler.processed_timers[:]): recordHandler.timeChanged(newEntry) else: # 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) elif newAT != newEntry and newEntry in (recordHandler.timer_list[:] + recordHandler.processed_timers[:]): if not self.isResolvedConflict(newEntry): newEntry.disabled = True recordHandler.timeChanged(newEntry) doLog("[AutoTimer] Unknown conflict, disable this timer %s." % newEntry.name) return (new, modified)
def addTimer(serviceref, begin, end, name, description, eit, disabled, dirname, vpsSettings, tags, logentries=None): recordHandler = NavigationInstance.instance.RecordTimer # config.plugins.serienRec.seriensubdir # if not dirname: # try: # dirname = config.plugins.serienRec.savetopath.value # except Exception: # dirname = preferredTimerPath() try: try: timer = RecordTimerEntry( ServiceReference(serviceref), begin, end, name, description, eit, disabled=disabled, justplay=config.plugins.serienRec.justplay.value, zapbeforerecord=config.plugins.serienRec.zapbeforerecord.value, justremind=config.plugins.serienRec.justremind.value, afterEvent=int(config.plugins.serienRec.afterEvent.value), dirname=dirname) except Exception: sys.exc_clear() timer = RecordTimerEntry( ServiceReference(serviceref), begin, end, name, description, eit, disabled, config.plugins.serienRec.justplay.value | config.plugins.serienRec.justremind.value, afterEvent=int(config.plugins.serienRec.afterEvent.value), dirname=dirname, tags=None) timer.repeated = 0 # Add tags timerTags = timer.tags[:] timerTags.append('SerienRecorder') if len(tags) != 0: timerTags.extend(tags) timer.tags = timerTags # If eit = 0 the VPS plugin won't work properly for this timer, so we have to disable VPS in this case. if SerienRecorder.VPSPluginAvailable and eit is not 0: timer.vpsplugin_enabled = vpsSettings[0] timer.vpsplugin_overwrite = timer.vpsplugin_enabled and (not vpsSettings[1]) if logentries: timer.log_entries = logentries timer.log(0, "[SerienRecorder] Timer angelegt") conflicts = recordHandler.record(timer) if conflicts: errors = [] for conflict in conflicts: errors.append(conflict.name) return { "result": False, "message": "In Konflikt stehende Timer vorhanden! %s" % " / ".join(errors) } except Exception, e: print "[%s] <%s>" % (__name__, e) return { "result": False, "message": "Timer konnte nicht angelegt werden '%s'!" % e }
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 allow_modify = True # Eventually change service to alternative if timer.overrideAlternatives: serviceref = timer.getAlternative(serviceref) if timer.series_labeling and sp_getSeasonEpisode is not None: allow_modify = False #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)) allow_modify = True 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)) allow_modify = True 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 if ServiceRecordingSettings: begin -= ServiceRecordingSettings.instance.getMarginBefore(eserviceref) end += ServiceRecordingSettings.instance.getMarginAfter(eserviceref) else: 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 if allow_modify: self.modifyTimer(newEntry, name, shortdesc, begin, end, serviceref, eit) msg = "[AutoTimer] AutoTimer modified timer: %s ." % (newEntry.name) doLog(msg) newEntry.log(501, msg) else: msg = "[AutoTimer] AutoTimer modification not allowed for timer: %s ." % (newEntry.name) doLog(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) return (new, modified)
def getTimers(self): self._timer_list = [] self._processed_timers = [] baseurl = self.getBaseUrl() print("[ClientModeBoxRemoteTimer] get remote timer list") try: httprequest = urllib2.urlopen(baseurl + '/web/timerlist') xmldoc = minidom.parseString(httprequest.read()) timers = xmldoc.getElementsByTagName('e2timer') for timer in timers: serviceref = ServiceReference( getValueFromNode(timer, 'e2servicereference')) begin = int(getValueFromNode(timer, 'e2timebegin')) end = int(getValueFromNode(timer, 'e2timeend')) name = getValueFromNode(timer, 'e2name') description = getValueFromNode(timer, 'e2description') eit = int(getValueFromNode(timer, 'e2eit')) disabled = int(getValueFromNode(timer, 'e2disabled')) justplay = int(getValueFromNode(timer, 'e2justplay')) afterevent = int(getValueFromNode(timer, 'e2afterevent')) repeated = int(getValueFromNode(timer, 'e2repeated')) location = getValueFromNode(timer, 'e2location') tags = getValueFromNode(timer, 'e2tags').split(" ") entry = RecordTimerEntry(serviceref, begin, end, name, description, eit, disabled, justplay, afterevent, dirname=location, tags=tags, descramble=1, record_ecm=0, isAutoTimer=0, always_zap=0) entry.repeated = repeated entry.orig = RecordTimerEntry(serviceref, begin, end, name, description, eit, disabled, justplay, afterevent, dirname=location, tags=tags, descramble=1, record_ecm=0, isAutoTimer=0, always_zap=0) entry.orig.repeated = repeated if entry.shouldSkip( ) or entry.state == TimerEntry.StateEnded or ( entry.state == TimerEntry.StateWaiting and entry.disabled): insort(self._processed_timers, entry) else: insort(self._timer_list, entry) except Exception as e: print("[ClientModeBoxRemoteTimer]", e) self.last_update_ts = time()
class vps_timer: def __init__(self, timer, session): self.timer = timer self.session = session self.program = eConsoleAppContainer() self.dataAvail_conn = self.program.dataAvail.connect(self.program_dataAvail) self.appClosed_conn = self.program.appClosed.connect(self.program_closed) self.program_running = False self.program_try_search_running = False self.activated_auto_increase = False self.simulate_recordService = None self.demux = -1 self.rec_ref = None self.found_pdc = False self.dont_restart_program = False self.org_timer_end = 0 self.org_timer_begin = 0 self.max_extending_timer = 4*3600 self.next_events = [ ] self.new_timer_copy = None self.pausing = False def program_closed(self, retval): self.timer.log(0, "[VPS] stop monitoring (process terminated)") if self.program_running or self.program_try_search_running: self.program_running = False self.program_try_search_running = False self.stop_simulation() def program_dataAvail(self, str): if self.timer is None or self.timer.state == TimerEntry.StateEnded or self.timer.cancelled: self.program_abort() self.stop_simulation() return if self.timer.vpsplugin_enabled == False or config.plugins.vps.enabled.value == False: if self.activated_auto_increase: self.timer.autoincrease = False self.program_abort() self.stop_simulation() return lines = str.split("\n") for line in lines: data = line.split() if len(data) == 0: continue self.timer.log(0, "[VPS] " + line) if data[0] == "RUNNING_STATUS": if data[1] == "0": # undefined if data[2] == "FOLLOWING": data[1] = "1" else: data[1] = "4" if data[1] == "1": # not running # Wenn der Eintrag im Following (Section_Number = 1) ist, # dann nicht beenden (Sendung begann noch gar nicht) if data[2] == "FOLLOWING": self.activate_autoincrease() elif self.timer.state == TimerEntry.StateRunning and not self.pausing and not self.set_next_event(): self.stop_recording() self.dont_restart_program = True self.program_abort() elif data[1] == "2": # starts in a few seconds self.activate_autoincrease() if self.timer.state == TimerEntry.StateWaiting: self.session.nav.RecordTimer.doActivate(self.timer) elif data[1] == "3": # pausing self.pausing = True if self.timer.state == TimerEntry.StateRunning: self.activate_autoincrease() elif data[1] == "4": # running self.pausing = False if self.timer.state == TimerEntry.StateRunning: self.activate_autoincrease() elif self.timer.state == TimerEntry.StateWaiting or self.timer.state == TimerEntry.StatePrepared: # setze Startzeit auf jetzt self.timer.begin = int(time()) self.session.nav.RecordTimer.timeChanged(self.timer) self.activate_autoincrease() self.program_abort() self.stop_simulation() vps_timers.checksoon(2000) # Programm neu starten elif data[1] == "5": # service off-air self.timer.vpsplugin_overwrite = False if self.activated_auto_increase: self.timer.autoincrease = False self.activated_auto_increase = False elif data[0] == "EVENT_ENDED": if not self.set_next_event(): if self.timer.state == TimerEntry.StateRunning: self.stop_recording() self.program_abort() self.stop_simulation() elif data[0] == "OTHER_TS_RUNNING_STATUS": if self.timer.state == TimerEntry.StateWaiting: self.timer.start_prepare = int(time()) self.session.nav.RecordTimer.doActivate(self.timer) self.program_abort() self.stop_simulation() vps_timers.checksoon(2000) # PDC elif data[0] == "PDC_FOUND_EVENT_ID": self.found_pdc = True self.timer.eit = int(data[1]) epgcache = eEPGCache.getInstance() evt = epgcache.lookupEventId(self.rec_ref, self.timer.eit) if evt: self.timer.name = evt.getEventName() self.timer.description = evt.getShortDescription() self.program_abort() vps_timers.checksoon(500) elif data[0] == "FOUND_EVENT_ON_SCHEDULE": starttime = int(data[1]) duration = int(data[2]) # Soll die Sendung laut EPG erst nach dem Ende dieses Timers beginnen? if (not self.timer.vpsplugin_overwrite and (self.timer.end + 300) < starttime) or (self.timer.vpsplugin_overwrite and (self.timer.end + self.max_extending_timer - 1800) < starttime): if self.new_timer_copy is None: if self.activated_auto_increase: self.timer.autoincrease = False self.activated_auto_increase = False self.copyTimer(starttime, duration) self.timer.log(0, "[VPS] copied this timer, since the event may start later than this timer ends") elif not self.activated_auto_increase: self.activate_autoincrease() elif data[0] == "EVENT_OVER" or data[0] == "CANNOT_FIND_EVENT": self.max_extending_timer = 2*3600 if self.activated_auto_increase: self.timer.autoincrease = False self.activated_auto_increase = False elif data[0] == "PDC_MULTIPLE_FOUND_EVENT": self.check_and_add_event(int(data[1])) # Programm meldet, dass die EIT (present/following) des Senders offenbar # momentan fehlerhaft ist elif data[0] == "EIT_APPARENTLY_UNRELIABLE": if self.timer.vpsplugin_overwrite: self.timer.vpsplugin_overwrite = False self.timer.log(0, "[VPS] can't trust EPG currently, go to safe mode") def stop_recording(self): self.activated_auto_increase = False self.timer.autoincrease = False if self.timer.vpsplugin_overwrite: # Stopp nach margin_after seconds if config.plugins.vps.margin_after.value == 0: self.timer.abort() self.session.nav.RecordTimer.doActivate(self.timer) else: self.timer.end = int(time()) + config.plugins.vps.margin_after.value self.session.nav.RecordTimer.timeChanged(self.timer) self.stop_simulation() else: new_end_time = int(time()) + config.plugins.vps.margin_after.value if new_end_time > self.timer.end: self.timer.end = new_end_time self.session.nav.RecordTimer.timeChanged(self.timer) def activate_autoincrease(self): if not self.activated_auto_increase: self.activated_auto_increase = True self.timer.autoincrease = True self.timer.autoincreasetime = 60 if self.org_timer_end == 0: self.org_timer_end = self.timer.end self.timer.log(0, "[VPS] enable autoincrease") if self.new_timer_copy is not None and (self.new_timer_copy in self.session.nav.RecordTimer.timer_list): self.new_timer_copy.afterEvent = AFTEREVENT.NONE self.new_timer_copy.dontSave = True NavigationInstance.instance.RecordTimer.removeEntry(self.new_timer_copy) self.new_timer_copy = None self.timer.log(0, "[VPS] delete timer copy") # Noch ein Event aufnehmen? def set_next_event(self): if not self.timer.vpsplugin_overwrite and len(self.next_events) > 0: if not self.activated_auto_increase: self.activate_autoincrease() (starttime, neweventid) = self.next_events.pop(0) self.timer.eit = neweventid self.dont_restart_program = False self.program_abort() self.timer.log(0, "[VPS] record now event_id "+ str(neweventid)) vps_timers.checksoon(3000) return True else: return False def program_abort(self): if self.program_running or self.program_try_search_running: #self.program.sendCtrlC() self.program.kill() self.program_running = False self.program_try_search_running = False self.timer.log(0, "[VPS] stop monitoring") def stop_simulation(self): if self.simulate_recordService: NavigationInstance.instance.stopRecordService(self.simulate_recordService) self.simulate_recordService = None self.timer.log(0, "[VPS] stop RecordService (simulation)") def check_and_add_event(self, neweventid): if not config.plugins.vps.allow_seeking_multiple_pdc.value: return epgcache = eEPGCache.getInstance() evt = epgcache.lookupEventId(self.rec_ref, neweventid) if evt: evt_begin = evt.getBeginTime() + 60 evt_end = evt.getBeginTime() + evt.getDuration() - 60 if evt_begin < self.timer.begin: return for checktimer in self.session.nav.RecordTimer.timer_list: if checktimer == self.timer: continue if (checktimer.begin - evt_begin) > 3600*2: break compareString = checktimer.service_ref.ref.toCompareString() if compareString == self.timer.service_ref.ref.toCompareString() or compareString == self.rec_ref.toCompareString(): if checktimer.eit == neweventid: return if checktimer.begin <= evt_begin and checktimer.end >= evt_end: if checktimer.vpsplugin_enabled is None or not checktimer.vpsplugin_enabled: return # manuell angelegter Timer mit VPS if checktimer.name == "" and checktimer.vpsplugin_time is not None: checktimer.eit = neweventid checktimer.name = evt.getEventName() checktimer.description = evt.getShortDescription() checktimer.vpsplugin_time = None checktimer.log(0, "[VPS] changed timer (found same PDC-Time as in other VPS-recording)") return # eigenen Timer überprüfen, wenn Zeiten nicht überschrieben werden dürfen if not self.timer.vpsplugin_overwrite and evt_begin <= self.timer.end: check_already_existing = [x for (x,y) in self.next_events if y == neweventid] if len(check_already_existing) > 0: start = check_already_existing.pop() if start == evt_begin: return else: self.next_events.remove( (start, neweventid) ) self.timer.log(0, "[VPS] delete event_id "+ str(neweventid) +" because of delay "+ str(evt_begin - start)) self.next_events.append( (evt_begin, neweventid) ) self.next_events = sorted(self.next_events) self.timer.log(0, "[VPS] add event_id "+ str(neweventid)) else: newevent_data = parseEvent(evt) newEntry = RecordTimerEntry(ServiceReference(self.rec_ref), *newevent_data) newEntry.vpsplugin_enabled = True newEntry.vpsplugin_overwrite = True newEntry.dirname = self.timer.dirname newEntry.log(0, "[VPS] added this timer (found same PDC-Time as in other VPS-recording)") # Wenn kein Timer-Konflikt auftritt, wird der Timer angelegt. res = NavigationInstance.instance.RecordTimer.record(newEntry) self.timer.log(0, "[VPS] added another timer, res "+ str(res)) def copyTimer(self, start, duration): starttime = start - config.recording.margin_before.value * 60 endtime = start + duration + config.recording.margin_after.value * 60 self.new_timer_copy = RecordTimerEntry(ServiceReference(self.rec_ref), starttime, endtime, self.timer.name, self.timer.description, self.timer.eit, False, False, AFTEREVENT.AUTO, False, self.timer.dirname, self.timer.tags) self.new_timer_copy.vpsplugin_enabled = True self.new_timer_copy.vpsplugin_overwrite = self.timer.vpsplugin_overwrite self.new_timer_copy.log(0, "[VPS] added this timer") NavigationInstance.instance.RecordTimer.record(self.new_timer_copy) # startet den Hintergrundprozess def program_do_start(self, mode): if self.program_running or self.program_try_search_running: self.program_abort() if mode == 1: self.demux = -1 current_service = NavigationInstance.instance.getCurrentService() if current_service: stream = current_service.stream() if stream: streamdata = stream.getStreamingData() if (streamdata and ('demux' in streamdata)): self.demux = streamdata['demux'] if self.demux == -1: return; self.program_try_search_running = True self.program_running = False mode_program = 1 else: self.program_try_search_running = False self.program_running = True mode_program = 0 sid = self.rec_ref.getData(1) tsid = self.rec_ref.getData(2) onid = self.rec_ref.getData(3) demux = "/dev/dvb/adapter0/demux" + str(self.demux) # PDC-Zeit? if (self.timer.name == "" or self.timer.eit is None) and self.timer.vpsplugin_time is not None and not self.found_pdc: mode_program += 2 day = strftime("%d", localtime(self.timer.vpsplugin_time)) month = strftime("%m", localtime(self.timer.vpsplugin_time)) hour = strftime("%H", localtime(self.timer.vpsplugin_time)) minute = strftime("%M", localtime(self.timer.vpsplugin_time)) cmd = vps_exe + " "+ demux +" "+ str(mode_program) +" "+ str(onid) +" "+ str(tsid) +" "+ str(sid) +" 0 "+ day +" "+ month +" "+ hour +" "+ minute self.timer.log(0, "[VPS] seek PDC-Time") self.program.execute(cmd) return cmd = vps_exe + " "+ demux +" "+ str(mode_program) +" "+ str(onid) +" "+ str(tsid) +" "+ str(sid) +" "+ str(self.timer.eit) self.timer.log(0, "[VPS] start monitoring running-status") self.program.execute(cmd) def program_start(self): self.demux = -1 if self.dont_restart_program: return self.rec_ref = self.timer.service_ref and self.timer.service_ref.ref if self.rec_ref and self.rec_ref.flags & eServiceReference.isGroup: self.rec_ref = getBestPlayableServiceReference(self.rec_ref, eServiceReference()) # recordService (Simulation) ggf. starten if self.timer.state == TimerEntry.StateWaiting: if self.simulate_recordService is None: if self.rec_ref: self.simulate_recordService = NavigationInstance.instance.recordService(self.rec_ref, True) if self.simulate_recordService: res = self.simulate_recordService.start() self.timer.log(0, "[VPS] start recordService (simulation) " + str(res)) if res != 0 and res != -1: # Fehler aufgetreten (kein Tuner frei?) NavigationInstance.instance.stopRecordService(self.simulate_recordService) self.simulate_recordService = None # in einer Minute ggf. nochmal versuchen if 60 < self.nextExecution: self.nextExecution = 60 # Bei Overwrite versuchen ohne Fragen auf Sender zu schalten if self.timer.vpsplugin_overwrite == True: cur_ref = NavigationInstance.instance.getCurrentlyPlayingServiceReference() if cur_ref and not cur_ref.getPath() and self.rec_ref.toCompareString() != cur_ref.toCompareString(): self.timer.log(9, "[VPS-Plugin] zap without asking (simulation)") Notifications.AddNotification(MessageBox, _("In order to record a timer, the TV was switched to the recording service!\n"), type=MessageBox.TYPE_INFO, timeout=20) NavigationInstance.instance.playService(self.rec_ref) if 3 < self.nextExecution: self.nextExecution = 3 else: # ansonsten versuchen auf dem aktuellen Transponder/Kanal nach Infos zu suchen if not self.program_try_search_running: self.program_do_start(1) else: # Simulation hat geklappt if 1 < self.nextExecution: self.nextExecution = 1 else: # Simulation läuft schon # hole Demux stream = self.simulate_recordService.stream() if stream: streamdata = stream.getStreamingData() if (streamdata and ('demux' in streamdata)): self.demux = streamdata['demux'] if self.demux == -1: # ist noch nicht soweit(?), in einer Sekunde erneut versuchen if 1 < self.nextExecution: self.nextExecution = 1 else: self.program_do_start(0) elif self.timer.state == TimerEntry.StatePrepared or self.timer.state == TimerEntry.StateRunning: stream = self.timer.record_service.stream() if stream: streamdata = stream.getStreamingData() if (streamdata and ('demux' in streamdata)): self.demux = streamdata['demux'] if self.demux != -1: self.program_do_start(0) # überprüft, ob etwas zu tun ist und gibt die Sekunden zurück, bis die Funktion # spätestens wieder aufgerufen werden sollte # oder -1, um vps_timer löschen zu lassen def check(self): # Simulation ggf. stoppen if self.timer.state > TimerEntry.StateWaiting and self.simulate_recordService: self.stop_simulation() # VPS wurde wieder deaktiviert oder Timer wurde beendet if self.timer is None or self.timer.state == TimerEntry.StateEnded or self.timer.cancelled: self.program_abort() self.stop_simulation() return -1 if self.timer.vpsplugin_enabled == False or config.plugins.vps.enabled.value == False: if self.activated_auto_increase: self.timer.autoincrease = False self.program_abort() self.stop_simulation() return -1 self.nextExecution = 180 if config.plugins.vps.initial_time.value < 2 and self.timer.vpsplugin_overwrite: initial_time = 120 else: initial_time = config.plugins.vps.initial_time.value * 60 if self.timer.vpsplugin_overwrite == True: if self.timer.state == TimerEntry.StateWaiting or self.timer.state == TimerEntry.StatePrepared: # Startzeit verschieben if (self.timer.begin - 60) < time(): if self.org_timer_begin == 0: self.org_timer_begin = self.timer.begin elif (self.org_timer_begin + self.max_extending_timer) < time(): # Sendung begann immer noch nicht -> abbrechen self.timer.abort() self.session.nav.RecordTimer.doActivate(self.timer) self.program_abort() self.stop_simulation() self.timer.log(0, "[VPS] abort timer, waited enough to find Event-ID") return -1 self.timer.begin += 60 if (self.timer.end - self.timer.begin) < 300: self.timer.end += 180 # auf Timer-Konflikt prüfen timersanitycheck = TimerSanityCheck(self.session.nav.RecordTimer.timer_list, self.timer) if not timersanitycheck.check(): self.timer.abort() self.session.nav.RecordTimer.doActivate(self.timer) self.program_abort() self.stop_simulation() self.timer.log(0, "[VPS] abort timer due to TimerSanityCheck") return -1 self.session.nav.RecordTimer.timeChanged(self.timer) if 30 < self.nextExecution: self.nextExecution = 30 # Programm starten if not self.program_running: if self.timer.state == TimerEntry.StateRunning: self.program_start() elif initial_time > 0: if (self.timer.begin - initial_time) <= time(): self.program_start() else: n = self.timer.begin - initial_time - time() if n < self.nextExecution: self.nextExecution = n if self.timer.state == TimerEntry.StateRunning: if self.activated_auto_increase and self.org_timer_end != 0 and (self.org_timer_end + (4*3600)) < time(): # Aufnahme läuft seit 4 Stunden im Autoincrease -> abbrechen self.timer.autoincrease = False self.activated_auto_increase = False self.dont_restart_program = True self.program_abort() self.stop_simulation() self.timer.log(0, "[VPS] stop recording, too much autoincrease") return self.nextExecution
def __init__(self, session, serviceref, begin, end, name, description, eit, disabled = False, justplay = False, afterEvent = AFTEREVENT.AUTO, checkOldTimers = False, dirname = None, tags = None): self.session = session RecordTimerEntry.__init__(self, serviceref, begin, end, name, description, eit, disabled = False, justplay = False, afterEvent = AFTEREVENT.AUTO, checkOldTimers = False, dirname = None, tags = None)
class vps_timer: def __init__(self, timer, session): self.timer = timer self.session = session self.program = eConsoleAppContainer() self.program.dataAvail.append(self.program_dataAvail) self.program.appClosed.append(self.program_closed) self.program_running = False self.program_try_search_running = False self.activated_auto_increase = False self.simulate_recordService = None self.demux = -1 self.rec_ref = None self.found_pdc = False self.dont_restart_program = False self.org_timer_end = 0 self.org_timer_begin = 0 self.max_extending_timer = 4 * 3600 self.next_events = [] self.new_timer_copy = None self.pausing = False def program_closed(self, retval): self.timer.log(0, "[VPS] stop monitoring (process terminated)") if self.program_running or self.program_try_search_running: self.program_running = False self.program_try_search_running = False self.stop_simulation() def program_dataAvail(self, str): if self.timer is None or self.timer.state == TimerEntry.StateEnded or self.timer.cancelled: self.program_abort() self.stop_simulation() return if self.timer.vpsplugin_enabled == False or config.plugins.vps.enabled.value == False: if self.activated_auto_increase: self.timer.autoincrease = False self.program_abort() self.stop_simulation() return lines = str.split("\n") for line in lines: data = line.split() if len(data) == 0: continue self.timer.log(0, "[VPS] " + line) if data[0] == "RUNNING_STATUS": if data[1] == "0": # undefined if data[2] == "FOLLOWING": data[1] = "1" else: data[1] = "4" if data[1] == "1": # not running # Wenn der Eintrag im Following (Section_Number = 1) ist, # dann nicht beenden (Sendung begann noch gar nicht) if data[2] == "FOLLOWING": self.activate_autoincrease() elif self.timer.state == TimerEntry.StateRunning and not self.pausing and not self.set_next_event( ): self.stop_recording() self.dont_restart_program = True self.program_abort() elif data[1] == "2": # starts in a few seconds self.activate_autoincrease() if self.timer.state == TimerEntry.StateWaiting: self.session.nav.RecordTimer.doActivate(self.timer) elif data[1] == "3": # pausing self.pausing = True if self.timer.state == TimerEntry.StateRunning: self.activate_autoincrease() elif data[1] == "4": # running self.pausing = False if self.timer.state == TimerEntry.StateRunning: self.activate_autoincrease() elif self.timer.state == TimerEntry.StateWaiting or self.timer.state == TimerEntry.StatePrepared: # setze Startzeit auf jetzt self.timer.begin = int(time()) self.session.nav.RecordTimer.timeChanged(self.timer) self.activate_autoincrease() self.program_abort() self.stop_simulation() vps_timers.checksoon(2000) # Programm neu starten elif data[1] == "5": # service off-air self.timer.vpsplugin_overwrite = False if self.activated_auto_increase: self.timer.autoincrease = False self.activated_auto_increase = False elif data[0] == "EVENT_ENDED": if not self.set_next_event(): if self.timer.state == TimerEntry.StateRunning: self.stop_recording() # program terminates by itself #self.program_abort() self.stop_simulation() elif data[0] == "OTHER_TS_RUNNING_STATUS": if self.timer.state == TimerEntry.StateWaiting: self.timer.start_prepare = int(time()) self.session.nav.RecordTimer.doActivate(self.timer) # program terminates by itself #self.program_abort() self.stop_simulation() vps_timers.checksoon(2000) # PDC elif data[0] == "PDC_FOUND_EVENT_ID": self.found_pdc = True self.timer.eit = int(data[1]) epgcache = eEPGCache.getInstance() evt = epgcache.lookupEventId(self.rec_ref, self.timer.eit) if evt: self.timer.name = evt.getEventName() self.timer.description = evt.getShortDescription() # program terminates by itself #self.program_abort() vps_timers.checksoon(500) elif data[0] == "FOUND_EVENT_ON_SCHEDULE": starttime = int(data[1]) duration = int(data[2]) # Soll die Sendung laut EPG erst nach dem Ende dieses Timers beginnen? if (not self.timer.vpsplugin_overwrite and (self.timer.end + 300) < starttime) or ( self.timer.vpsplugin_overwrite and (self.timer.end + self.max_extending_timer - 1800) < starttime): if self.new_timer_copy is None: if self.activated_auto_increase: self.timer.autoincrease = False self.activated_auto_increase = False self.copyTimer(starttime, duration) self.timer.log( 0, "[VPS] copied this timer, since the event may start later than this timer ends" ) elif not self.activated_auto_increase: self.activate_autoincrease() elif data[0] == "EVENT_OVER" or data[0] == "CANNOT_FIND_EVENT": self.max_extending_timer = 2 * 3600 if self.activated_auto_increase: self.timer.autoincrease = False self.activated_auto_increase = False elif data[0] == "PDC_MULTIPLE_FOUND_EVENT": self.check_and_add_event(int(data[1])) # Programm meldet, dass die EIT (present/following) des Senders offenbar # momentan fehlerhaft ist elif data[0] == "EIT_APPARENTLY_UNRELIABLE": if self.timer.vpsplugin_overwrite: self.timer.vpsplugin_overwrite = False self.timer.log( 0, "[VPS] can't trust EPG currently, go to safe mode") def stop_recording(self): self.activated_auto_increase = False self.timer.autoincrease = False if self.timer.vpsplugin_overwrite: # Stopp nach margin_after seconds if config.plugins.vps.margin_after.value == 0: self.timer.abort() self.session.nav.RecordTimer.doActivate(self.timer) else: self.timer.end = int( time()) + config.plugins.vps.margin_after.value self.session.nav.RecordTimer.timeChanged(self.timer) self.stop_simulation() else: new_end_time = int(time()) + config.plugins.vps.margin_after.value if new_end_time > self.timer.end: self.timer.end = new_end_time self.session.nav.RecordTimer.timeChanged(self.timer) def activate_autoincrease(self): if not self.activated_auto_increase: self.activated_auto_increase = True self.timer.autoincrease = True self.timer.autoincreasetime = 60 if self.org_timer_end == 0: self.org_timer_end = self.timer.end self.timer.log(0, "[VPS] enable autoincrease") if self.new_timer_copy is not None and ( self.new_timer_copy in self.session.nav.RecordTimer.timer_list): self.new_timer_copy.afterEvent = AFTEREVENT.NONE self.new_timer_copy.dontSave = True NavigationInstance.instance.RecordTimer.removeEntry( self.new_timer_copy) self.new_timer_copy = None self.timer.log(0, "[VPS] delete timer copy") # Noch ein Event aufnehmen? def set_next_event(self): if not self.timer.vpsplugin_overwrite and len(self.next_events) > 0: if not self.activated_auto_increase: self.activate_autoincrease() (starttime, neweventid) = self.next_events.pop(0) self.timer.eit = neweventid self.dont_restart_program = False self.program_abort() self.timer.log(0, "[VPS] record now event_id " + str(neweventid)) vps_timers.checksoon(3000) return True else: return False def program_abort(self): if self.program_running or self.program_try_search_running: #self.program.sendCtrlC() self.program.kill() self.program_running = False self.program_try_search_running = False self.timer.log(0, "[VPS] stop monitoring") def stop_simulation(self): if self.simulate_recordService: NavigationInstance.instance.stopRecordService( self.simulate_recordService) self.simulate_recordService = None self.timer.log(0, "[VPS] stop RecordService (simulation)") def check_and_add_event(self, neweventid): if not config.plugins.vps.allow_seeking_multiple_pdc.value: return epgcache = eEPGCache.getInstance() evt = epgcache.lookupEventId(self.rec_ref, neweventid) if evt: evt_begin = evt.getBeginTime() + 60 evt_end = evt.getBeginTime() + evt.getDuration() - 60 if evt_begin < self.timer.begin: return for checktimer in self.session.nav.RecordTimer.timer_list: if checktimer == self.timer: continue if (checktimer.begin - evt_begin) > 3600 * 2: break compareString = checktimer.service_ref.ref.toCompareString() if compareString == self.timer.service_ref.ref.toCompareString( ) or compareString == self.rec_ref.toCompareString(): if checktimer.eit == neweventid: return if checktimer.begin <= evt_begin and checktimer.end >= evt_end: if checktimer.vpsplugin_enabled is None or not checktimer.vpsplugin_enabled: return # manuell angelegter Timer mit VPS if checktimer.name == "" and checktimer.vpsplugin_time is not None: checktimer.eit = neweventid checktimer.name = evt.getEventName() checktimer.description = evt.getShortDescription() checktimer.vpsplugin_time = None checktimer.log( 0, "[VPS] changed timer (found same PDC-Time as in other VPS-recording)" ) return # eigenen Timer überprüfen, wenn Zeiten nicht überschrieben werden dürfen if not self.timer.vpsplugin_overwrite and evt_begin <= self.timer.end: check_already_existing = [ x for (x, y) in self.next_events if y == neweventid ] if len(check_already_existing) > 0: start = check_already_existing.pop() if start == evt_begin: return else: self.next_events.remove((start, neweventid)) self.timer.log( 0, "[VPS] delete event_id " + str(neweventid) + " because of delay " + str(evt_begin - start)) self.next_events.append((evt_begin, neweventid)) self.next_events = sorted(self.next_events) self.timer.log(0, "[VPS] add event_id " + str(neweventid)) else: newevent_data = parseEvent(evt) newEntry = RecordTimerEntry(ServiceReference(self.rec_ref), *newevent_data) newEntry.vpsplugin_enabled = True newEntry.vpsplugin_overwrite = True newEntry.dirname = self.timer.dirname newEntry.log( 0, "[VPS] added this timer (found same PDC-Time as in other VPS-recording)" ) # Wenn kein Timer-Konflikt auftritt, wird der Timer angelegt. res = NavigationInstance.instance.RecordTimer.record(newEntry) self.timer.log(0, "[VPS] added another timer, res " + str(res)) def copyTimer(self, start, duration): starttime = start - config.recording.margin_before.value * 60 endtime = start + duration + config.recording.margin_after.value * 60 self.new_timer_copy = RecordTimerEntry( ServiceReference(self.rec_ref), starttime, endtime, self.timer.name, self.timer.description, self.timer.eit, False, False, AFTEREVENT.AUTO, False, self.timer.dirname, self.timer.tags) self.new_timer_copy.vpsplugin_enabled = True self.new_timer_copy.vpsplugin_overwrite = self.timer.vpsplugin_overwrite self.new_timer_copy.log(0, "[VPS] added this timer") NavigationInstance.instance.RecordTimer.record(self.new_timer_copy) # startet den Hintergrundprozess def program_do_start(self, mode): if self.program_running or self.program_try_search_running: self.program_abort() if mode == 1: self.demux = -1 current_service = NavigationInstance.instance.getCurrentService() if current_service: stream = current_service.stream() if stream: streamdata = stream.getStreamingData() if (streamdata and ('demux' in streamdata)): self.demux = streamdata['demux'] if self.demux == -1: return self.program_try_search_running = True self.program_running = False mode_program = 1 else: self.program_try_search_running = False self.program_running = True mode_program = 0 sid = self.rec_ref.getData(1) tsid = self.rec_ref.getData(2) onid = self.rec_ref.getData(3) demux = "/dev/dvb/adapter0/demux" + str(self.demux) # PDC-Zeit? if (self.timer.name == "" or self.timer.eit is None ) and self.timer.vpsplugin_time is not None and not self.found_pdc: mode_program += 2 day = strftime("%d", localtime(self.timer.vpsplugin_time)) month = strftime("%m", localtime(self.timer.vpsplugin_time)) hour = strftime("%H", localtime(self.timer.vpsplugin_time)) minute = strftime("%M", localtime(self.timer.vpsplugin_time)) cmd = vps_exe + " " + demux + " " + str(mode_program) + " " + str( onid) + " " + str(tsid) + " " + str( sid ) + " 0 " + day + " " + month + " " + hour + " " + minute self.timer.log(0, "[VPS] seek PDC-Time") self.program.execute(cmd) return cmd = vps_exe + " " + demux + " " + str(mode_program) + " " + str( onid) + " " + str(tsid) + " " + str(sid) + " " + str( self.timer.eit) self.timer.log(0, "[VPS] start monitoring running-status") self.program.execute(cmd) def program_start(self): self.demux = -1 if self.dont_restart_program: return self.rec_ref = self.timer.service_ref and self.timer.service_ref.ref if self.rec_ref and self.rec_ref.flags & eServiceReference.isGroup: self.rec_ref = getBestPlayableServiceReference( self.rec_ref, eServiceReference()) # recordService (Simulation) ggf. starten if self.timer.state == TimerEntry.StateWaiting: if self.simulate_recordService is None: if self.rec_ref: self.simulate_recordService = NavigationInstance.instance.recordService( self.rec_ref, True) if self.simulate_recordService: res = self.simulate_recordService.start() self.timer.log( 0, "[VPS] start recordService (simulation) " + str(res)) if res != 0 and res != -1: # Fehler aufgetreten (kein Tuner frei?) NavigationInstance.instance.stopRecordService( self.simulate_recordService) self.simulate_recordService = None # in einer Minute ggf. nochmal versuchen if 60 < self.nextExecution: self.nextExecution = 60 # Bei Overwrite versuchen ohne Fragen auf Sender zu schalten if self.timer.vpsplugin_overwrite == True: cur_ref = NavigationInstance.instance.getCurrentlyPlayingServiceReference( ) if cur_ref and not cur_ref.getPath( ) and self.rec_ref.toCompareString( ) != cur_ref.toCompareString(): self.timer.log( 9, "[VPS-Plugin] zap without asking (simulation)" ) Notifications.AddNotification( MessageBox, _("In order to record a timer, the TV was switched to the recording service!\n" ), type=MessageBox.TYPE_INFO, timeout=20) NavigationInstance.instance.playService( self.rec_ref) if 3 < self.nextExecution: self.nextExecution = 3 else: # ansonsten versuchen auf dem aktuellen Transponder/Kanal nach Infos zu suchen if not self.program_try_search_running: self.program_do_start(1) else: # Simulation hat geklappt if 1 < self.nextExecution: self.nextExecution = 1 else: # Simulation läuft schon # hole Demux stream = self.simulate_recordService.stream() if stream: streamdata = stream.getStreamingData() if (streamdata and ('demux' in streamdata)): self.demux = streamdata['demux'] if self.demux == -1: # ist noch nicht soweit(?), in einer Sekunde erneut versuchen if 1 < self.nextExecution: self.nextExecution = 1 else: self.program_do_start(0) elif self.timer.state == TimerEntry.StatePrepared or self.timer.state == TimerEntry.StateRunning: stream = self.timer.record_service.stream() if stream: streamdata = stream.getStreamingData() if (streamdata and ('demux' in streamdata)): self.demux = streamdata['demux'] if self.demux != -1: self.program_do_start(0) # überprüft, ob etwas zu tun ist und gibt die Sekunden zurück, bis die Funktion # spätestens wieder aufgerufen werden sollte # oder -1, um vps_timer löschen zu lassen def check(self): # Simulation ggf. stoppen if self.timer.state > TimerEntry.StateWaiting and self.simulate_recordService: self.stop_simulation() # VPS wurde wieder deaktiviert oder Timer wurde beendet if self.timer is None or self.timer.state == TimerEntry.StateEnded or self.timer.cancelled: self.program_abort() self.stop_simulation() return -1 if self.timer.vpsplugin_enabled == False or config.plugins.vps.enabled.value == False: if self.activated_auto_increase: self.timer.autoincrease = False self.program_abort() self.stop_simulation() return -1 self.nextExecution = 180 if config.plugins.vps.initial_time.value < 2 and self.timer.vpsplugin_overwrite: initial_time = 120 else: initial_time = config.plugins.vps.initial_time.value * 60 if self.timer.vpsplugin_overwrite == True: if self.timer.state == TimerEntry.StateWaiting or self.timer.state == TimerEntry.StatePrepared: # Startzeit verschieben if (self.timer.begin - 60) < time(): if self.org_timer_begin == 0: self.org_timer_begin = self.timer.begin elif (self.org_timer_begin + self.max_extending_timer) < time(): # Sendung begann immer noch nicht -> abbrechen self.timer.abort() self.session.nav.RecordTimer.doActivate(self.timer) self.program_abort() self.stop_simulation() self.timer.log( 0, "[VPS] abort timer, waited enough to find Event-ID" ) return -1 self.timer.begin += 60 if (self.timer.end - self.timer.begin) < 300: self.timer.end += 180 # auf Timer-Konflikt prüfen timersanitycheck = TimerSanityCheck( self.session.nav.RecordTimer.timer_list, self.timer) if not timersanitycheck.check(): self.timer.abort() self.session.nav.RecordTimer.doActivate(self.timer) self.program_abort() self.stop_simulation() self.timer.log( 0, "[VPS] abort timer due to TimerSanityCheck") return -1 self.session.nav.RecordTimer.timeChanged(self.timer) if 30 < self.nextExecution: self.nextExecution = 30 # Programm starten if not self.program_running: if self.timer.state == TimerEntry.StateRunning: self.program_start() elif initial_time > 0: if (self.timer.begin - initial_time) <= time(): self.program_start() else: n = self.timer.begin - initial_time - time() if n < self.nextExecution: self.nextExecution = n if self.timer.state == TimerEntry.StateRunning: if self.activated_auto_increase and self.org_timer_end != 0 and ( self.org_timer_end + (4 * 3600)) < time(): # Aufnahme läuft seit 4 Stunden im Autoincrease -> abbrechen self.timer.autoincrease = False self.activated_auto_increase = False self.dont_restart_program = True self.program_abort() self.stop_simulation() self.timer.log(0, "[VPS] stop recording, too much autoincrease") return self.nextExecution
def parseEPG(self, simulateOnly = False): if NavigationInstance.instance is None: print("[AutoTimer] Navigation is not available, can't parse EPG") return (0, 0, 0, [], [], []) total = 0 new = 0 modified = 0 timers = [] conflicting = [] similar = defaultdict(list) # Contains the the marked similar eits and the conflicting strings similars = [] # Contains the added similar timers # NOTE: the config option specifies "the next X days" which means today (== 1) + X delta = timedelta(days = config.plugins.autotimer.maxdaysinfuture.value + 1) evtLimit = mktime((date.today() + delta).timetuple()) checkEvtLimit = delta.days > 1 del delta # Read AutoTimer configuration self.readXml() # Get E2 instances epgcache = eEPGCache.getInstance() serviceHandler = eServiceCenter.getInstance() recordHandler = NavigationInstance.instance.RecordTimer # Save Recordings in a dict to speed things up a little # We include processed timers as we might search for duplicate descriptions # The recordict is always filled #Question: It might be better to name it timerdict #Question: Move to a separate function getTimerDict() #Note: It is also possible to use RecordTimer isInTimer(), but we won't get the timer itself on a match recorddict = defaultdict(list) for timer in chain(recordHandler.timer_list, recordHandler.processed_timers): if timer and timer.service_ref: if timer.eit is not None: event = epgcache.lookupEventId(timer.service_ref.ref, timer.eit) extdesc = event and event.getExtendedDescription() or '' timer.extdesc = extdesc elif not hasattr(timer, 'extdesc'): timer.extdesc = '' recorddict[str(timer.service_ref)].append(timer) # Create dict of all movies in all folders used by an autotimer to compare with recordings # The moviedict will be filled only if one AutoTimer is configured to avoid duplicate description for any recordings #Question: It might be better to name it recorddict moviedict = defaultdict(list) # Iterate Timer for timer in self.getEnabledTimerList(): # 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 if timer.encoding != 'UTF-8': try: match = match.decode('UTF-8').encode(timer.encoding) except UnicodeDecodeError: pass # Search EPG, default to empty list epgmatches = epgcache.search(('RITBDSE', 1000, typeMap[timer.searchType], match, caseMap[timer.searchCase])) or [] # Sort list of tuples by begin time 'B' epgmatches.sort(key=itemgetter(3)) # Reset the the marked similar servicerefs similar.clear() # Loop over all EPG matches for idx, ( serviceref, eit, name, begin, duration, shortdesc, extdesc ) in enumerate( epgmatches ): #Question: Do we need this? #Question: Move to separate function getRealService() 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 # 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 similar: 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 to 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) \ )) or 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 # Eventually change service to alternative if timer.overrideAlternatives: serviceref = timer.getAlternative(serviceref) total += 1 # Append to timerlist and abort if simulating timers.append((name, begin, end, serviceref, timer.name)) if simulateOnly: continue # Check for existing recordings in directory if timer.avoidDuplicateDescription == 3: # Reset movie Exists movieExists = False # Eventually create cache if dest and dest not in moviedict: #Question: Move to a separate function getRecordDict() movielist = serviceHandler.list(eServiceReference("2:0:1:0:0:0:0:0:0:0:" + dest)) if movielist is None: print("[AutoTimer] listing of movies in " + dest + " failed") else: append = moviedict[dest].append while 1: movieref = movielist.getNext() if not movieref.valid(): break if movieref.flags & eServiceReference.mustDescent: continue info = serviceHandler.info(movieref) if info is None: continue event = info.getEvent(movieref) if event is None: continue append({ "name": info.getName(movieref), "shortdesc": info.getInfoString(movieref, iServiceInformation.sDescription), "extdesc": event.getExtendedDescription() or '' # XXX: does event.getExtendedDescription() actually return None on no description or an empty string? }) del append for movieinfo in moviedict.get(dest, ()): if movieinfo.get("name") == name \ and movieinfo.get("shortdesc") == shortdesc: # Some channels indicate replays in the extended descriptions # If the similarity percent is higher then 0.8 it is a very close match extdescM = movieinfo.get("extdesc") if ( len(extdesc) == len(extdescM) and extdesc == extdescM ) \ or ( 0.8 < SequenceMatcher(lambda x: x == " ",extdesc, extdescM).ratio() ): print("[AutoTimer] We found a matching recorded movie, skipping event:", name) movieExists = True break if movieExists: continue # Initialize newEntry = None oldExists = False # 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 recorddict.get(serviceref, ()): if rtimer.eit == eit or config.plugins.autotimer.try_guessing.value and getTimeDiff(rtimer, evtBegin, evtEnd) > ((duration/10)*8): oldExists = True # Abort if we don't want to modify timers or timer is repeated if config.plugins.autotimer.refresh.value == "none" or rtimer.repeated: print("[AutoTimer] Won't modify existing timer because either no modification allowed or repeated timer") break if hasattr(rtimer, "isAutoTimer"): rtimer.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") break rtimer.log(501, "[AutoTimer] Warning, AutoTimer %s messed with a timer which might not belong to it." % (timer.name)) newEntry = rtimer modified += 1 # Modify values saved in timer newEntry.name = name newEntry.description = shortdesc newEntry.begin = int(begin) newEntry.end = int(end) newEntry.service_ref = ServiceReference(serviceref) break elif timer.avoidDuplicateDescription >= 1 \ and not rtimer.disabled \ and rtimer.name == name \ and rtimer.description == shortdesc: # Some channels indicate replays in the extended descriptions # If the similarity percent is higher then 0.8 it is a very close match if ( len(extdesc) == len(rtimer.extdesc) and extdesc == rtimer.extdesc ) \ or ( 0.8 < SequenceMatcher(lambda x: x == " ",extdesc, rtimer.extdesc).ratio() ): 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(recorddict) ): if not rtimer.disabled \ and rtimer.name == name \ and rtimer.description == shortdesc: # Some channels indicate replays in the extended descriptions # If the similarity percent is higher then 0.8 it is a very close match if ( len(extdesc) == len(rtimer.extdesc) and extdesc == rtimer.extdesc ) \ or ( 0.8 < SequenceMatcher(lambda x: x == " ",extdesc, rtimer.extdesc).ratio() ): 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 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 = timer.destination newEntry.justplay = timer.justplay newEntry.tags = timer.tags newEntry.vpsplugin_enabled = timer.vps_enabled newEntry.vpsplugin_overwrite = timer.vps_overwrite 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 = similar[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: 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 conflicts and 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 shortdesc == shortdescS: # Some channels indicate replays in the extended descriptions # If the similarity percent is higher then 0.8 it is a very close match if ( len(extdesc) == len(extdescS) and extdesc == extdescS ) \ or ( 0.8 < SequenceMatcher(lambda x: x == " ",extdesc, extdescS).ratio() ): # Check if the similar is already known if eitS not in similar: print("[AutoTimer] Found similar Timer: " + name) # Store the actual and similar eit and conflictString, so it can be handled later newEntry.conflictString = conflictString similar[eit] = newEntry similar[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 break else: similarTimer = False newEntry = similar[eitS] break if conflicts is None: timer.decrementCounter() new += 1 newEntry.extdesc = extdesc recorddict[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)) similar.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) return (total, new, modified, timers, conflicting, similars)