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)
Example #2
0
    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)
Example #4
0
    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)
Example #5
0
    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()
Example #8
0
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)
Example #9
0
    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()
Example #10
0
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 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']))
Example #14
0
    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)
Example #15
0
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)
Example #17
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)
Example #18
0
 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)
Example #19
0
    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)
Example #20
0
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)
Example #21
0
 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))
Example #23
0
 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()
Example #24
0
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))
Example #26
0
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)
Example #27
0
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'])
Example #29
0
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)
Example #30
0
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, 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()
Example #32
0
    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))
Example #35
0
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()
Example #36
0
    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)
Example #37
0
    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()
Example #38
0
    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()
Example #39
0
            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
Example #40
0
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)
Example #42
0
#!/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()


Example #43
0
        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()

Example #44
0
        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"
Example #45
0
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)
Example #46
0
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))
Example #47
0
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()
Example #48
0
#!/usr/bin/env python

from MythTV import MythDB

mythdb = MythDB()

for record in mythdb.searchRecord():
    if record['autouserjob1'] == 0:
        record.update(autouserjob1=1)

Example #49
0
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)