class test_Methodheap_MythDB_006(unittest.TestCase): """Test method 'searchRecord' from MythTV.MythDB().""" @classmethod def setUpClass(cls): # get the global test environment global TestEnv cls.testenv = TestEnv def setUp(self): self.mydb = MythDB() def test_Methodheap_MythDB_006_searchRecord_01(self): """Test 'searchRecord' method from MythTV.MythDB() using 'title' and 'chanid'. """ programs = self.mydb.searchRecord( chanid = self.testenv['UPCHANID'] , title = self.testenv['UPTITLE']) program01 = next(programs) self.assertTrue(isinstance(program01, Record)) def test_Methodheap_MythDB_006_searchRecord_02(self): """Test 'searchRecord' method from MythTV.MythDB() using 'title' and 'chanid'. """ programs = self.mydb.searchRecord( chanid = self.testenv['UPCHANID'] , title = self.testenv['UPTITLE']) program02 = next(programs) self.assertTrue(isinstance(program02, Record)) # check if accessing a property works self.assertTrue(self.testenv['UPTITLEFUZZY'] in program02.title)
def test_Dataheap_VideoGrabber_002_importMetadata_02(self): """Test 'VideoGrabber.grabInetref' method and 'Video.importMetadata' methods using predefined values. See 'mythvidexport.py' as well. See MythtTV trac tickets #12243 and #12245. Note: Use a TV series with season and episode. """ from MythTV import MythDB, Video, VideoGrabber with add_log_flags(): self.mydb = MythDB() alf_metadata = { u'title' : u'Alf', u'subtitle' : u'Wedding Bell Blues', u'season' : 2, u'episode' : 4, u'inetref' : '78020'} grab = VideoGrabber('TV', lang='en', db=self.mydb) metadata = grab.grabInetref( alf_metadata['inetref'], alf_metadata['season'], alf_metadata['episode']) # print(metadata.collectionref) # ---> '78020' self.assertEqual(metadata.collectionref, alf_metadata[u'inetref']) # run the grabber again, but based on a 'VideoMetadata' object new_metadata = grab.grabInetref(metadata) # check if collected metadata are the equal self.assertEqual(new_metadata, metadata)
class test_Methodheap_MythDB_005(unittest.TestCase): """Test method 'searchGuide' from MythTV.MythDB().""" @classmethod def setUpClass(cls): # get the global test environment global TestEnv cls.testenv = TestEnv def setUp(self): self.mydb = MythDB() def test_Methodheap_MythDB_005_searchGuide_01(self): """Test 'searchGuide' method from MythTV.MythDB(). """ guide = next(self.mydb.searchGuide()) self.assertTrue(isinstance(guide, Guide)) # test '__repr__' and '__str__' print() print(repr(guide)) # print(str(guide)) # XXX this does not work def test_Methodheap_MythDB_005_searchGuide_02(self): """Test 'searchGuide' method from MythTV.MythDB() using fuzzy title. """ guide_fuzz = next(self.mydb.searchGuide( fuzzytitle = \ self.testenv['UPTITLEFUZZY']) ) self.assertTrue(isinstance(guide_fuzz, Guide)) # check if accessing a property works self.assertTrue(self.testenv['UPTITLEFUZZY'] in guide_fuzz.title)
def test_Dataheap_VideoGrabber_001_sortedSearch_01(self): """Test 'sortedSearch' method from MythTV.VideoGrabber using predefined values. """ from MythTV import MythDB, RecordedArtwork, Video, VideoGrabber with add_log_flags(): self.mydb = MythDB() title = self.testenv['VIDTITLE_DE'] cast = self.testenv['VIDCAST_DE'] inetrefstr = self.testenv['VIDINETREF_DE'] lang = self.testenv['VIDLANGUAGE_DE'] # remove grabber from inetref: try: inetref = inetrefstr.split('_')[-1] except IndexError: inetref = inetrefstr grab = VideoGrabber("Movie", lang=lang, db=self.mydb) metadatalist = grab.sortedSearch(title, subtitle=None, tolerance=2) inetref_found = False for m in metadatalist: if (m.inetref == inetref): inetref_found = True break self.assertTrue(inetref_found)
def test_Dataheap_VideoGrabber_001_search_01(self): """Test 'search' method from MythTV.VideoGrabber using 'searchVideos'. """ from MythTV import MythDB, RecordedArtwork, Video, VideoGrabber with add_log_flags(): self.mydb = MythDB() title = self.testenv['VIDTITLE'] cast = self.testenv['VIDCAST'] inetrefstr = self.testenv['VIDINETREF'] lang = self.testenv['VIDLANGUAGE'] # remove grabber from inetref: try: inetref = inetrefstr.split('_')[-1] except IndexError: inetref = inetrefstr vids = self.mydb.searchVideos( title = title ) vid = next(vids) # print("%s : %s" %(vid.title, type(vid.title))) self.assertTrue(isinstance(vid, Video)) grab = VideoGrabber("Movie", lang = lang, db = self.mydb) metadatalistgen = grab.search(vid.title, subtitle=None, tolerance=1) mlist = list(metadatalistgen) inetref_found = False for m in mlist: if (m.inetref == inetref): inetref_found = True break self.assertTrue(inetref_found)
def test_repr_002_01(self): """ Test '__repr__' and '__str__' methods from dataheap.py """ chanid = self.testenv['RECCHANID'] starttimemyth = self.testenv['RECSTARTTIMEMYTH'] title = self.testenv['RECTITLE'] c_s = (chanid, starttimemyth) db = MythDB() # Recorded class r = Recorded(c_s, db=db) print() print(repr(r)) print(str(r)) # Markup table m = r.markup print() print(repr(m)) print(str(m)) # one entry of the marlup table: print() print(repr(m[0])) print(str(m[0])) # one value of a single entry: print() print(repr(m[0].mark)) print(str(m[0].mark)) print() print(repr(m[0].data)) print(str(m[0].data)) # Artwork a = r.artwork print() print(repr(a)) print(str(a)) # Coverart of an Artwork ac = a.coverart print() print(repr(ac)) print(str(ac)) # Program of the recorded entry: prgrm = r.getRecordedProgram() print() print(repr(prgrm)) print(str(prgrm)) # OldRecorded of the recorded entry: oldrecs = db.searchOldRecorded(chanid=chanid, title=title) oldrec = next(oldrecs) print() print(repr(oldrec)) print(str(oldrec))
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 main(): db = MythDB() jobs = db.searchJobs() activejob = 0 for job in jobs: if job.status in [Job.ABORTING, Job.ERRORING, Job.PAUSED, Job.PENDING, \ Job.QUEUED, Job.RETRY, Job.RUNNING, Job.STARTING, Job.STOPPING]: activejob = 1 break sys.exit(activejob)
def __init__(self, opts, jobid=None): if jobid: self.job = Job(jobid) self.chanid = self.job.chanid self.starttime = self.job.starttime self.job.update(status=Job.STARTING) else: self.job = None self.chanid = opts.chanid self.starttime = opts.starttime self.opts = opts self.db = MythDB() self.log = MythLog(module='mythvidexport.py', db=self.db) # load setting strings self.get_format() # 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': gethostname() }) # process data self.get_meta() self.get_dest() # bug fix to work around limitation in the bindings where DBDataRef classes # are mapped to the filename at time of Video element creation. since the # filename is specified as blank when the video is created, the markup # handler is not properly initialized self.vid.markup._refdat = (self.vid.filename, ) # save file self.copy() if opts.seekdata: self.copy_seek() if opts.skiplist: self.copy_markup(static.MARKUP.MARK_COMM_START, static.MARKUP.MARK_COMM_END) if opts.cutlist: self.copy_markup(static.MARKUP.MARK_CUT_START, static.MARKUP.MARK_CUT_END) self.vid.update() # delete old file if opts.delete: self.rec.delete()
def test_repr_001_03(self): """ Test '__repr__' and '__str__' methods from MythTV.MythDB.StorageGroup """ db = MythDB() sgi = db.getStorageGroup('LiveTV') sg = next(sgi) print() print(repr(sg)) print(str(sg))
def setUp(self): # create a file with this name if it does not exist self.frtitle = self.testenv["VIDFRTITLE"] # Le Dernier Métro" self.frfilename = self.testenv["VIDFRFILENAME"] self.frpath = self.testenv["VIDFRPATH"] self.frfullpath = os.path.join(self.frpath, self.frfilename) with add_log_flags(): self.mydb = MythDB() self.master_backend_ip = self.mydb.settings['NULL']['MasterServerIP'] shcmd = 'head -c 10M </dev/urandom > "%s"' % self.frfullpath cmd = "ssh mythtv@%s '%s'" % (self.master_backend_ip, shcmd) print(cmd) self.result = subprocess.call(cmd, shell=True) # will be checked later
class test_Methodheap_MythDB_002(unittest.TestCase): """Test method 'searchOldRecorded' from MythTV.MythDB().""" @classmethod def setUpClass(cls): # get the global test environment global TestEnv cls.testenv = TestEnv def setUp(self): self.mydb = MythDB() def test_Methodheap_MythDB_002_searchOldRecorded_01(self): """Test 'searchOldRecorded' method from MythTV.MythDB() via 'chanid'. """ recs = self.mydb.searchOldRecorded(chanid = self.testenv['RECCHANID']) rec02 = next(recs) self.assertTrue(isinstance(rec02, OldRecorded)) def test_Methodheap_MythDB_002_searchOldRecorded_02(self): """Test 'searchOldRecorded' method from MythTV.MythDB() via 'chanid', 'title'. """ recs = self.mydb.searchOldRecorded( chanid = self.testenv['RECCHANID'] , title = self.testenv['RECTITLE'] ) rec02 = next(recs) self.assertTrue(isinstance(rec02, OldRecorded)) # check if accessing a property works self.assertTrue(rec02.recordid == int(self.testenv['RECRECORDID'])) def test_Methodheap_MythDB_002_searchOldRecorded_03(self): """Test 'searchOldRecorded' method from MythTV.MythDB() via 'chanid' and 'starttime'- """ # get the 'progstart' time of the 'recorded' table: rec = Recorded((int(self.testenv['RECCHANID']), int(self.testenv['RECSTARTTIMEMYTH'])), db = self.mydb) starttime_utc_iso = rec.progstart.utcisoformat() +"Z" # use 'progstart' as 'starttime' in the oldrecored table, like '2019-03-05T12:55:00Z': recs = self.mydb.searchOldRecorded( chanid = self.testenv['RECCHANID'] , starttime = starttime_utc_iso ) rec02 = next(recs) self.assertTrue(isinstance(rec02, OldRecorded)) # check if accessing a property works self.assertTrue(rec02.recordid == int(self.testenv['RECRECORDID']))
def test_Dataheap_VideoGrabber_001_grabInetref_01(self): """Test 'grabInetref' and 'toXML' methods from MythTV.VideoGrabber using predefined values. """ from MythTV import MythDB, RecordedArtwork, Video, VideoGrabber with add_log_flags(): self.mydb = MythDB() title = self.testenv['VIDTITLE_DE'] cast = self.testenv['VIDCAST_DE'] inetrefstr = self.testenv['VIDINETREF_DE'] lang = self.testenv['VIDLANGUAGE_DE'] # remove grabber from inetref: try: inetref = inetrefstr.split('_')[-1] except IndexError: inetref = inetrefstr grab = VideoGrabber("Movie", lang=lang, db=self.mydb) metadatalist = grab.sortedSearch(title, subtitle=None, tolerance=2) details = grab.grabInetref(metadatalist[0].inetref) # details has lists of dicts for # 'certifications', 'categories', 'countries', 'studios', 'people', 'images' names = [n.name for n in details.people] self.assertTrue(cast in names) tree = etree.XML(u'<metadata></metadata>') tree.append(details.toXML()) xml_str = etree.tostring ( tree , pretty_print = True , xml_declaration = True , encoding = "UTF-8" , standalone = "yes" ) xml_file = open("/tmp/details.xml", "wb") xml_file.write(xml_str) xml_file.close() # read xml file and check for cast root = etree.parse(r'/tmp/details.xml').getroot() cast_found = False for name in root.findall('item/people/person'): if (name.attrib['name'] == cast): cast_found = True self.assertTrue(cast_found)
def findrecs(title): db = MythDB() if title is None: print 'title is the only supported query right now...' return None recs = db.searchRecorded(title=title) tobedone = [] for rec in recs: #print 'Checking rec:' #print rec if rec.basename.endswith('mpg'): tobedone.append(rec) else: print 'Skipping non mpg: %s' % rec.basename return tobedone
class test_Methodheap_MythDB_102(unittest.TestCase): """Test method 'getStorageGroup' from MythTV.MythDB() inherited from DBCache.""" def setUp(self): self.mydb = MythDB() def test_Methodheap_MythDB_102_getStorageGroup_01(self): """Test 'getStorageGroup' method from MythTV.MythDB() inherited from DBCache. """ sgroups = self.mydb.getStorageGroup(hostname = self.mydb.getMasterBackend()) sg = next(sgroups) sgname = sg[u'groupname'] #print(sgname, type(sgname)) self.assertGreater(len(sgname), 0)
class MythEventListener(BECache): # based on BEEventMonitor in the 0.25 Python bindings def __init__(self, callback = None, backend=None, blockshutdown=False, db=None): # the 3rd parameter is for "events" (e.g. true). super(MythEventListener, self).__init__(backend, blockshutdown, True, db) self.callback = callback self.db = MythDB() def _neweventconn(self): # this is called by the Myth python bindings once the backend/db has # been identified # level is one of: 3=system only, 2=generic only, 1=both, 0=none return BEEventConnection( self.host, self.port, self.db.gethostname(), level=3 ) def _listhandlers(self): # this is called by the Myth python bindings, and is used to register # the eventMonitor method below as a handler of events return [ self.eventMonitor ] def eventMonitor(self, event=None): # the event handler method. It needs to return a regex to match its # handled events, if called without an event. Otherwise, it should # handle the event. if event is None: return re.compile('BACKEND_MESSAGE') self.callback(event)
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 __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 getjob(jobid=None, chanid=None, starttime=None): db = MythDB() if jobid: job = Job(jobid, db=db) chanid = job.chanid starttime = job.starttime return Recorded((chanid, starttime), db=db)
def initializeMythDB(self): ''' Import the MythTV database bindings return nothing ''' try: from MythTV import MythDB, MythLog, MythError try: '''Create an instance of each: MythDB ''' MythLog._setlevel( 'none' ) # Some non option -M cannot have any logging on stdout self.mythdb = MythDB() except MythError as e: sys.stderr.write('\n! Error - %s\n' % e.args[0]) filename = os.path.expanduser("~") + '/.mythtv/config.xml' if not os.path.isfile(filename): sys.stderr.write( '\n! Error - A correctly configured (%s) file must exist\n' % filename) else: sys.stderr.write( '\n! Error - Check that (%s) is correctly configured\n' % filename) sys.exit(1) except Exception as e: sys.stderr.write( "\n! Error - Creating an instance caused an error for one of: MythDB. error(%s)\n" % e) sys.exit(1) except Exception as e: sys.stderr.write( "\n! Error - MythTV python bindings could not be imported. error(%s)\n" % e) sys.exit(1)
class test_Dataheap_Video_001(unittest.TestCase): """Test class 'Videos' from dataheap. """ @classmethod def setUpClass(cls): # get the global test environment global TestEnv cls.testenv = TestEnv def setUp(self): self.mydb = MythDB() def test_Dataheap_Video_001_getHash_01(self): """Test 'getHash' method from MythTV.Video using 'searchVideos'. """ vids = self.mydb.searchVideos(title=self.testenv['VIDTITLE'], cast=self.testenv['VIDCAST']) vid = next(vids) #print("%s : %s" %(vid.title, type(vid.title))) self.assertTrue(isinstance(vid, Video)) vid_hash = vid.getHash() # check if retval is hexadecimal: hex_set = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' } self.assertTrue(set(vid_hash.lower()).issubset(hex_set)) # test '__repr__' and '__str__' print() print(repr(vid)) print(str(vid))
def __init__(self, callback = None, backend=None, blockshutdown=False, db=None): # the 3rd parameter is for "events" (e.g. true). super(MythEventListener, self).__init__(backend, blockshutdown, True, db) self.callback = callback self.db = MythDB()
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 test_repr_001_02(self): """ Test '__repr__' and '__str__' methods from MythTV.MythDB.settings """ db = MythDB() s = db.settings print() print(repr(s)) print(str(s)) #self.assertTrue(u'DBSchemaVer' in str(s)) sn = db.settings['NULL'] print() print(repr(sn)) print(str(sn)) ip = db.settings.NULL.MasterServerIP print() print(repr(ip)) print(str(ip)) fn = db.settings['%s' % self.testenv['FRONTENDNAME']] print() print(repr(fn)) print(str(fn)) fnp = fn.NetworkControlPort print() print(repr(fnp)) print(str(fnp))
def link_all(): db = MythDB() channels = get_channels(db) if opts.live: recs = db.searchRecorded(livetv=True) else: recs = db.searchRecorded() targets = {} for rec in recs: details = get_link_details(rec, channels) if details is None: continue source, dest, destfile = details if dest not in targets: targets[dest] = {} targets[dest][destfile] = source for (path, dirs, files) in os.walk(opts.dest, topdown=False): dir = path.split("/")[-1] if dir == "": continue if dir not in targets: for fname in files: if not os.path.islink(path + "/" + fname): raise Exception("Found non link - " + path + "/" + fname) os.unlink(path + "/" + fname) os.rmdir(path) continue else: for fname in files: print dir, fname if dir not in targets or fname not in targets[dir]: if not os.path.islink(path + "/" + fname): raise Exception("Found non link - " + path + "/" + fname) os.unlink(path + "/" + fname) else: del targets[dir][fname] print targets[dir] if len(targets[dir]) == 0: del targets[dir] for dir in targets: if not os.path.exists(opts.dest + dir): os.mkdir(opts.dest + dir) for fname in targets[dir]: os.symlink(targets[dir][fname], opts.dest + "/" + dir + "/" + fname)
def link_all(): # removing old content for path,dirs,files in os.walk(opts.dest, topdown=False): for fname in files: tmppath = os.path.join(path, fname) if not os.path.islink(tmppath): raise Exception('Non-link file found in destination path.') os.unlink(tmppath) os.rmdir(path) db = MythDB() if opts.live: recs = db.searchRecorded(livetv=True) else: recs = db.searchRecorded() for rec in recs: gen_link(rec)
class test_Methodheap_MythDB_003(unittest.TestCase): """Test method 'searchArtwork' from MythTV.MythDB().""" @classmethod def setUpClass(cls): # get the global test environment global TestEnv cls.testenv = TestEnv def setUp(self): self.mydb = MythDB() def test_Methodheap_MythDB_003_searchArtwork_01(self): """Test 'searchArtwork' method from MythTV.MythDB(). """ artwork = next(self.mydb.searchArtwork()) self.assertTrue(isinstance(artwork, RecordedArtwork)) def test_Methodheap_MythDB_003_searchArtwork_02(self): """Test 'searchArtwork' method from MythTV.MythDB(), given 'chanid', 'starttime'. """ # get the sattime in UTC and ISO format: like '2019-03-05T12:51:00Z' starttime_utc_iso = datetime.duck(self.testenv['RECSTARTTIMEMYTH']).utcisoformat() +"Z" artworks = self.mydb.searchArtwork( chanid = self.testenv['RECCHANID'] , starttime = starttime_utc_iso ) artwork = next(artworks) self.assertTrue(isinstance(artwork, RecordedArtwork)) # check if accessing a property works self.assertTrue(artwork.coverart.data == "%s_coverart.jpg" % self.testenv['RECINETREF']) def test_Methodheap_MythDB_003_searchArtwork_03(self): """Test 'searchArtwork' method from MythTV.MythDB() by given inetref. """ artworks = self.mydb.searchArtwork( inetref = self.testenv['RECINETREF']) artwork = next(artworks) self.assertTrue(isinstance(artwork, RecordedArtwork)) # check if accessing a property works self.assertTrue(artwork.coverart.data == "%s_coverart.jpg" % self.testenv['RECINETREF'])
def store_format(): i = iter(sys.argv) while i.next() != '--storeformat': pass tag = i.next() fmt = i.next() db = MythDB() db.settings.NULL['mythfs.format.%s' % tag] = fmt sys.exit()
def __init__(self, opts, jobid=None): if jobid: self.job = Job(jobid) self.chanid = self.job.chanid self.starttime = self.job.starttime self.job.update(status=3) else: self.job = None self.chanid = opts.chanid self.starttime = opts.starttime self.opts = opts self.db = MythDB() self.log = MythLog(module='mythvidexport.py', db=self.db) # load setting strings self.get_format() # prep objects self.rec = Recorded((self.chanid, self.starttime), db=self.db) self.log(MythLog.IMPORTANT, 'Using recording', '%s - %s' % (self.rec.title, self.rec.subtitle)) self.vid = Video(db=self.db).create({ 'title': '', 'filename': '', 'host': gethostname() }) # process data self.get_meta() self.get_dest() # save file self.copy() if opts.seekdata: self.copy_seek() if opts.skiplist: self.copy_markup(static.MARKUP.MARK_COMM_START, static.MARKUP.MARK_COMM_END) if opts.cutlist: self.copy_markup(static.MARKUP.MARK_CUT_START, static.MARKUP.MARK_CUT_END) self.vid.update()
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))
class test_Methodheap_MythDB_004(unittest.TestCase): """Test method 'searchJobs' from MythTV.MythDB().""" def setUp(self): self.mydb = MythDB() def test_Methodheap_MythDB_004_searchJobs_01(self): """Test 'searchJobs' method from MythTV.MythDB(). """ jobs = next(self.mydb.searchJobs()) self.assertTrue(isinstance(jobs, Job))
def print_formats(): db = MythDB() print ' Label Format ' print ' ----- ------ ' with db as cursor: cursor.execute( """SELECT value,data FROM settings WHERE value like 'mythfs.format.%'""" ) for lbl, fmt in cursor.fetchall(): lbl = lbl[14:] print '%s %s' % (lbl.center(16), fmt) sys.exit()
def test_Dataheap_VideoGrabber_002_importMetadata_01(self): """Test 'VideoGrabber.grabInetref' method and 'Video.importMetadata' methods using predefined values. See 'mythvidexport.py' as well. See MythtTV trac tickets #12243 and #12245. Note: Use a video with French accents in title and cast. """ from MythTV import MythDB, Video, VideoGrabber with add_log_flags(): title = u"Le Dernier Métro" castlist = [u"Catherine Deneuve", u"Gérard Depardieu", u"Andréa Ferréol"] inetstr = "tmdb3.py_1716" lang = "fr" filename = title + u".mkv" # create a new Video instance self.mydb = MythDB() vid = Video(db=self.mydb).create ({'title' : title, 'filename': filename, 'host' : self.mydb.getMasterBackend()}) # grab details from www.themoviedb.org grab = VideoGrabber('MOVIE', lang=lang, db=self.mydb) metadata = grab.grabInetref(inetstr) #print(type(metadata)) --> <class 'MythTV.system.VideoMetadata'> # import metadata into Video instance vid.importMetadata(metadata) #print(vid.cast[1].cast) ### ---> Gérard Depardieu # create a list of cast members from metadata vidcastmembers = [c.cast for c in vid.cast] # check for members in cast list self.assertTrue(self.is_sublist(castlist, vidcastmembers)) # delete previously created Video object in database vid.delete() #print(metadata.inetref) # --> 1716 self.assertEqual(metadata.inetref, inetstr.split('_')[-1]) # run the grabber again, but based on a 'VideoMetadata' object new_metadata = grab.grabInetref(metadata) # check if collected metadata are the equal self.assertEqual(new_metadata, metadata)
def __init__(self, opts, jobid=None): if jobid: self.job = Job(jobid) self.chanid = self.job.chanid self.starttime = self.job.starttime self.job.update(status=Job.STARTING) else: self.job = None self.chanid = opts.chanid self.starttime = opts.starttime self.opts = opts self.db = MythDB() self.log = MythLog(module='mythvidexport.py', db=self.db) # load setting strings self.get_format() # 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':gethostname()}) # process data self.get_meta() self.get_dest() # bug fix to work around limitation in the bindings where DBDataRef classes # are mapped to the filename at time of Video element creation. since the # filename is specified as blank when the video is created, the markup # handler is not properly initialized self.vid.markup._refdat = (self.vid.filename,) # save file self.copy() if opts.seekdata: self.copy_seek() if opts.skiplist: self.copy_markup(static.MARKUP.MARK_COMM_START, static.MARKUP.MARK_COMM_END) if opts.cutlist: self.copy_markup(static.MARKUP.MARK_CUT_START, static.MARKUP.MARK_CUT_END) self.vid.update() # delete old file if opts.delete: self.rec.delete()
def __init__(self, opts, jobid=None): if jobid: self.job = Job(jobid) self.chanid = self.job.chanid self.starttime = self.job.starttime self.job.update(status=3) else: self.job = None self.chanid = opts.chanid self.starttime = opts.starttime self.opts = opts self.db = MythDB() self.log = MythLog(module='mythvidexport.py', db=self.db) # load setting strings self.get_format() # prep objects self.rec = Recorded((self.chanid, self.starttime), db=self.db) self.log(MythLog.IMPORTANT, 'Using recording', '%s - %s' % (self.rec.title, self.rec.subtitle)) self.vid = Video(db=self.db).create({'title':'', 'filename':'', 'host':gethostname()}) # process data self.get_meta() self.get_dest() # kludgy fix for issue with altered filenames in the bindings self.vid.markup._refdat = (self.vid.filename,) # save file self.copy() if opts.seekdata: self.copy_seek() if opts.skiplist: self.copy_markup(static.MARKUP.MARK_COMM_START, static.MARKUP.MARK_COMM_END) if opts.cutlist: self.copy_markup(static.MARKUP.MARK_CUT_START, static.MARKUP.MARK_CUT_END) self.vid.update()
break except EOFError: break except MythError: print "Remote side closed connection..." break except ValueError: # assume an frontend stalled, opening a file pass except: raise if __name__ == '__main__': if len(sys.argv) == 2: try: db = MythDB() frontend = db.getFrontend(sys.argv[1]) wrapper(main) except socket.timeout: print "Could not connect to "+sys.argv[1] pass except TypeError: print sys.argv[1]+" does not exist" pass except KeyboardInterrupt: sys.exit() except: raise else: print "Please choose from the following available frontends:" frontends = None
from MythTV import MythDB, Record my_myth_db = MythDB(args=(('DBHostName', 'localhost'), ('DBName', 'mythconverg'), ('DBUserName', 'root'), ('DBPassword', ''), ('DBPort', '3306'))) shows = my_myth_db.searchGuide(category="Special") for x in shows: x.record(Record.kFindOneRecord)
class VIDEO: def __init__(self, opts, jobid=None): if jobid: self.job = Job(jobid) self.chanid = self.job.chanid self.starttime = self.job.starttime self.job.update(status=Job.STARTING) else: self.job = None self.chanid = opts.chanid self.starttime = opts.starttime self.opts = opts self.db = MythDB() self.log = MythLog(module='mythvidexport.py', db=self.db) # load setting strings self.get_format() # 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, self.rec.subtitle)) self.vid = Video(db=self.db).create({'title':'', 'filename':'', 'host':gethostname()}) # process data self.get_meta() self.get_dest() # bug fix to work around limitation in the bindings where DBDataRef classes # are mapped to the filename at time of Video element creation. since the # filename is specified as blank when the video is created, the markup # handler is not properly initialized self.vid.markup._refdat = (self.vid.filename,) # save file self.copy() if opts.seekdata: self.copy_seek() if opts.skiplist: self.copy_markup(static.MARKUP.MARK_COMM_START, static.MARKUP.MARK_COMM_END) if opts.cutlist: self.copy_markup(static.MARKUP.MARK_CUT_START, static.MARKUP.MARK_CUT_END) self.vid.update() # delete old file if opts.delete: self.rec.delete() def get_format(self): host = self.db.gethostname() # TV Format if self.opts.tformat: self.tfmt = self.opts.tformat elif self.db.settings[host]['mythvideo.TVexportfmt']: self.tfmt = self.db.settings[host]['mythvideo.TVexportfmt'] else: self.tfmt = 'Television/%TITLE%/Season %SEASON%/'+\ '%TITLE% - S%SEASON%E%EPISODEPAD% - %SUBTITLE%' # Movie Format if self.opts.mformat: self.mfmt = self.opts.mformat elif self.db.settings[host]['mythvideo.MOVIEexportfmt']: self.mfmt = self.db.settings[host]['mythvideo.MOVIEexportfmt'] else: self.mfmt = 'Movies/%TITLE%' # Generic Format if self.opts.gformat: self.gfmt = self.opts.gformat elif self.db.settings[host]['mythvideo.GENERICexportfmt']: self.gfmt = self.db.settings[host]['mythvideo.GENERICexportfmt'] else: self.gfmt = 'Videos/%TITLE%' def get_meta(self): self.vid.hostname = self.db.gethostname() if self.rec.inetref: # good data is available, use it if self.rec.season is not None: self.log(self.log.GENERAL, self.log.INFO, 'Performing TV export with local data.') self.type = 'TV' else: self.log(self.log.GENERAL, self.log.INFO, 'Performing Movie export with local data.') self.type = 'MOVIE' metadata = self.rec.exportMetadata() elif self.opts.listingonly: # force use of local data if self.rec.subtitle: self.log(self.log.GENERAL, self.log.INFO, 'Forcing TV export with local data.') self.type = 'TV' else: self.log(self.log.GENERAL, self.log.INFO, 'Forcing Movie export with local data.') self.type = 'MOVIE' metadata = self.rec.exportMetadata() else: if self.rec.subtitle: # subtitle exists, assume tv show self.type = 'TV' self.log(self.log.GENERAL, self.log.INFO, 'Attempting TV export.') grab = VideoGrabber(self.type) match = grab.sortedSearch(self.rec.title, self.rec.subtitle) else: # assume movie self.type = 'MOVIE' self.log(self.log.GENERAL, self.log.INFO, 'Attempting Movie export.') grab = VideoGrabber(self.type) match = grab.sortedSearch(self.rec.title) if len(match) == 0: # no match found self.log(self.log.GENERAL, self.log.INFO, 'Falling back to generic export.') self.type = 'GENERIC' metadata = self.rec.exportMetadata() elif (len(match) > 1) & (match[0].levenshtein > 0): # multiple matches found, and closest is not exact self.vid.delete() raise MythError('Multiple metadata matches found: '\ +self.rec.title) else: self.log(self.log.GENERAL, self.log.INFO, 'Importing content from', match[0].inetref) metadata = grab.grabInetref(match[0]) self.vid.importMetadata(metadata) self.log(self.log.GENERAL, self.log.INFO, 'Import complete') def get_dest(self): if self.type == 'TV': self.vid.filename = self.process_fmt(self.tfmt) elif self.type == 'MOVIE': self.vid.filename = self.process_fmt(self.mfmt) elif self.type == 'GENERIC': self.vid.filename = self.process_fmt(self.gfmt) def process_fmt(self, fmt): # replace fields from viddata #print self.vid.data 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) # fmt = fmt.replace('%CARDID%',self.rec.cardid) # fmt = fmt.replace('%CARDNAME%',self.rec.cardid) # fmt = fmt.replace('%SOURCEID%',self.rec.cardid) # fmt = fmt.replace('%SOURCENAME%',self.rec.cardid) # fmt = fmt.replace('%CHANNUM%',self.rec.channum) # fmt = fmt.replace('%CHANNAME%',self.rec.cardid) if len(self.vid.genre): fmt = fmt.replace('%GENRE%',self.vid.genre[0].genre) else: fmt = fmt.replace('%GENRE%','') # if len(self.country): # fmt = fmt.replace('%COUNTRY%',self.country[0]) # else: # fmt = fmt.replace('%COUNTRY%','') return fmt+ext 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') 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 copy_seek(self): for seek in self.rec.seek: self.vid.markup.add(seek.mark, seek.offset, seek.type) 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)
#!/usr/bin/env python from MythTV import MythDB from datetime import datetime, timedelta MAXAGE=365 # number of days db = MythDB() recs = db.searchRecorded(custom=(('starttime<%s', datetime.now()-timedelta(MAXAGE)),)) if recs is None: print 'No old recordings to delete' else: for rec in recs: if rec.subtitle: print 'Deleting "%s: %s - %s"' % (rec.endtime, rec.title, rec.subtitle) else: print 'Deleting %s: "%s"' % (rec.endtime, rec.title) rec.delete()
help="""Specify that LiveTV recordings are to be linked as well. Default is to only process links for scheduled recordings.""") parser.add_option("--format", action="store", dest="format", help="""Specify the output format to be used to generate link paths.""") parser.add_option("--underscores", action="store", dest="underscores", help="""Replace whitespace in filenames with underscore characters.""") parser.add_option('-v', '--verbose', action='store', type='string', dest='verbose', help='Verbosity level') opts, args = parser.parse_args() if opts.dest is None: opts.dest = '/mnt/nfs/mythtv/plex/' if opts.format is None: format = '%T/%T - %pY%-%pm%-%pd %ph:%pi:%ps - %S (%c)' if opts.jobid: db = MythDB() job = Job(opts.jobid, db=db) rec = Recorded((job.chanid, job.starttime), db=db) gen_link(rec) elif opts.chanid and opts.starttime: rec = Recorded((opts.chanid, opts.starttime)) gen_link(rec) elif opts.filename: db = MythDB() rec = db.searchRecorded(basename=opts.filename) gen_link(rec) else: link_all()
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: print "\tDifferent Program Ids"
class VIDEO: def __init__(self, opts, jobid=None): if jobid: self.job = Job(jobid) self.chanid = self.job.chanid self.starttime = self.job.starttime self.job.update(status=3) else: self.job = None self.chanid = opts.chanid self.starttime = opts.starttime self.opts = opts self.db = MythDB() self.log = MythLog(module='mythvidexport.py', db=self.db) # load setting strings self.get_format() # prep objects self.rec = Recorded((self.chanid, self.starttime), db=self.db) self.log(MythLog.IMPORTANT, 'Using recording', '%s - %s' % (self.rec.title, self.rec.subtitle)) self.vid = Video(db=self.db).create({'title':'', 'filename':'', 'host':gethostname()}) # process data self.get_meta() self.get_dest() # kludgy fix for issue with altered filenames in the bindings self.vid.markup._refdat = (self.vid.filename,) # save file self.copy() if opts.seekdata: self.copy_seek() if opts.skiplist: self.copy_markup(static.MARKUP.MARK_COMM_START, static.MARKUP.MARK_COMM_END) if opts.cutlist: self.copy_markup(static.MARKUP.MARK_CUT_START, static.MARKUP.MARK_CUT_END) self.vid.update() def get_format(self): host = self.db.gethostname() # TV Format if self.opts.tformat: self.tfmt = self.opts.tformat elif self.db.settings[host]['mythvideo.TVexportfmt']: self.tfmt = self.db.settings[host]['mythvideo.TVexportfmt'] else: self.tfmt = 'Television/%TITLE%/Season %SEASON%/' + \ '%TITLE% - S%SEASON%E%EPISODEPAD% - %SUBTITLE%' # Movie Format if self.opts.mformat: self.mfmt = self.opts.mformat elif self.db.settings[host]['mythvideo.MOVIEexportfmt']: self.mfmt = self.db.settings[host]['mythvideo.MOVIEexportfmt'] else: self.mfmt = 'Movies/%TITLE%' # Generic Format if self.opts.gformat: self.gfmt = self.opts.gformat elif self.db.settings[host]['mythvideo.GENERICexportfmt']: self.gfmt = self.db.settings[host]['mythvideo.GENERICexportfmt'] else: self.gfmt = 'Videos/%TITLE%' def get_meta(self): self.vid.hostname = self.db.gethostname() if self.rec.subtitle: # subtitle exists, assume tv show self.type = 'TV' self.log(self.log.IMPORTANT, 'Attempting TV export.') if self.opts.listingonly: self.log(self.log.IMPORTANT, 'Forcing listing data only.') self.get_generic(False) return grab = VideoGrabber(self.type) match = grab.sortedSearch(self.rec.title, self.rec.subtitle) else: # assume movie self.type = 'MOVIE' self.log(self.log.IMPORTANT, 'Attempting Movie export.') if self.opts.listingonly: self.log(self.log.IMPORTANT, 'Forcing listing data only.') self.get_generic(False) return grab = VideoGrabber(self.type) match = grab.sortedSearch(self.rec.title) if len(match) == 0: # no match found self.log(self.log.IMPORTANT, 'Falling back to generic export.') self.get_generic() elif (len(match) > 1) & (match[0].levenshtein > 0): # multiple matches found, and closest is not exact self.vid.delete() raise MythError('Multiple metadata matches found: '\ + self.rec.title) else: self.log(self.log.IMPORTANT, 'Importing content from', match[0].inetref) self.vid.importMetadata(grab.grabInetref(match[0])) def get_generic(self, name_as_generic=True): self.vid.title = self.rec.title if self.rec.subtitle: self.vid.subtitle = self.rec.subtitle if self.rec.description: self.vid.plot = self.rec.description if self.rec.originalairdate: self.vid.year = self.rec.originalairdate.year self.vid.releasedate = self.rec.originalairdate lsec = (self.rec.endtime - self.rec.starttime).seconds self.vid.length = str(lsec / 60) for member in self.rec.cast: if member.role == 'director': self.vid.director = member.name elif member.role == 'actor': self.vid.cast.append(member.name) if name_as_generic: self.type = 'GENERIC' def get_dest(self): if self.type == 'TV': self.vid.filename = self.process_fmt(self.tfmt) elif self.type == 'MOVIE': self.vid.filename = self.process_fmt(self.mfmt) elif self.type == 'GENERIC': self.vid.filename = self.process_fmt(self.gfmt) def process_fmt(self, fmt): # replace fields from viddata # print self.vid.data 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) # fmt = fmt.replace('%CARDID%',self.rec.cardid) # fmt = fmt.replace('%CARDNAME%',self.rec.cardid) # fmt = fmt.replace('%SOURCEID%',self.rec.cardid) # fmt = fmt.replace('%SOURCENAME%',self.rec.cardid) # fmt = fmt.replace('%CHANNUM%',self.rec.channum) # fmt = fmt.replace('%CHANNAME%',self.rec.cardid) if len(self.vid.genre): fmt = fmt.replace('%GENRE%', self.vid.genre[0].genre) else: fmt = fmt.replace('%GENRE%', '') # if len(self.country): # fmt = fmt.replace('%COUNTRY%',self.country[0]) # else: # fmt = fmt.replace('%COUNTRY%','') return fmt + ext def copy(self): stime = time.time() srcsize = self.rec.filesize htime = [stime, stime, stime, stime] self.log.log(MythLog.IMPORTANT | MythLog.FILE, "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') if self.job: self.job.setStatus(4) 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.IMPORTANT | MythLog.FILE, "Transfer Complete", "%d seconds elapsed" % int(time.time() - stime)) if self.job: self.job.setComment("Complete - %d seconds elapsed" % \ (int(time.time() - stime))) self.job.setStatus(256) def copy_seek(self): for seek in self.rec.seek: self.vid.markup.add(seek.mark, seek.offset, seek.type) 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 main(): ' setup logger, all to stdout and INFO and higher to LOGFILE ' logging.basicConfig(format='%(message)s', level=logging.NOTSET) loggingfile = logging.handlers.RotatingFileHandler(LOGFILE, maxBytes=(MAXLOGSIZE), backupCount=MAXLOGS) loggingfile.setLevel(logging.INFO) formatter = logging.Formatter('%(asctime)s: %(message)s', datefmt='%m-%d %H:%M') loggingfile.setFormatter(formatter) logging.getLogger('').addHandler(loggingfile) ' connect to mythtv database ' db = MythDB() be = MythBE(db=db) ' loop all files in lib_dir that are symlinks and create listing ' listings = [] 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)): listings.append(lib_listing(filepath)) ' get list of all recordings from MythDB, link with library, figure out their status ' recordings = [] activeRecordings = [] activeJobs = [] newExpireList = [] mythrecordings = db.searchRecorded() recorderList = be.getRecorderList() for mrec in mythrecordings: logging.debug(mrec) rec = recording(mrec) recordings.append(rec) ' skip items already set to autoexpire ' if (rec.mythrec.autoexpire == 1): logging.debug(" - already set to expire, skip") continue ' loop through the list of library items looking for matching recordings, linking them ' for l in listings: if rec.match(l.symlink): if rec.lib_listing != None: logging.error("UH OH! Linking with something already linked!") else: rec.lib_listing = l ' figure out if the recording is active ' for recorder in recorderList: arec = be.getCurrentRecording(recorder) if (arec['title'] is not None) and (arec['chanid'] is not 0) and \ (arec == rec.program): logging.debug(" - currently recording, skip") activeRecordings.append(rec) rec.state = RecordingState.Recording break if (rec.state != RecordingState.Recorded): continue ' figuire if the recording is part of an active job ' jobs = db.searchJobs() #need to generate jobs on each loop for job in jobs: if job.status in [Job.ABORTING, Job.ERRORING, Job.PAUSED, Job.PENDING, \ Job.QUEUED, Job.RETRY, Job.RUNNING, Job.STARTING, Job.STOPPING]: jobrec = Recorded((job.chanid, job.starttime), db=db) if rec.mythrec == jobrec: logging.debug(" - currently part of a job, skip") activeJobs.append(rec) rec.state = RecordingState.BusyJob break if (rec.state != RecordingState.Recorded): continue ' potentially add to auto-expire list, and set orphaned recordings to auto-expire ' if (rec.lib_listing == None) and (rec.state == RecordingState.Recorded): logging.debug(" - no link, auto-expire") newExpireList.append(rec) # rec.mythrec.delete(force=True, rerecord=False) rec.mythrec.autoexpire = 1 rec.mythrec.update() ' log summary ' logging.info("") logging.info("** Summary **") logging.info(" [MythDB Recordings][%s]" % len(recordings)) logging.info(" - active recordings: %s" % len(activeRecordings)) for arec in activeRecordings: logging.info(" - %s [%s]" % (arec.program, arec.metadata.filename)) logging.info(" - in progress jobs: %s" % len(activeJobs)) for ajobs in activeJobs: logging.info(" - %s [%s]" % (ajobs.program, ajobs.metadata.filename)) logging.info("") logging.info(" [Mythical Links][%s]" % len(listings)) logging.info(" - new auto-expire items: %s" % len(newExpireList)) for d in newExpireList: logging.info( " - %s [%s]" % (d.program, d.metadata.filename))
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()
#!/usr/bin/env python from MythTV import MythDB mythdb = MythDB() for record in mythdb.searchRecord(): if record['autouserjob1'] == 0: record.update(autouserjob1=1)
d="/var/lib/mythtv/movies-group/rihanna/" def cmpTime(a,b): atime = int(time.mktime(a.recstartts.timetuple())) btime = int(time.mktime(b.recstartts.timetuple())) return btime - atime def filterMovies(a): return a.stars > 0 and a.recstatus == -1; myth = MythTV() db = MythDB() c = db.cursor() # c.execute("""UPDATE recorded SET %s = %%s # WHERE chanid=%%s and starttime=%%s""" % field, # (value,self.chanid,self.starttime)) # c.close() progs = filter(filterMovies, myth.getPendingRecordings()) for p in sorted(progs, cmpTime): print "%s %s %-8s %-8s %-20s" % (p.recstartts, p.chanid, p.recgroup, p.storagegroup, p.title) if p.recgroup <> "Movies" or p.storagegroup <> "Movies": c.execute("UPDATE record SET recgroup='Movies', storagegroup='Movies' WHERE recordid='%s'" % (p.recordid)) print "UPDATE: %s!" % p.recordid c.close()
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)