def __init__(self): self.db = MythDB() self.be = MythBE(db=db) self.vids = {} self._addCallback = doNothing self._events = [self.handleUpdate] self.be.registerevent(self.handleUpdate)
def copy(self): stime = time.time() srcsize = self.rec.filesize htime = [stime,stime,stime,stime] self.log(MythLog.GENERAL|MythLog.FILE, MythLog.INFO, "Copying myth://%s@%s/%s"\ % (self.rec.storagegroup, self.rec.hostname, self.rec.basename)\ +" to myth://Videos@%s/%s"\ % (self.vid.host, self.vid.filename)) srcfp = self.rec.open('r') dstfp = self.vid.open('w', nooverwrite=True) if self.job: self.job.setStatus(Job.RUNNING) tsize = 2**24 while tsize == 2**24: tsize = min(tsize, srcsize - dstfp.tell()) dstfp.write(srcfp.read(tsize)) htime.append(time.time()) rate = float(tsize*4)/(time.time()-htime.pop(0)) remt = (srcsize-dstfp.tell())/rate if self.job: self.job.setComment("%02d%% complete - %d seconds remaining" %\ (dstfp.tell()*100/srcsize, remt)) srcfp.close() dstfp.close() self.vid.hash = self.vid.getHash() self.log(MythLog.GENERAL|MythLog.FILE, MythLog.INFO, "Transfer Complete", "%d seconds elapsed" % int(time.time()-stime)) if self.opts.reallysafe: if self.job: self.job.setComment("Checking file hashes") self.log(MythLog.GENERAL|MythLog.FILE, MythLog.INFO, "Checking file hashes.") srchash = hashfile(self.rec.open('r')) dsthash = hashfile(self.vid.open('r')) if srchash != dsthash: raise MythError('Source hash (%s) does not match destination hash (%s)' \ % (srchash, dsthash)) elif self.opts.safe: self.log(MythLog.GENERAL|MythLog.FILE, MythLog.INFO, "Checking file sizes.") be = MythBE(db=self.vid._db) try: srcsize = be.getSGFile(self.rec.hostname, self.rec.storagegroup, \ self.rec.basename)[1] dstsize = be.getSGFile(self.vid.host, 'Videos', self.vid.filename)[1] except: raise MythError('Could not query file size from backend') if srcsize != dstsize: raise MythError('Source size (%d) does not match destination size (%d)' \ % (srcsize, dstsize)) if self.job: self.job.setComment("Complete - %d seconds elapsed" % \ (int(time.time()-stime))) self.job.setStatus(Job.FINISHED)
def __init__(self, host=None): self.db = MythDB() self.db.searchRecorded.handler = Recorded self.be = MythBE(db=self.db) self.log = MythLog(db=self.db) self.set_host(host) self.load_backends() self.load_storagegroups()
class Videos( Handler ): def __init__(self): self.db = MythDB() self.be = MythBE(db=db) self.vids = {} self._addCallback = doNothing self._events = [self.handleUpdate] self.be.registerevent(self.handleUpdate) def add(self, vid): if not vid.browse: return if vid.intid in self.vids: return vid.path = vid.filename vid.attr = Attr() try: ctime = vid.insertdate.timestamp() except: ctime = 0 vid.attr.st_ctime = ctime vid.attr.st_atime = atime vid.attr.st_mtime = ctime vid.attr.st_mode = stat.S_IFREG | 0444 t,s = self.be.getSGFile(vid.host, 'Videos', vid.filename) vid.attr.st_size = int(s) self._addCallback(vid) self.vids[vid.intid] = vid.attr.st_ino def getAll(self): for vid in Video.getAllEntries(db=self.db): self.add(vid) def handleUpdate(self, event=None): if event is None: self._reUp = re.compile( re.escape(static.BACKEND_SEP).\ join(['BACKEND_MESSAGE', 'VIDEO_LIST_CHANGE', 'empty'])) return self._reUp with self.db as cursor: cursor.execute("""SELECT intid FROM videometadata""") newids = [id[0] for id in cursor.fetchall()] oldids = self.vids.keys() for id in list(oldids): if id in newids: oldids.remove(id) newids.remove(id) for id in oldids: self._deleteCallback(self.vids[id]) for id in newids: self.add(Video(id, db=self.db))
class Videos(Handler): def __init__(self): self.db = MythDB() self.be = MythBE(db=db) self.vids = {} self._addCallback = doNothing self._events = [self.handleUpdate] self.be.registerevent(self.handleUpdate) def add(self, vid): if not vid.browse: return if vid.intid in self.vids: return vid.path = vid.filename vid.attr = Attr() try: ctime = vid.insertdate.timestamp() except: ctime = 0 vid.attr.st_ctime = ctime vid.attr.st_atime = atime vid.attr.st_mtime = ctime vid.attr.st_mode = stat.S_IFREG | 0444 t, s = self.be.getSGFile(vid.host, 'Videos', vid.filename) vid.attr.st_size = int(s) self._addCallback(vid) self.vids[vid.intid] = vid.attr.st_ino def getAll(self): for vid in Video.getAllEntries(db=self.db): self.add(vid) def handleUpdate(self, event=None): if event is None: self._reUp = re.compile( re.escape(static.BACKEND_SEP).\ join(['BACKEND_MESSAGE', 'VIDEO_LIST_CHANGE', 'empty'])) return self._reUp with self.db as cursor: cursor.execute("""SELECT intid FROM videometadata""") newids = [id[0] for id in cursor.fetchall()] oldids = self.vids.keys() for id in list(oldids): if id in newids: oldids.remove(id) newids.remove(id) for id in oldids: self._deleteCallback(self.vids[id]) for id in newids: self.add(Video(id, db=self.db))
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 main(): """Startup function.""" parser = OptionParser(usage="usage: %prog [options]") parser.add_option( "--verbose", action="store_true", default=False, help="enable verbose output of MythTV API") parser.add_option( '-f', "--force", action="store_true", default=False, help="non-interactive mode, answer 'yes' to all questions") parser.add_option( '-t', "--title", action="store", type="string", help="limit recordings that match title") opts, _ = parser.parse_args() MythLog._setlevel('unknown' if opts.verbose else 'err') # pylint:disable=protected-access try: backend = MythBE() recs = [ r for r in list(backend.getRecordings()) if r.recgroup == 'Deleted' ] if opts.title: recs = [ r for r in recs if re.findall(opts.title, r.title, re.IGNORECASE) ] if len(recs) == 0: print('no matching recordings found') sys.exit(0) if opts.force: undelete_all(backend, recs) else: interactive_undelete(backend, recs) except MythDBError as e: if e.name == 'DB_CREDENTIALS': print("ERROR: Could not find MythDB host:port OR correct login " "credentials!") sys.exit(-1) else: raise sys.exit(0)
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 __init__(self, opts, jobid=None): # Setup for the job to run if jobid: self.thisJob = Job(jobid) self.chanID = self.thisJob.chanid self.startTime = self.thisJob.starttime self.thisJob.update(status=Job.STARTING) # If no job ID given, must be a command line run else: self.thisJob = jobid self.chanID = opts.chanid self.startTime = opts.startdate + " " + opts.starttime + opts.offset self.opts = opts self.type = "none" self.db = MythDB() self.log = MythLog(module='Myth-Rec-to-Vid.py', db=self.db) # Capture the backend host name self.host = self.db.gethostname() # prep objects self.rec = Recorded((self.chanID,self.startTime), db=self.db) self.log(MythLog.GENERAL, MythLog.INFO, 'Using recording', '%s - %s' % (self.rec.title.encode('utf-8'), self.rec.subtitle.encode('utf-8'))) self.vid = Video(db=self.db).create({'title':'', 'filename':'', 'host':self.host}) self.bend = MythBE(db=self.db)
def transcode(jobid=None, chanid=None, starttime=None): ' connect to mythtv database ' db = MythDB() be = MythBE(db=db) logging.info("start transcode") if jobid: job = Job(jobid, db=db) chanid = job.chanid starttime = job.starttime logging.info("%s" % jobid) logging.info("%s" % chanid) logging.info("%s" % starttime) rec = Recorded((chanid, starttime), db=db) ' TODO: get the starttime into the correct format (from 20150827014200 to 2015-08-27 00:42:00-06:00)' ' loop all files in lib_dir that are symlinks and find the one that matches this recording ' for ld in LIBDIR: for dp, dn, files in os.walk(ld): for file in files: filepath = os.path.join(dp,file) if (os.path.islink(filepath)): logging.debug("%s -> %s" % (filepath, os.readlink(filepath))) ' do the transode ' ' update the database for the new file name ' ' update the symlink for the new file name '
def main(): """Startup function.""" parser = OptionParser(usage="usage: %prog [options]") parser.add_option("--verbose", action="store_true", default=False, help="enable verbose output of MythTV API") parser.add_option( '-f', "--force", action="store_true", default=False, help="non-interactive mode, answer 'yes' to all questions") parser.add_option('-t', "--title", action="store", type="string", help="limit recordings that match title") opts, _ = parser.parse_args() MythLog._setlevel('unknown' if opts.verbose else 'err') # pylint:disable=protected-access try: backend = MythBE() recs = [ r for r in list(backend.getRecordings()) if r.recgroup == 'Deleted' ] if opts.title: recs = [ r for r in recs if re.findall(opts.title, r.title, re.IGNORECASE) ] if len(recs) == 0: print('no matching recordings found') sys.exit(0) if opts.force: undelete_all(backend, recs) else: interactive_undelete(backend, recs) except MythDBError as e: if e.name == 'DB_CREDENTIALS': print("ERROR: Could not find MythDB host:port OR correct login " "credentials!") sys.exit(-1) else: raise sys.exit(0)
def main(opts): MythBE.getPendingRecordings.handler = MyProgram be = MythBE() now = datetime.now() if not opts.plaintext: print '<h3>Upcoming Recordings:</h3>' print '<div class="schedule">' count = 0 for rec in be.getPendingRecordings(): if not ((opts.filter & 2**0 and rec.is_scheduled) or (opts.filter & 2**1 and rec.is_duplicate) or (opts.filter & 2**2 and rec.is_deactivated) or (opts.filter & 2**3 and rec.is_conflict)): continue if opts.time and (opts.time < rec.recstartts): continue if now > rec.recendts: continue if opts.count and (opts.count <= count): break count += 1 if opts.plaintext: print '{0} - {1}'.format(rec.starttime.strftime('%m/%d, %I:%M %p'), rec.callsign) if rec.subtitle: print '{0.title} - {0.subtitle}'.format(rec) else: print '{0.title}'.format(rec) print rec.description print '' else: print '<a href="#">{0} - {1} - {2}'.format( rec.starttime.strftime('%m/%d, %I:%M %p'), rec.callsign, rec.title), if rec.subtitle: print rec.subtitle, print '<br /><span><strong>{0.title}</strong>'.format(rec), print rec.starttime.strftime('%m/%d, %I:%M %p'), print '<br /><em>{0.description}<br/></span></a><hr />' if not opts.plaintext: print '</div>'
def main(opts): MythBE.getPendingRecordings.handler = MyProgram be = MythBE() now = datetime.now() if not opts.plaintext: print '<h3>Upcoming Recordings:</h3>' print '<div class="schedule">' count = 0 for rec in be.getPendingRecordings(): if not ((opts.filter&2**0 and rec.is_scheduled) or (opts.filter&2**1 and rec.is_duplicate) or (opts.filter&2**2 and rec.is_deactivated) or (opts.filter&2**3 and rec.is_conflict)): continue if opts.time and (opts.time < rec.recstartts): continue if now > rec.recendts: continue if opts.count and (opts.count <= count): break count += 1 if opts.plaintext: print '{0} - {1}'.format(rec.starttime.strftime('%m/%d, %I:%M %p'), rec.callsign) if rec.subtitle: print '{0.title} - {0.subtitle}'.format(rec) else: print '{0.title}'.format(rec) print rec.description print '' else: print '<a href="#">{0} - {1} - {2}'.format(rec.starttime.strftime('%m/%d, %I:%M %p'), rec.callsign, rec.title), if rec.subtitle: print rec.subtitle, print '<br /><span><strong>{0.title}</strong>'.format(rec), print rec.starttime.strftime('%m/%d, %I:%M %p'), print '<br /><em>{0.description}<br/></span></a><hr />' if not opts.plaintext: print '</div>'
def test_repr_004_01(self): """ Test '__repr__' and '__str__' methods of 'MythBE' class. Note: MythBE inherits from 'FileOps', which is inherited from 'BeCache'. """ db = MythDB() be = MythBE(db=db) print() print(repr(be)) print(str(be))
def load_backends(self): with self.db as c: c.execute("""SELECT hostname FROM settings WHERE value='BackendServerIP'""") hosts = [r[0] for r in c.fetchall()] self.hosts = [] for host in hosts: # try to access all defined hosts, and # store the ones currently accessible try: MythBE(backend=host) self.hosts.append(host) except: pass
def __init__(self, opts, jobid=None): # Setup for the job to run if jobid: self.thisJob = Job(jobid) self.chanID = self.thisJob.chanid self.startTime = self.thisJob.starttime self.thisJob.update(status=Job.STARTING) # If no job ID given, must be a command line run else: self.thisJob = jobid self.chanID = opts.chanid self.startTime = opts.startdate + " " + opts.starttime + opts.offset self.opts = opts self.type = "none" self.db = MythDB() self.log = MythLog(module='Myth-Rec-to-Vid.py', db=self.db) # Capture the backend host name self.host = self.db.gethostname() # prep objects self.rec = Recorded((self.chanID, self.startTime), db=self.db) self.log( MythLog.GENERAL, MythLog.INFO, 'Using recording', '%s - %s' % (self.rec.title.encode('utf-8'), self.rec.subtitle.encode('utf-8'))) self.vid = Video(db=self.db).create({ 'title': '', 'filename': '', 'host': self.host }) self.bend = MythBE(db=self.db)
#!/usr/bin/python # Check for recording conflicts on a mythtv-backend from MythTV import MythBE be = MythBE() for program in be.getConflictedRecordings(): title = "{} - {}".format(program['title'], program['subtitle']) starttime = program['starttime'].strftime('%Y-%m-%d %I:%M %p') print("{} at {} is in conflict and will not be recorded.\n".format(title, starttime))
def delete(self): be = MythBE(self.host, db=DB) be.deleteFile(self, self.group)
#!/usr/bin/python # get the files to delete # ./files.py | cut -f 1 -d " " | grep -v lock | grep var | xargs ls -lastrh from MythTV import MythBE import os myth = MythBE() allShows = myth.getRecordings() dirs = ["/var/lib/mythtv/recordings", "/var/lib/mythtv/recordings2", "/var/lib/mythtv/recordings3", "/var/lib/mythtv/movies-group"] showMap = {} badFiles = [] def handleFile(dir,file): parts = file.split(".") key = parts[0] bad = False if key not in showMap: badFiles.append(os.path.join(dir,file)) bad = True if parts[len(parts)-1] == "mpg": print key, file def handleDir(dir): for file in os.listdir(dir): path = os.path.join(dir,file) if os.path.isdir(path): handleDir(path)
def delete(self): be = MythBE(self.hosts[0], db=self.db) be.deleteFile(self, self.group)
#!/usr/bin/python from MythTV import MythBE, Program from datetime import timedelta, datetime, time def willRec(x): return x.recstatus == Program.rsWillRecord and x.airdate is not None myth = MythBE() pending = filter(willRec, myth.getPendingRecordings()) print "%-20.20s %-35.35s %-20s %-20s" % ("title", "subtitle", "original date", "record start time") for p in sorted(pending, key=lambda a: a.title): diff = p.starttime - datetime.combine(p.airdate, time(tzinfo=p.starttime.tzinfo)) if diff > timedelta(days=1,hours=6) and diff < timedelta(days=24): print "%-20.20s %-35.35s %-20s %-20s" % (p.title, p.subtitle, p.airdate, p.starttime)
def copy(self): stime = time.time() srcsize = self.rec.filesize htime = [stime, stime, stime, stime] self.log(MythLog.GENERAL|MythLog.FILE, MythLog.INFO, "Copying myth://%s@%s/%s"\ % (self.rec.storagegroup, self.rec.hostname, self.rec.basename)\ +" to myth://Videos@%s/%s"\ % (self.vid.host, self.vid.filename)) srcfp = self.rec.open('r') dstfp = self.vid.open('w', nooverwrite=True) if self.job: self.job.setStatus(Job.RUNNING) tsize = 2**24 while tsize == 2**24: tsize = min(tsize, srcsize - dstfp.tell()) dstfp.write(srcfp.read(tsize)) htime.append(time.time()) rate = float(tsize * 4) / (time.time() - htime.pop(0)) remt = (srcsize - dstfp.tell()) / rate if self.job: self.job.setComment("%02d%% complete - %d seconds remaining" %\ (dstfp.tell()*100/srcsize, remt)) srcfp.close() dstfp.close() self.vid.hash = self.vid.getHash() self.log(MythLog.GENERAL | MythLog.FILE, MythLog.INFO, "Transfer Complete", "%d seconds elapsed" % int(time.time() - stime)) if self.opts.reallysafe: if self.job: self.job.setComment("Checking file hashes") self.log(MythLog.GENERAL | MythLog.FILE, MythLog.INFO, "Checking file hashes.") srchash = hashfile(self.rec.open('r')) dsthash = hashfile(self.vid.open('r')) if srchash != dsthash: raise MythError('Source hash (%s) does not match destination hash (%s)' \ % (srchash, dsthash)) elif self.opts.safe: self.log(MythLog.GENERAL | MythLog.FILE, MythLog.INFO, "Checking file sizes.") be = MythBE(db=self.vid._db) try: srcsize = be.getSGFile(self.rec.hostname, self.rec.storagegroup, \ self.rec.basename)[1] dstsize = be.getSGFile(self.vid.host, 'Videos', self.vid.filename)[1] except: raise MythError('Could not query file size from backend') if srcsize != dstsize: raise MythError('Source size (%d) does not match destination size (%d)' \ % (srcsize, dstsize)) if self.job: self.job.setComment("Complete - %d seconds elapsed" % \ (int(time.time()-stime))) self.job.setStatus(Job.FINISHED)
def filepathToScreenshotFromRecording(recording): filepath = filepathFromRecording(recording) return filepath + ".png" db = None db = MythDB() if db == None: print "Database could not be open or found" exit() be = None be = MythBE() if be == None: print "Backend could not be found." #exit() recordings = be.getRecordings() for r in recordings: filepath = filepathFromRecording(r) size = -1 try: size = os.path.getsize(filepath) / 1024 / 1024 except: size = "NaN"
for programTeam in programTeams: if isTeamMatch(team, programTeam): return True return False try: opts, args = getopt.getopt(sys.argv[1:],"h:",["hours="]) except getopt.GetoptError: print 'soccer.py -h <hours a recording can be late - default 1>' sys.exit(2) hours = 1 for o, a in opts: if o == "-h": hours = int(a) myth = MythBE() pending = filter(sportsRecs, myth.getPendingRecordings()) # get the output from the schedule output = check_output(["/usr/bin/java", "-jar", "/opt/soccer/soccerschedule.jar", "--weeks=2"]) # split the output into lines and then filter only the real output lines = filter(filterOutput,output.splitlines()) for line in lines: data = getData(line) if not hasMatches(data, pending): print "%s %s vs. %s (%s) on %s " % (data[0], " ".join(data[3]), " ".join(data[2]), data[1], data[4])
#!/usr/bin/python # Check for recording conflicts on a mythtv-backend from MythTV import MythBE be = MythBE() for program in be.getConflictedRecordings(): title = "{} - {}".format(program['title'], program['subtitle']) starttime = program['starttime'].strftime('%Y-%m-%d %I:%M %p') print("{} at {} is in conflict and will not be recorded.\n".format( title, starttime))
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
from MythTV import MythDB, MythBE db = None db = MythDB() if db == None: print "Database could not be open or found" exit() be = None be = MythBE() if be == None: print "Backend could not be found." #exit() for x in be.getRecorderList(): print "Tuner " + str(x) + " is recording: " + str( be.getCurrentRecording(x).title) fs = be.getFreeSpace() print "----------------------------------" for x in fs: print "Host: " + x.host print "\tPath: " + x.path print "\t\tTotal space: " + str(x.totalspace / 1024 / 1024) + "MB" print "\t\tFree space: " + str(x.freespace / 1024 / 1024) + "MB" print "\t\tUsed space: " + str(x.usedspace / 1024 / 1024) + "MB" print "----------------------------------"
def copy(self): stime = time.time() srcsize = self.rec.filesize htime = [stime,stime,stime,stime] self.log(MythLog.GENERAL|MythLog.FILE, MythLog.INFO, "Copying myth://%s@%s/%s"\ % (self.rec.storagegroup, self.rec.hostname, self.rec.basename)\ +" to myth://Videos@%s/%s"\ % (self.vid.host, self.vid.filename)) bend = MythBE(db=self.vid._db) self.log(MythLog.GENERAL, MythLog.INFO, 'Checking for duplication of ', '%s - %s' % (self.rec.title.encode('utf-8'), self.rec.subtitle.encode('utf-8'))) if bend.fileExists(self.vid.filename, 'Videos'): self.log(MythLog.GENERAL, MythLog.INFO, 'Recording already exists in Myth Videos') self.job.setComment("Action would result in duplicate entry, job ended" ) self.job.setStatus(Job.FINISHED) self.vid.delete() self.log(MythLog.GENERAL, MythLog.INFO, 'Exiting program') sys.exit(0) else: srcfp = self.rec.open('r') dstfp = self.vid.open('w') if self.job: self.job.setStatus(Job.RUNNING) tsize = 2**24 while tsize == 2**24: tsize = min(tsize, srcsize - dstfp.tell()) dstfp.write(srcfp.read(tsize)) htime.append(time.time()) rate = float(tsize*4)/(time.time()-htime.pop(0)) remt = (srcsize-dstfp.tell())/rate if self.job: self.job.setComment("%02d%% complete - %d seconds remaining" %\ (dstfp.tell()*100/srcsize, remt)) srcfp.close() dstfp.close() self.vid.hash = self.vid.getHash() self.log(MythLog.GENERAL|MythLog.FILE, MythLog.INFO, "Transfer Complete", "%d seconds elapsed" % int(time.time()-stime)) if self.opts.reallysafe: if self.job: self.job.setComment("Checking file hashes") self.log(MythLog.GENERAL|MythLog.FILE, MythLog.INFO, "Checking file hashes.") srchash = hashfile(self.rec.open('r')) dsthash = hashfile(self.rec.open('r')) if srchash != dsthash: raise MythError('Source hash (%s) does not match destination hash (%s)' \ % (srchash, dsthash)) elif self.opts.safe: self.log(MythLog.GENERAL|MythLog.FILE, MythLog.INFO, "Checking file sizes.") be = MythBE(db=self.vid._db) try: srcsize = be.getSGFile(self.rec.hostname, self.rec.storagegroup, \ self.rec.basename)[1] dstsize = be.getSGFile(self.vid.host, 'Videos', self.vid.filename)[1] except: raise MythError('Could not query file size from backend') if srcsize != dstsize: raise MythError('Source size (%d) does not match destination size (%d)' \ % (srcsize, dstsize)) if self.job: self.job.setComment("Complete - %d seconds elapsed" % \ (int(time.time()-stime))) self.job.setStatus(Job.FINISHED)
def exists(self): be = MythBE(self.host, db=DB) return be.fileExists(self, self.group)
class Recordings( Handler ): def __init__(self): self.be = MythBE() self.recs = {} self._events = [self.handleAdd, self.handleDelete, self.handleUpdate] for e in self._events: self.be.registerevent(e) def add(self, rec): # check for duplicates match = (str(rec.chanid),rec.recstartts.isoformat()) if match in self.recs: return # add attributes rec.attr = Attr() ctime = rec.lastmodified.timestamp() rec.attr.st_ctime = ctime rec.attr.st_mtime = ctime rec.attr.st_atime = ctime rec.attr.st_size = rec.filesize rec.attr.st_mode = stat.S_IFREG | 0444 # process name rec.path = rec.formatPath(self.fmt, ' ') # add file self._addCallback(rec) self.recs[match] = rec.attr.st_ino def genAttr(self, rec): attr = Attr() ctime = rec.lastmodified.timestamp() attr.st_ctime = ctime attr.st_mtime = ctime attr.st_atime = ctime attr.st_size = rec.filesize attr.st_mode = stat.S_IFREG | 0444 return attr def getAll(self): for rec in self.be.getRecordings(): self.add(rec) def handleAdd(self, event=None): if event is None: self._reAdd = re.compile( re.escape(static.BACKEND_SEP).\ join(['BACKEND_MESSAGE', 'RECORDING_LIST_CHANGE ADD ' '(?P<chanid>[0-9]*) ' '(?P<starttime>[0-9-]*T[0-9:]*)', 'empty'])) return self._reAdd LOG(LOG.FILE, 'add event received', event) match = self._reAdd.match(event).groups() if match in self.recs: return rec = self.be.getRecording(match[0], match[1]) self.add(rec) def handleDelete(self, event=None): if event is None: self._reDel = re.compile( re.escape(static.BACKEND_SEP).\ join(['BACKEND_MESSAGE', 'RECORDING_LIST_CHANGE DELETE ' '(?P<chanid>[0-9]*) ' '(?P<starttime>[0-9-]*T[0-9:]*)', 'empty'])) return self._reDel LOG(LOG.FILE, 'delete event received', event) match = self._reDel.match(event).groups() if match not in self.recs: return self._deleteCallback(self.recs[match]) del self.recs[match] def handleUpdate(self, event=None): if event is None: self._reUp = re.compile( re.escape(static.BACKEND_SEP).\ join(['BACKEND_MESSAGE', 'UPDATE_FILE_SIZE ' '(?P<chanid>[0-9]*) ' '(?P<starttime>[0-9-]*T[0-9:]*) ' '(?P<size>[0-9]*)', 'empty'])) return self._reUp LOG(LOG.FILE, 'update event received', event) match = self._reUp.match(event) size = match.group(3) match = match.group(1,2) if match not in self.recs: return inode = self.recs[match] rec = self._inodeCallback(inode) rec.filesize = int(size) rec.attr.st_size = int(size) def setFormat(self, fmt): if '%' not in fmt: LOG(LOG.FILE, 'pulling format from database', 'mythfs.format.%s' % fmt) fmt = self.be.db.settings.NULL['mythfs.format.%s' % fmt] LOG(LOG.FILE, 'using format', fmt) self.fmt = fmt
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)
def __init__(self): self.be = MythBE() self.recs = {} self._events = [self.handleAdd, self.handleDelete, self.handleUpdate] for e in self._events: self.be.registerevent(e)
class populate(object): __metaclass__ = Singleton def __init__(self, host=None): self.db = MythDB() self.db.searchRecorded.handler = Recorded self.be = MythBE(db=self.db) self.log = MythLog(db=self.db) self.set_host(host) self.load_backends() self.load_storagegroups() def set_host(self, host): self.host = host if host: # if the host was defined on the command line, check # to make sure such host is defined in the database with self.db as c: c.execute( """SELECT count(1) FROM settings WHERE hostname=? AND value=?""", (host, 'BackendServerIP')) if c.fetchone()[0] == 0: raise Exception('Invalid hostname specified for backend.') def load_backends(self): with self.db as c: c.execute("""SELECT hostname FROM settings WHERE value='BackendServerIP'""") hosts = [r[0] for r in c.fetchall()] self.hosts = [] for host in hosts: # try to access all defined hosts, and # store the ones currently accessible try: MythBE(backend=host) self.hosts.append(host) except: pass def load_storagegroups(self): self.storagegroups = \ [sg for sg in self.db.getStorageGroup() \ if sg.groupname not in ('Videos','Banners','Coverart',\ 'Fanart','Screenshots','Trailers')] def flush(self): self.misplaced = [] self.zerorecs = [] self.pendrecs = [] self.orphrecs = [] self.orphvids = [] self.orphimgs = [] self.dbbackup = [] self.unfiltered = [] def __call__(self): self.refresh_content() return self def refresh_content(self): # scan through all accessible backends to # generate a new listof orphaned content self.flush() unfiltered = {} for host in self.hosts: for sg in self.storagegroups: try: dirs, files, sizes = self.be.getSGList( host, sg.groupname, sg.dirname) for f, s in zip(files, sizes): newfile = File(host, sg.groupname, sg.dirname, f, s, self.db) # each filename should be unique among all storage directories # defined on all backends, but may exist in the same directory # on multiple backends if they are shared if newfile not in unfiltered: # add a new file to the list unfiltered[str(newfile)] = newfile else: # add a reference to the host on which it was found unfiltered[str(newfile)].add_host(host) except: self.log( MythLog.GENERAL, MythLog.INFO, 'Could not access {0.groupname}@{1}{0.dirname}'.format( sg, host)) for rec in self.db.searchRecorded(livetv=True): if rec.hostname not in self.hosts: # recording is on an offline backend, ignore it name = rec.basename.rsplit('.', 1)[0] for n in unfiltered.keys(): if name in n: # and anything related to it del unfiltered[n] elif rec.basename in unfiltered: # run through list of recordings, matching basenames # with found files, and removing file from list f = unfiltered[rec.basename] del unfiltered[rec.basename] if f.size < 1024: # file is too small to be of any worth self.zerorecs.append(rec) elif rec.doubleorphan: # file is marked for deletion, but has been forgotten by the backend self.pendrecs.append(rec) elif rec.hostname not in f.hosts: # recording is in the database, but not where it should be self.misplaced.append(rec) name = rec.basename.rsplit('.', 1)[0] for f in unfiltered.keys(): if name in f: # file is related to a valid recording, ignore it del unfiltered[f] else: # recording has been orphaned self.orphrecs.append(rec) for n, f in unfiltered.iteritems(): if n.endswith('.mpg') or n.endswith('.nuv'): # filter files with recording extensions self.orphvids.append(f) elif n.endswith('.png'): # filter files with image extensions self.orphimgs.append(f) elif 'sql' in n: # filter for database backups self.dbbackup.append(f) else: self.unfiltered.append(f) def print_results(self): printrecs("Recordings found on the wrong host", self.misplaced) printrecs("Recordings with missing files", self.orphrecs) printrecs("Zero byte recordings", self.zerorecs) printrecs("Forgotten pending deletions", self.pendrecs) printfiles("Orphaned video files", self.orphvids) printfiles("Orphaned snapshots", self.orphimgs) printfiles("Database backups", self.dbbackup) printfiles("Other files", self.unfiltered)
from MythTV import MythDB, MythBE db = None db = MythDB() if db == None: print "Database could not be open or found" exit() be = None be = MythBE() if be == None: print "Backend could not be found." #exit() for x in be.getRecorderList(): print "Tuner " + str(x) + " is recording: " + str(be.getCurrentRecording(x).title) fs = be.getFreeSpace() print "----------------------------------" for x in fs: print "Host: " + x.host print "\tPath: " + x.path print "\t\tTotal space: " + str(x.totalspace / 1024 / 1024) + "MB" print "\t\tFree space: " + str(x.freespace / 1024 / 1024) + "MB" print "\t\tUsed space: " + str(x.usedspace / 1024 / 1024) + "MB" print "----------------------------------"
class populate( object ): __metaclass__ = Singleton def __init__(self, host=None): self.db = MythDB() self.db.searchRecorded.handler = Recorded self.be = MythBE(db=self.db) self.log = MythLog(db=self.db) self.set_host(host) self.load_backends() self.load_storagegroups() def set_host(self, host): self.host = host if host: # if the host was defined on the command line, check # to make sure such host is defined in the database with self.db as c: c.execute("""SELECT count(1) FROM settings WHERE hostname=? AND value=?""", (host, 'BackendServerIP')) if c.fetchone()[0] == 0: raise Exception('Invalid hostname specified for backend.') def load_backends(self): with self.db as c: c.execute("""SELECT hostname FROM settings WHERE value='BackendServerIP'""") hosts = [r[0] for r in c.fetchall()] self.hosts = [] for host in hosts: # try to access all defined hosts, and # store the ones currently accessible try: MythBE(backend=host) self.hosts.append(host) except: pass def load_storagegroups(self): self.storagegroups = \ [sg for sg in self.db.getStorageGroup() \ if sg.groupname not in ('Videos','Banners','Coverart',\ 'Fanart','Screenshots','Trailers')] def flush(self): self.misplaced = [] self.zerorecs = [] self.pendrecs = [] self.orphrecs = [] self.orphvids = [] self.orphimgs = [] self.dbbackup = [] self.unfiltered = [] def __call__(self): self.refresh_content() return self def refresh_content(self): # scan through all accessible backends to # generate a new listof orphaned content self.flush() unfiltered = {} for host in self.hosts: for sg in self.storagegroups: try: dirs,files,sizes = self.be.getSGList(host, sg.groupname, sg.dirname) for f,s in zip(files, sizes): newfile = File(host, sg.groupname, sg.dirname, f, s, self.db) # each filename should be unique among all storage directories # defined on all backends, but may exist in the same directory # on multiple backends if they are shared if newfile not in unfiltered: # add a new file to the list unfiltered[str(newfile)] = newfile else: # add a reference to the host on which it was found unfiltered[str(newfile)].add_host(host) except: self.log(MythLog.GENERAL, MythLog.INFO, 'Could not access {0.groupname}@{1}{0.dirname}'.format(sg, host)) for rec in self.db.searchRecorded(livetv=True): if rec.hostname not in self.hosts: # recording is on an offline backend, ignore it name = rec.basename.rsplit('.',1)[0] for n in unfiltered.keys(): if name in n: # and anything related to it del unfiltered[n] elif rec.basename in unfiltered: # run through list of recordings, matching basenames # with found files, and removing file from list f = unfiltered[rec.basename] del unfiltered[rec.basename] if f.size < 1024: # file is too small to be of any worth self.zerorecs.append(rec) elif rec.doubleorphan: # file is marked for deletion, but has been forgotten by the backend self.pendrecs.append(rec) elif rec.hostname not in f.hosts: # recording is in the database, but not where it should be self.misplaced.append(rec) name = rec.basename.rsplit('.',1)[0] for f in unfiltered.keys(): if name in f: # file is related to a valid recording, ignore it del unfiltered[f] else: # recording has been orphaned self.orphrecs.append(rec) for n,f in unfiltered.iteritems(): if n.endswith('.mpg') or n.endswith('.nuv'): # filter files with recording extensions self.orphvids.append(f) elif n.endswith('.png'): # filter files with image extensions self.orphimgs.append(f) elif 'sql' in n: # filter for database backups self.dbbackup.append(f) else: self.unfiltered.append(f) def print_results(self): printrecs("Recordings found on the wrong host", self.misplaced) printrecs("Recordings with missing files", self.orphrecs) printrecs("Zero byte recordings", self.zerorecs) printrecs("Forgotten pending deletions", self.pendrecs) printfiles("Orphaned video files", self.orphvids) printfiles("Orphaned snapshots", self.orphimgs) printfiles("Database backups", self.dbbackup) printfiles("Other files", self.unfiltered)
res = int(res) except: res = input('input number. ctrl-c to exit > ') continue if (res <= 0) or (res > len(opts)): res = input('input number within range > ') continue break opt = opts[res - 1] if opt[1] is None: continue else: opt[1](opt[2]) except KeyboardInterrupt: break except EOFError: sys.exit(0) DB = MythDB() BE = MythBE(db=DB) DB.searchRecorded.handler = MyRecorded DB.searchRecorded.dbclass = MyRecorded if __name__ == '__main__': if len(sys.argv) == 2: main(sys.argv[1]) else: main()
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
def main(): parser = OptionParser(usage="usage: option [option] [option]") maintenancegroup = OptionGroup( parser, "Maintenance", "These options can be used to perform DB cleanup.") maintenancegroup.add_option( "--dedup", action="store_true", default=False, dest="dedup", help="checks for duplicate entries in the Video database") maintenancegroup.add_option("--check_orphans", action="store_true", default=False, dest="check_orphans", help="checks for orphaned DB entries in Video") parser.add_option_group(maintenancegroup) actiongroup = OptionGroup(parser, "Meta Updates", "This option updates Video Meta Data") actiongroup.add_option('--update', action='store_true', default=False, dest='update', help='Updates the video meta data on each entry') parser.add_option_group(actiongroup) othergroup = OptionGroup(parser, 'Other Options', "These options allow for additional controls") othergroup.add_option( '--folder', action='store', type='string', dest='folder', help='Limits actions to Videos in a specified folder,\ example "Movies" would limit to any filename \ starting with Movies/some_video_file.mpg') othergroup.add_option( '--step', action="store_true", default=False, dest='step', help='Steps through each action to allow evaluation of \ process') parser.add_option_group(othergroup) opts, args = parser.parse_args() # if a manual channel and time entry then setup the export with opts # sys.exit(1) # setup the connection to the DB db = MythDB() be = MythBE() #Setup a scanner for all videos in the DB videos = db.searchVideos() # setup a Video object to work with def get_Meta(item): metadata = item.exportMetadata() if not item.filename.startswith('Television'): grab = VideoGrabber('Movie') if item.get('inetref') != '00000000': try: match = grab.grabInetref(item.inetref) item.importMetadata(match) item.plot = match.get('description') item.title = match.get('title') copy_Art(match, item) item.update() return except Exception: print 'grabber failed for: ' + str(item.get('inetref')) print 'trying by name instead' try: results = grab.sortedSearch(item.title) except Exception, e: print 'grabber failed for: ' + str(item.get('inetref')) print e return if len(results) > 0: if len(results) > 1: menu = {} list = 1 for each in results: menu[list]= each.title + ', year: ' + str(each.get('year')) \ + ', inetref: ' + str(each.get('inetref')) list = list + 1 menu[list] = 'Skip to next video\n\n' print '\n' while True: options = menu.keys() options.sort() for entry in options: print entry, menu[entry] try: selection = input("Please Select: ") if selection in range(1, len(results) + 1): listing = results[selection - 1] break elif selection == len(results) + 1: return else: print "Invalid Selection, try again!\n\n" except Exception: print "Invalid Selection, try again!\n\n" else: listing = results[0] try: match = grab.grabInetref(listing.get('inetref')) item.importMetadata(match) item.plot = match.get('description') item.title = match.get('title') copy_Art(match, item) item.update() print 'Full MetaData Import complete for: ' + item.title + '\n' except Exception, e: print 'grabber failed for: ' + str(item.get('inetref')) print e elif len(results) == 0: print 'No MetaData to import for: ' + item.title + '\n'
# files from TitanTV, importing them as scheduling rules for MythTV to # record #--------------------------- __title__ = "TitanImport" __author__ = "Raymond Wagner" __version__ = "v0.1.0" from MythTV import MythDB, MythBE, Channel, Record, datetime from datetime import timedelta from optparse import OptionParser import lxml.etree as etree DB = MythDB() BE = MythBE(db=DB) tzoff = timedelta(0, int(BE.backendCommand('QUERY_TIME_ZONE').split('[]:[]')[1])) def FindProgram(xmlprog, fuzzy): tvmode = xmlprog.find('tv-mode').text chan = None if tvmode == 'cable': # for cable, require a match of channel and station name for c in Channel.getAllEntries(db=DB): if c.freqid == xmlprog.find('rf-channel').text and \ c.mplexid == 32767 and \ c.callsign == xmlprog.find('station').text: chan = c break
def setUp(self): with add_log_flags(): self.mydb = MythDB() self.mybe = MythBE(db=self.mydb)
class VIDEO: def __init__(self, opts, jobid=None): # Setup for the job to run if jobid: self.thisJob = Job(jobid) self.chanID = self.thisJob.chanid self.startTime = self.thisJob.starttime self.thisJob.update(status=Job.STARTING) # If no job ID given, must be a command line run else: self.thisJob = jobid self.chanID = opts.chanid self.startTime = opts.startdate + " " + opts.starttime + opts.offset self.opts = opts self.type = "none" self.db = MythDB() self.log = MythLog(module='Myth-Rec-to-Vid.py', db=self.db) # Capture the backend host name self.host = self.db.gethostname() # prep objects self.rec = Recorded((self.chanID, self.startTime), db=self.db) self.log( MythLog.GENERAL, MythLog.INFO, 'Using recording', '%s - %s' % (self.rec.title.encode('utf-8'), self.rec.subtitle.encode('utf-8'))) self.vid = Video(db=self.db).create({ 'title': '', 'filename': '', 'host': self.host }) self.bend = MythBE(db=self.db) def check_hash(self): self.log(self.log.GENERAL, self.log.INFO, 'Performing copy validation.') srchash = self.bend.getHash(self.rec.basename, self.rec.storagegroup) dsthash = self.bend.getHash(self.vid.filename, 'Videos') if srchash != dsthash: return False else: return True def copy(self): stime = time.time() srcsize = self.rec.filesize htime = [stime, stime, stime, stime] self.log(MythLog.GENERAL|MythLog.FILE, MythLog.INFO, "Copying myth://%s@%s/%s"\ % (self.rec.storagegroup, self.rec.hostname, self.rec.basename)\ +" to myth://Videos@%s/%s"\ % (self.host, self.vid.filename)) srcfp = self.rec.open('r') dstfp = self.vid.open('w') if self.thisJob: self.set_job_status(Job.RUNNING) tsize = 2**24 while tsize == 2**24: tsize = min(tsize, srcsize - dstfp.tell()) dstfp.write(srcfp.read(tsize)) htime.append(time.time()) rate = float(tsize * 4) / (time.time() - htime.pop(0)) remt = (srcsize - dstfp.tell()) / rate if self.thisJob: self.thisJob.setComment("%02d%% complete - %d seconds remaining" %\ (dstfp.tell()*100/srcsize, remt)) srcfp.close() dstfp.close() self.vid.hash = self.vid.getHash() self.log(MythLog.GENERAL | MythLog.FILE, MythLog.INFO, "Transfer Complete", "%d seconds elapsed" % int(time.time() - stime)) if self.thisJob: self.thisJob.setComment("Complete - %d seconds elapsed" % \ (int(time.time()-stime))) def copy_markup(self, start, stop): for mark in self.rec.markup: if mark.type in (start, stop): self.vid.markup.add(mark.mark, 0, mark.type) def copy_seek(self): for seek in self.rec.seek: self.vid.markup.add(seek.mark, seek.offset, seek.type) def delete_vid(self): self.vid.delete() def delete_rec(self): self.rec.delete() def dup_check(self): self.log(MythLog.GENERAL, MythLog.INFO, 'Processing new file name ', '%s' % (self.vid.filename)) self.log( MythLog.GENERAL, MythLog.INFO, 'Checking for duplication of ', '%s - %s' % (self.rec.title.encode('utf-8'), self.rec.subtitle.encode('utf-8'))) if self.bend.fileExists(self.vid.filename, 'Videos'): self.log(MythLog.GENERAL, MythLog.INFO, 'Recording already exists in Myth Videos') if self.thisJob: self.thisJob.setComment( "Action would result in duplicate entry") return True else: self.log( MythLog.GENERAL, MythLog.INFO, 'No duplication found for ', '%s - %s' % (self.rec.title.encode('utf-8'), self.rec.subtitle.encode('utf-8'))) return False def get_dest(self): if self.type == 'TV': self.vid.filename = self.process_fmt(TVFMT) elif self.type == 'MOVIE': self.vid.filename = self.process_fmt(MVFMT) self.vid.markup._refdat = (self.vid.filename, ) def get_meta(self): import_info = 'Listing only MetaData import complete' metadata = self.rec.exportMetadata() yrInfo = self.rec.getProgram() metadata['year'] = yrInfo.get('year') self.vid.importMetadata(metadata) if self.type == 'MOVIE': grab = VideoGrabber('Movie') results = grab.sortedSearch(self.rec.title) if len(results) > 0: for i in results: if i.year == yrInfo.get( 'year') and i.title == self.rec.get('title'): self.vid.importMetadata(i) match = grab.grabInetref(i.get('inetref')) length = len(match.people) for p in range(length - 2): self.vid.cast.add(match.people[p].get('name')) self.vid.director = match.people[length - 1].get('name') import_info = 'Full MetaData Import complete' else: grab = VideoGrabber('TV') results = grab.sortedSearch(self.rec.title, self.rec.subtitle) if len(results) > 0: for i in results: if i.title == self.rec.get( 'title') and i.subtitle == self.rec.get( 'subtitle'): self.vid.importMetadata(i) match = grab.grabInetref(grab.grabInetref(i.get('inetref'), \ season=i.get('season'),episode=i.get('episode'))) length = len(match.people) for p in range(length - 2): self.vid.cast.add(match.people[p].get('name')) self.vid.director = match.people[length - 1].get('name') import_info = 'Full MetaData Import complete' self.vid.category = self.rec.get('category') self.log(self.log.GENERAL, self.log.INFO, import_info) def get_type(self): if self.rec.seriesid != None and self.rec.programid[:2] != 'MV': self.type = 'TV' self.log(self.log.GENERAL, self.log.INFO, 'Performing TV type migration.') else: self.type = 'MOVIE' self.log(self.log.GENERAL, self.log.INFO, 'Performing Movie type migration.') def process_fmt(self, fmt): # replace fields from viddata ext = '.' + self.rec.basename.rsplit('.', 1)[1] rep = (('%TITLE%', 'title', '%s'), ('%SUBTITLE%', 'subtitle', '%s'), ('%SEASON%', 'season', '%d'), ('%SEASONPAD%', 'season', '%02d'), ('%EPISODE%', 'episode', '%d'), ('%EPISODEPAD%', 'episode', '%02d'), ('%YEAR%', 'year', '%s'), ('%DIRECTOR%', 'director', '%s')) for tag, data, format in rep: if self.vid[data]: fmt = fmt.replace(tag, format % self.vid[data]) else: fmt = fmt.replace(tag, '') # replace fields from program data rep = (('%HOSTNAME%', 'hostname', '%s'), ('%STORAGEGROUP%', 'storagegroup', '%s')) for tag, data, format in rep: data = getattr(self.rec, data) fmt = fmt.replace(tag, format % data) if len(self.vid.genre): fmt = fmt.replace('%GENRE%', self.vid.genre[0].genre) else: fmt = fmt.replace('%GENRE%', '') return fmt + ext def set_job_status(self, status): self.thisJob.setStatus(status) def set_vid_hash(self): self.vid.hash = self.vid.getHash() def update_vid(self): self.vid.update()
# but in new ones: if marker in seen: continue seen[marker] = 1 result.append(item) return result def pendFun(x): out = x.title + "-" if x.subtitle is not None: out += x.subtitle return out def goodRec(x): return x.recgroup <> 'Sports' and x.subtitle is not None and x.title not in titlesIgnore and (x.recstatus == Program.rsWillRecord or x.recstatus == Program.rsConflict) myth = MythBE() mythDB = MythDB() pending = uniq(filter(goodRec, myth.getPendingRecordings()), pendFun) for p in sorted(pending, key=pendFun): # print p.title olds = list(mythDB.searchOldRecorded(title=p.title, subtitle=p.subtitle, recstatus=-3, duplicate=1)) # olds = filter(lambda x: x.programid == "", olds) if len(olds) > 0: print "%s %s (%s) %s" % (p.title, p.subtitle, p.starttime, p.recstatus) for o in olds: if o.programid == "": print "\tNo Old Program Id" if o.programid <> "" and o.programid <> p.programid: # if o.programid <> p.programid:
src = get_dir(filename) if not src: print "Can't find src for %s" % movie.title return dst = d + filename if not simulate: print "before: %s" % datetime.now() print "%-20.20s: moving from %s to %s" % (movie.title,src, dst) if not simulate: shutil.move(src,dst) print "after: %s" % datetime.now() myth = MythBE() input = "" progs = filter(filterMovies, myth.getRecordings()) progs = sorted(progs,cmpFileSize) totalSize = 0 for i,p in enumerate(progs): totalSize += p.filesize print "%2d: %-35.35s (%-5s) %s" % (i,p.title,pretty_filesize(p.filesize),get_dir(p.filename.split("/")[3])) print "Total size: %s" % pretty_filesize(totalSize) input = raw_input("Enter some numbers or 'q' : ") moveMovies(getIds(input),progs)
def getBeObject(databaseObj=getDbObject()): return MythBE(db=databaseObj)
class Recordings(Handler): def __init__(self): self.be = MythBE() self.recs = {} self._events = [self.handleAdd, self.handleDelete, self.handleUpdate] for e in self._events: self.be.registerevent(e) def add(self, rec): # check for duplicates match = (str(rec.chanid), rec.recstartts.isoformat()) if match in self.recs: return # add attributes rec.attr = Attr() ctime = rec.lastmodified.timestamp() rec.attr.st_ctime = ctime rec.attr.st_mtime = ctime rec.attr.st_atime = ctime rec.attr.st_size = rec.filesize rec.attr.st_mode = stat.S_IFREG | 0444 # process name rec.path = rec.formatPath(self.fmt, ' ') # add file self._addCallback(rec) self.recs[match] = rec.attr.st_ino def genAttr(self, rec): attr = Attr() ctime = rec.lastmodified.timestamp() attr.st_ctime = ctime attr.st_mtime = ctime attr.st_atime = ctime attr.st_size = rec.filesize attr.st_mode = stat.S_IFREG | 0444 return attr def getAll(self): for rec in self.be.getRecordings(): self.add(rec) def handleAdd(self, event=None): if event is None: self._reAdd = re.compile( re.escape(static.BACKEND_SEP).\ join(['BACKEND_MESSAGE', 'RECORDING_LIST_CHANGE ADD ' '(?P<chanid>[0-9]*) ' '(?P<starttime>[0-9-]*T[0-9:]*)', 'empty'])) return self._reAdd LOG(LOG.FILE, 'add event received', event) match = self._reAdd.match(event).groups() if match in self.recs: return rec = self.be.getRecording(match[0], match[1]) self.add(rec) def handleDelete(self, event=None): if event is None: self._reDel = re.compile( re.escape(static.BACKEND_SEP).\ join(['BACKEND_MESSAGE', 'RECORDING_LIST_CHANGE DELETE ' '(?P<chanid>[0-9]*) ' '(?P<starttime>[0-9-]*T[0-9:]*)', 'empty'])) return self._reDel LOG(LOG.FILE, 'delete event received', event) match = self._reDel.match(event).groups() if match not in self.recs: return self._deleteCallback(self.recs[match]) del self.recs[match] def handleUpdate(self, event=None): if event is None: self._reUp = re.compile( re.escape(static.BACKEND_SEP).\ join(['BACKEND_MESSAGE', 'UPDATE_FILE_SIZE ' '(?P<chanid>[0-9]*) ' '(?P<starttime>[0-9-]*T[0-9:]*) ' '(?P<size>[0-9]*)', 'empty'])) return self._reUp LOG(LOG.FILE, 'update event received', event) match = self._reUp.match(event) size = match.group(3) match = match.group(1, 2) if match not in self.recs: return inode = self.recs[match] rec = self._inodeCallback(inode) rec.filesize = int(size) rec.attr.st_size = int(size) def setFormat(self, fmt): if '%' not in fmt: LOG(LOG.FILE, 'pulling format from database', 'mythfs.format.%s' % fmt) fmt = self.be.db.settings.NULL['mythfs.format.%s' % fmt] LOG(LOG.FILE, 'using format', fmt) self.fmt = fmt
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
# files from TitanTV, importing them as scheduling rules for MythTV to # record #--------------------------- __title__ = "TitanImport" __author__ = "Raymond Wagner" __version__= "v0.1.0" from MythTV import MythDB, MythBE, Channel, Record, datetime from datetime import timedelta from optparse import OptionParser import lxml.etree as etree DB = MythDB() BE = MythBE(db=DB) tzoff = timedelta(0, int(BE.backendCommand('QUERY_TIME_ZONE').split('[]:[]')[1])) def FindProgram(xmlprog, fuzzy): tvmode = xmlprog.find('tv-mode').text chan = None if tvmode == 'cable': # for cable, require a match of channel and station name for c in Channel.getAllEntries(db=DB): if c.freqid == xmlprog.find('rf-channel').text and \ c.mplexid == 32767 and \ c.callsign == xmlprog.find('station').text: chan = c break else: if not fuzzy:
sendMessage("Could not find mythbackend process!") exit(-1) db = None try: db = MythDB() except: db = None if db == None: sendMessage("Database could not be open or found") exit(-1) be = None try: be = MythBE() except: be = None if be == None: sendMessage("Backend could not be found") exit(-1) if len(be.getRecorderList()) != 2: sendMessage("Tuner list is not what we expected") exit(-1) #if strftime("%S") == str('00') || strftime("%S") == str('30'): # exit(0) for x in be.getRecorderList():
class VIDEO: def __init__(self, opts, jobid=None): # Setup for the job to run if jobid: self.thisJob = Job(jobid) self.chanID = self.thisJob.chanid self.startTime = self.thisJob.starttime self.thisJob.update(status=Job.STARTING) # If no job ID given, must be a command line run else: self.thisJob = jobid self.chanID = opts.chanid self.startTime = opts.startdate + " " + opts.starttime + opts.offset self.opts = opts self.type = "none" self.db = MythDB() self.log = MythLog(module='Myth-Rec-to-Vid.py', db=self.db) # Capture the backend host name self.host = self.db.gethostname() # prep objects self.rec = Recorded((self.chanID,self.startTime), db=self.db) self.log(MythLog.GENERAL, MythLog.INFO, 'Using recording', '%s - %s' % (self.rec.title.encode('utf-8'), self.rec.subtitle.encode('utf-8'))) self.vid = Video(db=self.db).create({'title':'', 'filename':'', 'host':self.host}) self.bend = MythBE(db=self.db) def check_hash(self): self.log(self.log.GENERAL, self.log.INFO, 'Performing copy validation.') srchash = self.bend.getHash(self.rec.basename, self.rec.storagegroup) dsthash = self.bend.getHash(self.vid.filename, 'Videos') if srchash != dsthash: return False else: return True def copy(self): stime = time.time() srcsize = self.rec.filesize htime = [stime,stime,stime,stime] self.log(MythLog.GENERAL|MythLog.FILE, MythLog.INFO, "Copying myth://%s@%s/%s"\ % (self.rec.storagegroup, self.rec.hostname, self.rec.basename)\ +" to myth://Videos@%s/%s"\ % (self.host, self.vid.filename)) srcfp = self.rec.open('r') dstfp = self.vid.open('w') if self.thisJob: self.set_job_status(Job.RUNNING) tsize = 2**24 while tsize == 2**24: tsize = min(tsize, srcsize - dstfp.tell()) dstfp.write(srcfp.read(tsize)) htime.append(time.time()) rate = float(tsize*4)/(time.time()-htime.pop(0)) remt = (srcsize-dstfp.tell())/rate if self.thisJob: self.thisJob.setComment("%02d%% complete - %d seconds remaining" %\ (dstfp.tell()*100/srcsize, remt)) srcfp.close() dstfp.close() self.vid.hash = self.vid.getHash() self.log(MythLog.GENERAL|MythLog.FILE, MythLog.INFO, "Transfer Complete", "%d seconds elapsed" % int(time.time()-stime)) if self.thisJob: self.thisJob.setComment("Complete - %d seconds elapsed" % \ (int(time.time()-stime))) def copy_markup(self, start, stop): for mark in self.rec.markup: if mark.type in (start, stop): self.vid.markup.add(mark.mark, 0, mark.type) def copy_seek(self): for seek in self.rec.seek: self.vid.markup.add(seek.mark, seek.offset, seek.type) def delete_vid(self): self.vid.delete() def delete_rec(self): self.rec.delete() def dup_check(self): self.log(MythLog.GENERAL, MythLog.INFO, 'Processing new file name ', '%s' % (self.vid.filename)) self.log(MythLog.GENERAL, MythLog.INFO, 'Checking for duplication of ', '%s - %s' % (self.rec.title.encode('utf-8'), self.rec.subtitle.encode('utf-8'))) if self.bend.fileExists(self.vid.filename, 'Videos'): self.log(MythLog.GENERAL, MythLog.INFO, 'Recording already exists in Myth Videos') if self.thisJob: self.thisJob.setComment("Action would result in duplicate entry" ) return True else: self.log(MythLog.GENERAL, MythLog.INFO, 'No duplication found for ', '%s - %s' % (self.rec.title.encode('utf-8'), self.rec.subtitle.encode('utf-8'))) return False def get_dest(self): if self.type == 'TV': self.vid.filename = self.process_fmt(TVFMT) elif self.type == 'MOVIE': self.vid.filename = self.process_fmt(MVFMT) self.vid.markup._refdat = (self.vid.filename,) def get_meta(self): import_info = 'Listing only MetaData import complete' metadata = self.rec.exportMetadata() yrInfo = self.rec.getProgram() metadata['year'] = yrInfo.get('year') self.vid.importMetadata(metadata) if self.type == 'MOVIE': grab = VideoGrabber('Movie') results = grab.sortedSearch(self.rec.title) if len(results) > 0: for i in results: if i.year == yrInfo.get('year') and i.title == self.rec.get('title'): self.vid.importMetadata(i) match = grab.grabInetref(i.get('inetref')) length = len(match.people) for p in range(length-2): self.vid.cast.add(match.people[p].get('name')) self.vid.director = match.people[length - 1].get('name') import_info = 'Full MetaData Import complete' else: grab = VideoGrabber('TV') results = grab.sortedSearch(self.rec.title, self.rec.subtitle) if len(results) > 0: for i in results: if i.title == self.rec.get('title') and i.subtitle == self.rec.get('subtitle'): self.vid.importMetadata(i) match = grab.grabInetref(grab.grabInetref(i.get('inetref'), \ season=i.get('season'),episode=i.get('episode'))) length = len(match.people) for p in range(length-2): self.vid.cast.add(match.people[p].get('name')) self.vid.director = match.people[length - 1].get('name') import_info = 'Full MetaData Import complete' self.vid.category = self.rec.get('category') self.log(self.log.GENERAL, self.log.INFO, import_info) def get_type(self): if self.rec.seriesid != None and self.rec.programid[:2] != 'MV': self.type = 'TV' self.log(self.log.GENERAL, self.log.INFO, 'Performing TV type migration.') else: self.type = 'MOVIE' self.log(self.log.GENERAL, self.log.INFO, 'Performing Movie type migration.') def process_fmt(self, fmt): # replace fields from viddata ext = '.'+self.rec.basename.rsplit('.',1)[1] rep = ( ('%TITLE%','title','%s'), ('%SUBTITLE%','subtitle','%s'), ('%SEASON%','season','%d'), ('%SEASONPAD%','season','%02d'), ('%EPISODE%','episode','%d'), ('%EPISODEPAD%','episode','%02d'), ('%YEAR%','year','%s'), ('%DIRECTOR%','director','%s')) for tag, data, format in rep: if self.vid[data]: fmt = fmt.replace(tag,format % self.vid[data]) else: fmt = fmt.replace(tag,'') # replace fields from program data rep = ( ('%HOSTNAME%', 'hostname', '%s'), ('%STORAGEGROUP%','storagegroup','%s')) for tag, data, format in rep: data = getattr(self.rec, data) fmt = fmt.replace(tag,format % data) if len(self.vid.genre): fmt = fmt.replace('%GENRE%',self.vid.genre[0].genre) else: fmt = fmt.replace('%GENRE%','') return fmt+ext def set_job_status(self, status): self.thisJob.setStatus(status) def set_vid_hash(self): self.vid.hash = self.vid.getHash() def update_vid(self): self.vid.update()
magic = datetime(2009,9,1,0,0,0,0,a.starttime.tzinfo) # return a.recgroup == "Movies" and (float(a.channum) < 500 or getCommMethod(a.chanid) <> -2 or a.starttime < magic) return a.recgroup == "Movies" and (getCommMethod(a.chanid) <> -2 or a.starttime < magic) def getChannel(chanid): for c in channels: if c.chanid == chanid: return c def getCommMethod(chanid): c = getChannel(chanid); return c.commmethod myth = MythBE() channels = list(Channel.getAllEntries()) pending = filter(filterPMovies, myth.getPendingRecordings()) all_movies = filter(filterPMovies, myth.getRecordings()) recorded = filter(filterRMovies, all_movies) for r in sorted(recorded, key=lambda a: a.title): def r_filter(a): return a.title == r.title my_rec = filter(r_filter, all_movies) def p_filter(a): for b in my_rec: if a.title <> b.title:
"The Big Sleep" : 2, "Shaft" : 2, "The Wild One" : 2, "The Postman Always Rings Twice" : 2, "Witness for the Prosecution" : 2 } def findDupes(orig): seen = {} result = [] for item in orig: seen[item.title] = seen.get(item.title,0) + 1 return dict((title,count) for title,count in seen.iteritems() if count > allowedDupes.get(title,1)) myth = MythBE() mythDB = MythDB() # get all recordings recs = myth.getRecordings() # limit to only movies that are currently recorded recs = filter(lambda x: x.recgroup == "Movies", recs) # limit to only ones that are recorded multiple times recs = findDupes(recs) # sort by title recs = sorted(recs.iteritems(), key=operator.itemgetter(0)) for title,count in recs: