class MythTVScreen(Screen): """Main screen class for MythTV schedule. Screen attempts to connect to MythTV backend and retrieve list of upcoming recordings and display this. Data is cached so that information can still be viewed even if backend is offline (e.g. for power saving purposes). """ backendonline = BooleanProperty(False) isrecording = BooleanProperty(False) def __init__(self, **kwargs): super(MythTVScreen, self).__init__(**kwargs) # Get the path for the folder scr = sys.modules[self.__class__.__module__].__file__ # Create variable to retain path to our cache fie self.screendir = os.path.dirname(scr) self.cacheFile = os.path.join(self.screendir, "cache", "cache.json") # Some other useful variable self.running = False self.rec_timer = None self.status_timer = None self.be = None self.recs = None def on_enter(self): # We only update when we enter the screen. No need for regular updates. self.getRecordings() self.drawScreen() self.checkRecordingStatus() def on_leave(self): pass def cacheRecs(self, recs): """Method to save local copy of recordings. Backend may not be online all the time so a cache enables us to display recordings if if we can't poll the server for an update. """ with open(self.cacheFile, 'w') as outfile: json.dump(recs, outfile) def loadCache(self): """Retrieves cached recorings and returns as a python list object.""" try: raw = open(self.cacheFile, 'r') recs = json.load(raw) except: recs = [] return recs def recs_to_dict(self, uprecs): """Converts the MythTV upcoming recording iterator into a list of dict objects. """ raw_recs = [] recs = [] # Turn the response into a dict object and add to our list of recorings for r in uprecs: rec = {} st = r.starttime et = r.endtime rec["title"] = r.title rec["subtitle"] = r.subtitle if r.subtitle else "" day = dt.datetime(st.year, st.month, st.day) rec["day"] = (day - EPOCH).total_seconds() rec["time"] = "{} - {}".format(st.strftime("%H:%M"), et.strftime("%H:%M")) rec["timestamp"] = (st - EPOCH).total_seconds() rec["desc"] = r.description raw_recs.append(rec) # Group the recordings by day (so we can print a header) for k, g in groupby(raw_recs, lambda x: x["day"]): recs.append((k, list(g))) return recs def getRecordings(self): """Attempts to connect to MythTV backend and retrieve recordings.""" try: # If we can connect then get recordings and save a local cache. self.be = MythBE() uprecs = self.be.getUpcomingRecordings() self.recs = self.recs_to_dict(uprecs) self.cacheRecs(self.recs) self.backendonline = True except: # Can't connect so we need to set variables accordinly and try # to load data from the cache. self.be = None self.recs = self.loadCache() self.backendonline = False def checkRecordingStatus(self): """Checks whether the backend is currently recording.""" try: recbe = MythBE() for recorder in recbe.getRecorderList(): if recbe.isRecording(recorder): self.isrecording = True break except: # If we can't connect to it then it can't be recording. self.isrecording = False def drawScreen(self): """Main method for rendering screen. If there is recording data (live or cached) then is laid out in a scroll view. If not, the user is notified that the backend is unreachable. """ sv = self.ids.myth_scroll sv.clear_widgets() if self.recs: # Create a child widget to hold the recordings. self.sl = GridLayout(cols=1, size_hint=(1, None), spacing=2) self.sl.bind(minimum_height=self.sl.setter('height')) # Loop over the list of recordings. for rec in self.recs: # These are grouped by day so we need a header day = dt.timedelta(0, rec[0]) + EPOCH mrh = MythRecordingHeader(rec_date=day.strftime("%A %d %B")) self.sl.add_widget(mrh) # Then we loop over the recordings scheduled for that day for r in rec[1]: # and add them to the display. mr = MythRecording(rec=r) self.sl.add_widget(mr) sv.add_widget(self.sl) else: lb = Label(text="Backend is unreachable and there is no cached" " information") sv.add_widget(lb)
class myScreen(PiInfoScreen): refreshtime = 60 displaytime = 10 pluginname = "MythTV" plugininfo = "Displays details of upcoming recordings" supportedsizes = [(694, 466)] def setPluginVariables(self): self.be = None self.backendfail = False self.cachedmode = False self.isrecording = False self.regularfont = os.path.join(self.plugindir, "resources", "ArchivoNarrow-Regular.otf") self.italicfont = os.path.join(self.plugindir, "resources", "ArchivoNarrow-Italic.otf") self.boldfont = os.path.join(self.plugindir, "resources", "ArchivoNarrow-Bold.otf") self.cacheFile = os.path.join(self.plugindir, "resources", "cachedRecordings.json") self.mytitlefont = pygame.font.Font(self.boldfont, 24) self.myboldfont = pygame.font.Font(self.boldfont, 20) self.myregularfont = pygame.font.Font(self.regularfont, 16) self.myitalicfont = pygame.font.Font(self.italicfont, 16) self.rectwidth = 132 self.rectgap = 5 self.rectadjust = 2 def cacheRecs(self, recs): with open(self.cacheFile, 'w') as outfile: json.dump(recs, outfile) def loadCache(self): try: raw = open(self.cacheFile, 'r') recs = json.load(raw) except: recs = [] return recs def showScreen(self): self.surface.fill([0, 0, 0]) if self.be == None: try: recs = [] self.be = MythBE() self.upcomingrecs = self.be.getUpcomingRecordings() for r in self.upcomingrecs: rec = {} rec["title"] = r.title rec["subtitle"] = r.subtitle rec["time"] = r.starttime.strftime("%a %d %b %H:%M") rec["desc"] = r.description recs.append(rec) recorders = MythBE().getRecorderList() for recorder in recorders: if MythBE().isRecording(recorder): self.isrecording = True break self.backendfail = False self.cachedmode = False except: self.backendfail = True if self.backendfail: recs = self.loadCache() if recs: self.backendfail = False self.cachedmode = True if not self.backendfail: self.cacheRecs(recs) screentitle = self.mytitlefont.render("MythTV upcoming recordings", 1, (255, 255, 255)) screenrect = screentitle.get_rect() screenrect.centerx = self.surface.get_rect().centerx screenrect.centery = 20 self.surface.blit(screentitle, screenrect) n = min(len(recs), 5) if n > 0: for i in range(n): mytitlerect = pygame.Rect((0, 0, self.rectwidth, 60)) mytimerect = pygame.Rect((0, 0, self.rectwidth, 30)) mydescrect = pygame.Rect((0, 0, self.rectwidth, 330)) fontcolour = (255, 255, 255) rectcolour = (0, 50, 75) titletext = self.render_textrect(recs[i]["title"], self.myboldfont, mytitlerect, fontcolour, rectcolour, 1) timetext = self.render_textrect(recs[i]["time"], self.myitalicfont, mytimerect, fontcolour, rectcolour, 1) desctext = self.render_textrect(recs[i]["desc"], self.myregularfont, mydescrect, fontcolour, rectcolour, 0, margin=5) self.surface.blit(titletext, ((self.rectwidth * i) + (self.rectgap * (i + 1) + self.rectadjust), 40)) self.surface.blit(timetext, ((self.rectwidth * i) + (self.rectgap * (i + 1) + self.rectadjust), 80)) self.surface.blit(desctext, ((self.rectwidth * i) + (self.rectgap * (i + 1) + self.rectadjust), 105)) if self.cachedmode: mystatus = self.myitalicfont.render( "Backend is offline. Displaying cached recording list", 1, (255, 255, 255)) else: if self.isrecording: recording = "currently" else: recording = "not" mystatus = self.myitalicfont.render( "Backend is online and is " + recording + " recording.", 1, (255, 255, 255)) self.surface.blit(mystatus, (5, 445)) else: failtext = self.myboldfont.render( "No upcoming recordings found.", 1, (255, 255, 255)) failrect = failtext.get_rect() failrect.centerx = self.surface.get_rect().centerx failrect.centery = self.surface.get_rect().centery self.surface.blit(failtext, failrect) else: failtext = self.myboldfont.render("MythTV backend unavailable.", 1, (255, 255, 255)) failrect = failtext.get_rect() failrect.centerx = self.surface.get_rect().centerx failrect.centery = self.surface.get_rect().centery self.surface.blit(failtext, failrect) self.be = None # Scale our surface to the required screensize before sending back scaled = pygame.transform.scale(self.surface, self.screensize) self.screen.blit(scaled, (0, 0)) return self.screen
class myScreen(PiInfoScreen): refreshtime = 60 displaytime = 10 pluginname = "MythTV" plugininfo = "Displays details of upcoming recordings" supportedsizes = [ (694, 466) ] def setPluginVariables(self): self.be = None self.backendfail = False self.cachedmode = False self.isrecording = False self.regularfont = os.path.join(self.plugindir, "resources", "ArchivoNarrow-Regular.otf") self.italicfont = os.path.join(self.plugindir, "resources", "ArchivoNarrow-Italic.otf") self.boldfont = os.path.join(self.plugindir, "resources", "ArchivoNarrow-Bold.otf") self.cacheFile = os.path.join(self.plugindir, "resources", "cachedRecordings.json") self.mytitlefont = pygame.font.Font(self.boldfont, 24) self.myboldfont = pygame.font.Font(self.boldfont, 20) self.myregularfont = pygame.font.Font(self.regularfont, 16) self.myitalicfont = pygame.font.Font(self.italicfont, 16) self.rectwidth = 132 self.rectgap = 5 self.rectadjust = 2 def cacheRecs(self, recs): with open(self.cacheFile, 'w') as outfile: json.dump(recs, outfile) def loadCache(self): try: raw = open(self.cacheFile, 'r') recs = json.load(raw) except: recs = [] return recs def showScreen(self): self.surface.fill([0,0,0]) if self.be == None: try: recs = [] self.be = MythBE() self.upcomingrecs = self.be.getUpcomingRecordings() for r in self.upcomingrecs: rec = {} rec["title"] = r.title rec["subtitle"] = r.subtitle rec["time"] = r.starttime.strftime("%a %d %b %H:%M") rec["desc"] = r.description recs.append(rec) recorders = MythBE().getRecorderList() for recorder in recorders: if MythBE().isRecording(recorder): self.isrecording = True break self.backendfail = False self.cachedmode = False except: self.backendfail = True if self.backendfail: recs = self.loadCache() if recs: self.backendfail = False self.cachedmode = True if not self.backendfail: self.cacheRecs(recs) screentitle = self.mytitlefont.render("MythTV upcoming recordings",1,(255,255,255)) screenrect = screentitle.get_rect() screenrect.centerx = self.surface.get_rect().centerx screenrect.centery = 20 self.surface.blit(screentitle, screenrect) n = min(len(recs),5) if n > 0: for i in range(n): mytitlerect = pygame.Rect((0,0,self.rectwidth,60)) mytimerect = pygame.Rect((0,0,self.rectwidth,30)) mydescrect = pygame.Rect((0,0,self.rectwidth,330)) fontcolour = (255,255,255) rectcolour = (0,50,75) titletext = self.render_textrect(recs[i]["title"], self.myboldfont, mytitlerect, fontcolour, rectcolour,1) timetext = self.render_textrect(recs[i]["time"], self.myitalicfont, mytimerect, fontcolour, rectcolour,1) desctext = self.render_textrect(recs[i]["desc"], self.myregularfont, mydescrect, fontcolour, rectcolour,0,margin=5) self.surface.blit(titletext,((self.rectwidth*i)+(self.rectgap*(i+1)+self.rectadjust), 40)) self.surface.blit(timetext,((self.rectwidth*i)+(self.rectgap*(i+1)+self.rectadjust), 80)) self.surface.blit(desctext,((self.rectwidth*i)+(self.rectgap*(i+1)+self.rectadjust), 105)) if self.cachedmode: mystatus = self.myitalicfont.render("Backend is offline. Displaying cached recording list",1,(255,255,255)) else: if self.isrecording: recording = "currently" else: recording = "not" mystatus = self.myitalicfont.render("Backend is online and is " + recording + " recording.",1,(255,255,255)) self.surface.blit(mystatus,(5,445)) else: failtext = self.myboldfont.render("No upcoming recordings found.",1, (255,255,255)) failrect = failtext.get_rect() failrect.centerx = self.surface.get_rect().centerx failrect.centery = self.surface.get_rect().centery self.surface.blit(failtext, failrect) else: failtext = self.myboldfont.render("MythTV backend unavailable.",1, (255,255,255)) failrect = failtext.get_rect() failrect.centerx = self.surface.get_rect().centerx failrect.centery = self.surface.get_rect().centery self.surface.blit(failtext, failrect) self.be = None # Scale our surface to the required screensize before sending back scaled = pygame.transform.scale(self.surface,self.screensize) self.screen.blit(scaled,(0,0)) return self.screen