class WebRecorder(): ''' API methods are called by the RecorderPlugin in the RecorderWebService module. ''' # signal modes # TODO change mode to STATUS und ERROR... MSG_STATUS = MessageListener.MSG_STATUS # MSG_EPG = MessageListener.MSG_EPG MSG_REFRESH = MessageListener.MSG_REFRESH # Modes or types of rec? (MODE_DATA, MODE_REC, MODE_BLOCK) = range(0xA0, 0xA3) (TYPE_HEAD, TYPE_PROG, TYPE_INFO) = range(3) def __init__(self): ''' starts the app ''' self.configuration = Config() self.configuration.setupLogging("webdvb.log") self._lastMessage = None ml = MessageListener(); ml.addListener(ml.MSG_STATUS, self.storeLastMessage) # ml.addListener(ml.MSG_EPG, this.storeLastMessage) ml.addListener(ml.MSG_REFRESH, self.storeLastMessage) channelReader = ChannelReader() cPath = self.configuration.getChannelFilePath() channelReader.readChannels(cPath) self.channelList = channelReader.getChannels() self.progProvider = EPGProgramProvider(self, self.channelList, self.configuration) self._lastEpgRead = 0.0 # self._readCachedEpgData() self.checkEPGData() def _readCachedEpgData(self): ml = MessageListener(); if not self.channelList: ml.signalMessage(self.MSG_STATUS, "Where is that channel.conf? RTF!") return ml.signalMessage(self.MSG_STATUS, "Reading programm info") msg = "Idle" try: self.progProvider.readEPGCache() ml.signalMessage(self.MSG_REFRESH, "Program info read") # enforces a new list except IOError: msg = "No EPG data" except Exception, ex: msg = "Error reading cached EPG Data: " + str(ex.args[0]) self.configuration.getLogger().exception(msg) print msg self.configuration.logInfo(msg) ml.signalMessage(self.MSG_STATUS, msg)
class WebRecorder(): ''' API methods are called by the RecorderPlugin in the RecorderWebService module. ''' # signal modes # TODO change mode to STATUS und ERROR... MSG_STATUS = MessageListener.MSG_STATUS # MSG_EPG = MessageListener.MSG_EPG MSG_REFRESH = MessageListener.MSG_REFRESH # Modes or types of rec? (MODE_DATA, MODE_REC, MODE_BLOCK) = list(range(0xA0, 0xA3)) (TYPE_HEAD, TYPE_PROG, TYPE_INFO) = list(range(3)) def __init__(self): ''' starts the app ''' self.configuration = Config() self.configuration.setupLogging("webdvb.log") self._lastMessage = None ml = MessageListener() ml.addListener(ml.MSG_STATUS, self.storeLastMessage) # ml.addListener(ml.MSG_EPG, this.storeLastMessage) ml.addListener(ml.MSG_REFRESH, self.storeLastMessage) channelReader = ChannelReader() cPath = self.configuration.getChannelFilePath() channelReader.readChannels(cPath) self.channelList = channelReader.getChannels() self.progProvider = EPGProgramProvider(self, self.channelList, self.configuration) self._lastEpgRead = 0.0 # self._readCachedEpgData() self.checkEPGData() def _readCachedEpgData(self): ml = MessageListener() if not self.channelList: ml.signalMessage(self.MSG_STATUS, "Where is that channel.conf? RTF!") return ml.signalMessage(self.MSG_STATUS, "Reading programm info") msg = "Idle" try: self.progProvider.readEPGCache() ml.signalMessage(self.MSG_REFRESH, "Program info read") # enforces a new list except IOError: msg = "No EPG data" except Exception as ex: msg = "Error reading cached EPG Data: " + str(ex.args[0]) self.configuration.getLogger().exception(msg) print(msg) self.configuration.logInfo(msg) ml.signalMessage(self.MSG_STATUS, msg) def _getEPGDevice(self): return DVBDevice.getGrabber(self.channelList, self.configuration) def _collectEPGFromDevice(self): epgUpdater = self.progProvider.getEPGUpdater() self._updater.updateDatabase() if epgUpdater.hasError(): MessageListener().signalMessage(self.MSG_STATUS, epgUpdater.getErrorMessage()) ''' API to Recorder plugin Basic idea: should be json objects ''' def storeLastMessage(self, message): self._lastMessage = message def getLastSignal(self): msg = self._lastMessage self._lastMessage = None return msg def readEPGDeviceData(self): self._collectEPGFromDevice() def getChannelList(self): channelNames = [] for channel in self.channelList: channelNames.append(channel.getName()) jchannels = json.dumps(channelNames) return jchannels def getProgrammInfoForChannel(self, aChannelString): daytoDayList = self.progProvider.getInfosForChannel(aChannelString) jDayToDayArray = self._formatProgramList(daytoDayList) jInfos = json.dumps(jDayToDayArray) return jInfos def toggleRecordMode(self, jsonString): jsonDict = json.loads(jsonString) epgInfo = self._lookupEPGInfoFromJSON(jsonDict) forceRemove = jsonDict["type"] == self.TYPE_INFO if epgInfo is not None: self.progProvider.toggleRecordInfo(epgInfo, forceRemove) result = self._formatProgramRow(epgInfo) else: jsonDict["error"] = "Entry not found" result = jsonDict if self._lastMessage is not None: result["error"] = self.getLastSignal() return json.dumps(result) def getFilterList(self, searchTuple): channelName = searchTuple[0] filterString = searchTuple[1] epgInfoList = self.progProvider.searchInChannel( channelName, filterString) jDayToDayArray = self._formatProgramList(epgInfoList) return json.dumps(jDayToDayArray) def getAutoSelectList(self): autoselectList = self.progProvider.getAutoSelector( ).getAutoSelectionList() jList = self._formatAutoSelectList(autoselectList) return json.dumps(jList) def addToAutoSelection(self, jsonString): jsonDict = json.loads(jsonString) epgInfo = self._lookupEPGInfoFromJSON(jsonDict) autoSelector = self.progProvider.getAutoSelector() autoSelector.addAutoSelectPreference(epgInfo) autoSelector.saveAutoSelectData() def saveAutoSelectionSetting(self, jsonString): # weekmode has changed. single entry. update & save jsonDict = json.loads(jsonString) hourString = jsonDict["timetext"] # Text is unicode!! titleString = jsonDict["text"] channelName = jsonDict["chanID"] weekModeString = jsonDict["weekMode"] autoSelector = self.progProvider.getAutoSelector() autoSelector.updateWeekMode(hourString, titleString, channelName, weekModeString) autoSelector.saveAutoSelectData() def removeFromAutoSelection(self, jsonString): # NOTE: json makes unicode out of the string jsonDict = json.loads(jsonString) hourString = jsonDict["timetext"] titleString = jsonDict["text"] channelName = jsonDict["chanID"] autoSelector = self.progProvider.getAutoSelector() autoSelector.removeFromAutoSelectPreference(hourString, str(titleString), str(channelName)) autoSelector.saveAutoSelectData() def getRecordingList(self): recInfoList = self.progProvider.getRecordQueue().getRecList() jList = self._formatRecordingList(recInfoList) return json.dumps(jList) #### dict: {u'marginStop': 600, u'marginStart': 300, u'jobID': u'1'} def updateRecorderMargins(self, jsonString): jsonList = json.loads(jsonString) recInfoList = self.progProvider.getRecordQueue().getRecList() for jEntry in jsonList: jobID = jEntry["jobID"] found = next((recEntry for recEntry in recInfoList if recEntry.getEPGInfo().getJobID() == jobID), None) if found: found.setMarginStart(jEntry["marginStart"]) found.setMarginStop(jEntry["marginStop"]) self.progProvider.getRecordQueue()._storeRecordQueue(recInfoList) ''' search the database for a string "cmd":"SEARCH_ALL","arg":"test","data":""} ''' def searchAll(self, searchString): epgInfoList = self.progProvider.searchAll(searchString) #jDayToDayArray = self._formatProgramList(epgInfoList) #return json.dumps(jDayToDayArray) #maybe sort them per channel? jDayList = [] for epgInfo in epgInfoList: jInfo = self._formatProgramRow(epgInfo) jDayList.append(jInfo) errorMsg = None if len(jDayList) == 0: errorMsg = "Nothing found" jAnswer = {"type": self.TYPE_INFO, "error": errorMsg, "list": jDayList} return json.dumps(jAnswer) ''' reread epg info if a modification took place.If changes took place (like a daemon epg update) update the database ''' def checkEPGData(self): fileName = self.configuration.getCachedXMLTVFilePath() currentModificationtime = 0 try: currentModificationtime = OSTools.getLastModificationTime(fileName) except OSError as osError: msg = "CheckEpgData:" + osError.strerror self.configuration.logError(msg) self.storeLastMessage(msg) return # if self._lastEpgRead is None: # self._lastEpgRead= currentModificationtime-100 if currentModificationtime - self._lastEpgRead > 60: self._lastEpgRead = currentModificationtime self._readCachedEpgData() ''' End of WebRecorder API -- Helper/conversion methods -- aka WebViewGenerator? ''' def _formatHeader(self, epgInfo): header = epgInfo.getStartTime().strftime("%A %d %B") headerText = "<b>%s</b>" % header return { "type": self.TYPE_HEAD, "text": headerText, "time": None } def _formatProgramRow(self, epgInfo): epgTime = epgInfo.getStartTimeString() epgDate = epgInfo.getDateString() title = epgInfo.getTitle() duration = str(epgInfo.getDuration()) description = epgInfo.getDescription() # TODO: Formating is client work- only the data! programText = "<b>%s</b><br>%s<small><i> Duration: %s</i></small>" % ( title, description, duration) jobID = None if epgInfo.isMarkedForRecord(): recmode = self.MODE_REC jobID = epgInfo.getJobID() elif epgInfo.isBlockedForRecord(): recmode = self.MODE_BLOCK else: recmode = self.MODE_DATA return { "type": self.TYPE_PROG, "text": programText, "time": epgTime, "date": epgDate, "recordMode": recmode, "jobID": jobID, "title": title, "channel": epgInfo.getChannel().getName(), "epgOK": epgInfo.isConsistent, "error": None } def _formatProgramList(self, daytoDayList): jDayToDayArray = [] for singleDayList in daytoDayList: jDayList = [] # adds the header - setting the date only # TODO: Empty singleDayList! Should not happen if len(singleDayList) == 0: print("ERROR: Empty single day list") continue headerText = self._formatHeader(singleDayList[0]) for epgInfo in singleDayList: jInfo = self._formatProgramRow(epgInfo) jDayList.append(jInfo) jDayObject = { "head": headerText, "list": jDayList } jDayToDayArray.append(jDayObject) if len(jDayToDayArray) == 0: return self.asJsonError("No data", self.getLastSignal()) return jDayToDayArray def asJsonError(self, errorMsg, argumentString): reason = argumentString if not reason: reason = errorMsg return {"type": self.TYPE_INFO, "error": errorMsg, "args": reason} def _lookupEPGInfoFromJSON(self, jsonData): aChannelString = jsonData["channel"] dayString = jsonData["date"] timeString = jsonData["time"] # Note JSON data always uses unicode - convert it to byte encoding it to uf8 daytoDayList = self.progProvider.getInfosForChannel(aChannelString) # Well... get the right DAY first.... for singleDayList in daytoDayList: if singleDayList[0].getDateString() in dayString: for epgInfo in singleDayList: if epgInfo.getStartTimeString() in timeString: return epgInfo return None def _formatAutoSelectList(self, autoSelectList): jDayList = [] for autoSelection in autoSelectList: timeString = autoSelection.getHourListString() title = autoSelection.getTitle() chanID = autoSelection.getChannelID() weekMode = autoSelection.getWeekMode() jInfo = { "type": self.TYPE_INFO, "timetext": timeString, "text": title, "title": title, "chanID": chanID, "weekMode": weekMode, "error": None } jDayList.append(jInfo) jsonASList = { "weekTypes": ["Mo-Fri", "Sa-Su", "Mo-Su"], "elements": jDayList } return jsonASList def _formatRecordingList(self, recInfoList): jDayList = [] for recInfo in recInfoList: jInfo = self._formatRecordingRow(recInfo) jDayList.append(jInfo) return jDayList def _formatRecordingRow(self, recInfo): epgInfo = recInfo.getEPGInfo() epgTime = epgInfo.getStartTimeString() epgDate = epgInfo.getDateString() theDay = epgInfo.getStartTime().strftime("%a %d") start = epgInfo.getStartTime().strftime("%H:%M-") end = epgInfo.getEndTime().strftime("%H:%M") channel = epgInfo.getChannel().getName() formatTime = "<b>%s</b><i> %s</i><br><small>%s%s</small>" % ( channel, theDay, start, end) title = epgInfo.getTitle() description = epgInfo.getDescription() jobID = epgInfo.getJobID() marginStart = recInfo.getMarginAsString(recInfo.marginStart) marginStop = recInfo.getMarginAsString(recInfo.marginEnd) if len(jobID) > 0: programText = "<b>%s</b><br>%s <i>(%s)</i>" % (title, description, jobID) else: programText = "<b>%s</b><br>%s" % (title, description) return { "type": self.TYPE_INFO, "timetext": formatTime, "text": programText, "time": epgTime, "date": epgDate, "jobID": jobID, "title": title, "channel": channel, "marginStart": marginStart, "marginStop": marginStop, "error": None }
class DVBRecorder(): ''' startup main class ''' def __init__(self): ''' starts the app ''' self.configuration = Config() self.configuration.setupLogging("dvb.log") channelReader = ChannelReader() cPath = self.configuration.getChannelFilePath() channelReader.readChannels(cPath) self.channelList = channelReader.getChannels() self.progProvider = EPGProgramProvider(self, self.channelList, self.configuration) recView = RecorderView(self.progProvider) ml = MessageListener() ml.addListener(ml.MSG_STATUS, recView.setStatusLine) ml.addListener(ml.MSG_EPG, recView.notifyEPGStatus) ml.addListener(ml.MSG_REFRESH, recView.refreshProgrammInfos) t1 = ReaderThread(False, self._readCachedEpgData) t1.start() recView.openView() #returns on close t1.join() def _readCachedEpgData(self): ml = MessageListener() if not self.channelList: ml.signalMessage(ml.MSG_STATUS, "Where is that channel.conf? RTF!") return ml.signalMessage(ml.MSG_STATUS, "Reading programm info") msg = "Idle" try: self.progProvider.readEPGCache() ml.signalMessage(ml.MSG_REFRESH) # enforces a new list except IOError: msg = "No EPG data" except Exception as ex: msg = "Error reading cached EPG Data: " + str(ex.args[0]) self.configuration.logInfo(msg) ml.signalMessage(ml.MSG_STATUS, msg) ##callback for the epg program provider: def readEPGDeviceData(self): self._EPGThread = ReaderThread(False, self._collectEPGFromDevice) #TEST code: self._EPGThread = ReaderThread(False,self._readEPGSimData) self._EPGThread.start() def _collectEPGFromDevice(self): ml = MessageListener() ml.signalMessage(ml.MSG_EPG, True) ml.signalMessage(ml.MSG_STATUS, "Retrieving new EPG data") epgUpdater = self.progProvider.getEPGUpdater() epgUpdater.updateDatabase() if epgUpdater.hasError(): ml.signalMessage(ml.MSG_STATUS, epgUpdater.getErrorMessage()) ml.signalMessage(ml.MSG_EPG, False) return ml.signalMessage(ml.MSG_REFRESH) # enforces redraw! ml.signalMessage(ml.MSG_STATUS, "EPG data up to date !") ml.signalMessage(ml.MSG_EPG, False) # def _readEPGSimData(self): # rawEPGPath = "/home/matze/JWSP/py1/VideoRecorder/src/xmltv/rawepg.xmltv" # with open(rawEPGPath, 'r') as aFile: # dvbList=aFile.readlines() # # epgReader=EpgReader(self.channelList) # for xmls in dvbList: # print "parse xml" # epgReader.parseXML_TVString(xmls,UTC=True) # # infoDictionary = epgReader.getInfoDictionary() # self.progProvider.updateProgramms(infoDictionary) # ml = MessageListener(); # ml.signalMessage(ml.MODE_REFRESH)# enforces redraw! # ml.signalMessage(ml.MODE_STATUS,"EPG data up to date !") # ml.signalMessage(ml.MODE_EPG,False) def _getEPGDevice(self): return DVBDevice.getGrabber(self.channelList, self.configuration) def persistEPGData(self): self.configuration.logInfo("Saving data...") epgUpdater = self.progProvider.getEPGUpdater() self.progProvider.getAutoSelector().saveAutoSelectData() epgUpdater.persistDatabase() if epgUpdater.hasError(): self.configuration.logError("Error saving xml data")
class RecorderDaemon(): LOG_FILE = "dvb_suspend.log" HEARTBEAT = 60 DAY_IN_SECONDS = 60*60*24 EPG_UPDATE_INTERVAL = DAY_IN_SECONDS+10 EPG_TS_Template = '%Y%m%d%H%M%S' def __init__(self,): self._config = Config() self._setUpLogging() self.epgUpdater = EpgUpdater(self._config) OSTools.ensureDirectory(self._config.getRecordingPath(),'') self._inhibitor = OSTools.Inhibitor() self._recordCmd = DVBDevice.getRecordCommander() self._lastJobId="0" self._recordPartIndex=0 self.isActive=True self._daemonPolicy = None def _getNextJob(self): return self.epgUpdater.getRecordQueue().getNextRecordingEntry() def _updateEPGData(self): days = self._getDaysSinceLastEPGUpdate() if days == 0: self._log("EPG is up to date") return False self._log("Last EPG update %s day(s) ago - Updating now.."%str(days)) self.epgUpdater.updateDatabase() if self.epgUpdater.hasError(): self._log("EPG Update failed: "+self.epgUpdater.getErrorMessage()) return False self.epgUpdater.persistDatabase() currentDate= datetime.now().strftime(self.EPG_TS_Template) path = self._config.getEPGTimestampPath() with open(path,'w') as f: f.write(currentDate) return True ''' Enforce a epg data read ''' def readEPGData(self): path = self._config.getEPGTimestampPath() if OSTools.fileExists(path): OSTools.removeFile(path) self._updateEPGData() def _getDaysSinceLastEPGUpdate(self): path = self._config.getEPGTimestampPath() if OSTools.fileExists(path): with open(path, 'r') as aFile: currentDate=aFile.read() try: checkDate = datetime.strptime(currentDate,self.EPG_TS_Template) delta = datetime.now()-checkDate return delta.days except ValueError: self._log("Error reading EPG Timestamp:"+str(currentDate)) return -1 def _isTimeLeftForEPGUpdate(self,nextStartTime): now = datetime.now() seconds = OSTools.getDifferenceInSeconds(nextStartTime, now) return seconds > self.HEARTBEAT*10 def _secondsUntilNextRun(self,startTime,prerunSeconds): now = datetime.now() secondsUntilStart = OSTools.getDifferenceInSeconds(startTime, now) secondsToSleep = secondsUntilStart - prerunSeconds #wake up a minute+ before if secondsToSleep < prerunSeconds: self._log("Next run in less than %s seconds"%(str(secondsUntilStart))) #sleep until time is ready.... no hibernate if secondsUntilStart > 0: self._log("Waiting to launch...."+str(secondsUntilStart)) xtime.sleep(secondsUntilStart) return 0 return secondsToSleep def launchRecording(self,recInfo): #As opposed to a previous version no AT queue is used. Reason: recording needs to be supervised #anyhow, so there is need for a supervising/scheduling process channel = recInfo.getEPGInfo().getChannel() OSTools.ensureDirectory(self._config.getRecordingPath(),channel.getEscapedName()) jobID= recInfo.getEPGInfo().getJobID() self._syncRecordIndex(jobID) scheduler= Recorder(self._config,self._recordCmd,self._recordPartIndex) return scheduler.scheduleRecording(recInfo) def _stopQueue(self): self.isActive=False def _exit(self): self._inhibitor.inhibit_gnome_screensaver(False) self._stopQueue() self._log("exit daemon") logging.shutdown() def _setUpLogging(self): path = self.LOG_FILE self._config.setupLogging(path) def _log(self,aString): self._getLogger().log(logging.INFO,aString) print(aString) def _getLogger(self): return logging.getLogger('dvb_scheduler') def _hasRecordingProcess(self): pidInfo = OSTools.getProcessPID(self._recordCmd.Command) if pidInfo is None: xtime.sleep(2) # in case of adjacent films pidInfo = OSTools.getProcessPID(self._recordCmd.Command) return pidInfo is not None ''' If that recInfo has been killed previously due to a bad reception increment the recordIndex which will be used for a unique title for a retry (therefore not overwriting already existing recordings) ''' def _syncRecordIndex(self,jobID): if self._lastJobId == jobID and int(jobID) > 1: self._recordPartIndex=self._recordPartIndex+1 else: self._recordPartIndex=0 self._lastJobId = jobID def _monitorCurrentRecording(self,recProcess,recordingJob): done = False #this is testing: emergencyCount=0; jobID=recordingJob.getEPGInfo().getJobID() isRecurrentWriteError=False recPath = self._config.getRecordingPath() videoSize=OSTools.getDirectorySize(recPath) xtime.sleep(self.HEARTBEAT) self._log("Monitoring JOB "+jobID) while not done: result = recProcess.poll() isAlive = result is None if isAlive: currentSize = OSTools.getDirectorySize(recPath) delta = currentSize - videoSize print(("JOB "+jobID+" - bytes written:"+str(delta))) #live sign- not logging videoSize = currentSize if delta == 0: self._log("JOB "+jobID+" does not write any data") if isRecurrentWriteError: done=True self._log("Terminating Rec process, preventing reschedule.. ") recProcess.terminate() self.__handleProcessTermination(recProcess) isRecurrentWriteError=True #only on retry permitted else: self._log("Quit JOB "+jobID) self.__handleProcessTermination(recProcess) done=True if not done: #Ensure that an adjacent job can follow - decrease the wait time gludu=recordingJob.getGluedDurance() endtime = OSTools.addToDateTime(recordingJob.getExecutionTime(),gludu) delta = max(10,OSTools.getDifferenceInSeconds(endtime,datetime.now())) sleepTime = min(self.HEARTBEAT,delta) if sleepTime != self.HEARTBEAT: emergencyCount+=1; endTimeString=endtime.strftime("%H:%M.%S") startTimeString = recordingJob.getExecutionTime().strftime("%H:%M.%S") self._log("Stopping in seconds: %d (Info Start: %s expected end: %s with dur %d)"%(sleepTime,startTimeString,endTimeString,gludu))#log only the fragments if emergencyCount > 10: self._log("REC Q error- force process termination") recProcess.terminate() self.__handleProcessTermination(recProcess) done=True xtime.sleep(sleepTime) self._log("JOB "+jobID+" is done") OSTools.syncFiles() def __handleProcessTermination(self,recProcess): result=recProcess.communicate() potentialErrorMessage = result[1].decode('utf8').strip() msg =">>"+result[0].decode('utf8').strip()+" status:"+potentialErrorMessage #TODO: check for errors ggf boolean back - cancel recording if necessary if "ERROR" in potentialErrorMessage: print("TODO Unhandled {c,t}ZAP error!") self._log(msg) def _initializePolicy(self): if Config.DAEMON_POLICY == Config.POLICY_SERVER: self._setServerPolicy() else: self._setVCRPolicy() def _setServerPolicy(self): self._daemonPolicy = ServerPolicy(self) def _setVCRPolicy(self): self._daemonPolicy = VCRPolicy(self) def _isPolicyChangeRequested(self,markerFile): if OSTools.fileExists(markerFile): OSTools.removeFile(markerFile) return True return False ''' if a file is present with the name of "SaveEnergy" remove it and switch to VCR mode ''' def isVCRPolicyChangeRequested(self): markerFile= self._config.getEnergySaverFileMarker() if self._isPolicyChangeRequested(markerFile): self._setVCRPolicy() return True return False def isServerPolicyChangeRequested(self): markerFile= self._config.getServerFileMarker() if self._isPolicyChangeRequested(markerFile): self._setServerPolicy() return True return False def _startDaemon(self): errorCount =0; while self.isActive and errorCount < 10: try: #Always a job, possibly a maintenance for epg update nextJob =self._getNextJob() startTime = nextJob.getExecutionTime() if self._isTimeLeftForEPGUpdate(startTime): if self._updateEPGData(): continue #maybe a fresh job came in... secondsToSleep = self._secondsUntilNextRun(startTime,self._daemonPolicy.PRERUN_SECONDS) if self._daemonPolicy.isReadyToRecord(startTime,secondsToSleep): if nextJob.getEPGInfo() is None: self._log("Maintenance mode- will update EPG") continue; process = self.launchRecording(nextJob) self._monitorCurrentRecording(process,nextJob) except KeyboardInterrupt: print('^C received, shutting down daemon') self._exit() except Exception as ex: msg= "Error running SuspensionDaemon: "+str(ex.args[0]) self._getLogger().exception(msg) print(msg) errorCount = errorCount + 1 def run(self): msg= self.epgUpdater.getErrorMessage() if msg: print("EPG Error:",msg) else: self._log("Starting daemon..") self._inhibitor.inhibit_gnome_screensaver(True) self.epgUpdater.readEPGCache() self._initializePolicy() self._startDaemon()