Find orphans.py

From MythTV Official Wiki
Revision as of 21:31, 17 December 2010 by Wagnerrp (talk | contribs) (allow handling of livetv sessions)

Jump to: navigation, search

Important.png Note: The correct title of this article is find_orphans.py. It appears incorrectly here due to technical restrictions.


Author Raymond Wagner
Description A scanner to look for missing and unknown recording files. This is informative only, informing the user of the files but taking no action.
Supports Version24.png  


This script is a "safe" alternative to the old Myth.find_orphans.pl script. It is informative only, showing any missing videos, or unknown files. It can handle multiple backends, and does not need to be run locally, however recordings stored on offline backends will be marked as orphaned.

>./find_orphans.py
Recordings with missing files
  Undercovers - Devices                  4642_20101006201300.mpg

Orphaned video files
  mythbe:/srv/mounts/twotb_1/video/2054_20080225110000.mpg                          2.5GB
                                                                          Total:    2.5GB

Orphaned snapshots
  myth0:/srv/mounts/myth0_1/video/4122_20101013113500.mpg.png                       2.6KB
  mythbe:/srv/mounts/twotb_1/video/2029_20100409024900.mpg.png                     84.9KB
  mythbe:/srv/mounts/twotb_1/video/2047_20100807180500.mpg.png                     92.9KB
  mythbe:/srv/mounts/twotb_1/video/2059_20100630090000.mpg.png                     87.0KB
                                                                          Total:  267.4KB

Database backups
  mythbe:/mnt/mythtv/store/backups/mythconverg--20101007134000.sql                 17.3MB
  mythbe:/mnt/mythtv/store/backups/mythconverg-1254-20100902174922.sql.gz          13.1MB
  mythbe:/mnt/mythtv/store/backups/mythconverg-1263-20100913163154.sql             62.1MB
  mythbe:/mnt/mythtv/store/backups/mythconverg-1263-20100913163216.sql.gz          13.0MB
  mythbe:/mnt/mythtv/store/backups/mythconverg-1263-20101007134659.sql.gz          15.9MB
  mythbe:/mnt/mythtv/store/backups/mythconverg-1264-20101008023651.sql.gz          16.5MB
                                                                          Total:  137.9MB

Other files
  mythbe:/srv/mounts/twotb_1/video/4121_20100312215900.mpg.tmp                    398.6MB
  mythbe:/srv/mounts/twotb_1/video/4191_20090928200000.mpg.tmp                      2.4GB
  mythbe:/srv/mounts/twotb_1/video/4191_20091005195900.mpg.tmp                      2.6GB
  mythbe:/srv/mounts/twotb_1/video/4642_20101006201300.mpg.1                        4.9GB
                                                                          Total:   10.2GB


PythonIcon.png find_orphans.py

#!/usr/bin/env python

from MythTV import MythDB, MythBE
from socket import timeout

import os
import sys

class File( str ):
    def __new__(self, host, group, path, name, size):
        return str.__new__(self, name)
    def __init__(self, host, group, path, name, size):
        self.host = host
        self.group = group
        self.path = path
        self.size = int(size)

def human_size(s):
    s = float(s)
    o = 0
    while s > 1000:
        s /= 1000
        o += 1
    return str(round(s,1))+('B ','KB','MB','GB')[o]

def prettyprint(f):
    print ('  %s:%s' % (f.host, os.path.join(f.path, f))).ljust(80),\
            human_size(f.size).rjust(8)


def main(host=None):
    db = MythDB()
    be = MythBE()

    unfiltered = []
    kwargs = {'livetv':True}
    if host:
        with db as c:
            c.execute("""SELECT count(1) FROM settings
                         WHERE hostname=%s AND value=%s""",
                        (host, 'BackendServerIP'))
            if c.fetchone()[0] == 0:
                raise Exception('Invalid hostname specified on command line.')
        hosts = [host]
        kwargs['hostname'] = host
    else:
        with db as c:
            c.execute("""SELECT hostname FROM settings
                         WHERE value='BackendServerIP'""")
            hosts = [r[0] for r in c.fetchall()]
    for host in hosts:
        for sg in db.getStorageGroup():
            if sg.groupname in ('Videos','Banners','Coverart',\
                                'Fanart','Screenshots','Trailers'):
                continue
            try:
                dirs,files,sizes = be.getSGList(host, sg.groupname, sg.dirname)
                for f,s in zip(files,sizes):
                    newfile = File(host, sg.groupname, sg.dirname, f, s)
                    if newfile not in unfiltered:
                        unfiltered.append(newfile)
            except:
                pass

    recs = list(db.searchRecorded(**kwargs))

    zerorecs = []
    orphvids = []
    for rec in list(recs):
        if rec.basename in unfiltered:
            recs.remove(rec)
            i = unfiltered.index(rec.basename)
            f = unfiltered.pop(i)
            if f.size < 1024:
                zerorecs.append(rec)
            name = rec.basename.rsplit('.',1)[0]
            for f in list(unfiltered):
                if name in f:
                    unfiltered.remove(f)
    for f in list(unfiltered):
        if not (f.endswith('.mpg') or f.endswith('.nuv')):
            continue
        orphvids.append(f)
        unfiltered.remove(f)

    orphimgs = []
    for f in list(unfiltered):
        if not f.endswith('.png'):
            continue
        orphimgs.append(f)
        unfiltered.remove(f)

    dbbackup = []
    for f in list(unfiltered):
        if 'sql' not in f:
            continue
        dbbackup.append(f)
        unfiltered.remove(f)

    if len(recs):
        print "Recordings with missing files"
        for rec in recs:
            if rec.subtitle:
                print ('  %s - %s' % (rec.title, rec.subtitle)).ljust(60),
            else:
                print ('  %s' % rec.title).ljust(60),
            print rec.basename

    if len(zerorecs):
        print "\nZero byte recordings"
        for rec in zerorecs:
            if rec.subtitle:
                print ('  %s - %s' % (rec.title, rec.subtitle)).ljust(40),
            else:
                print ('  %s' % rec.title).ljust(40),
            print rec.basename

    if len(orphvids):
        print "\nOrphaned video files"
        for f in sorted(orphvids, key=lambda x: x.path):
            # os.unlink(os.path.join(f.path,f))
            prettyprint(f)
        size = sum([f.size for f in orphvids])
        print 'Total:'.rjust(80),human_size(size).rjust(8)

    if len(orphimgs):
        print "\nOrphaned snapshots"
        for f in sorted(orphimgs, key=lambda x: x.path):
            prettyprint(f)
        size = sum([f.size for f in orphimgs])
        print 'Total:'.rjust(80),human_size(size).rjust(8)

    if len(dbbackup):
        print "\nDatabase backups"
        for f in sorted(dbbackup, key=lambda x: x.path):
            prettyprint(f)
        size = sum([f.size for f in dbbackup])
        print 'Total:'.rjust(80),human_size(size).rjust(8)

    if len(unfiltered):
        print "\nOther files"
        for f in sorted(unfiltered, key=lambda x: x.path):
            prettyprint(f)
        size = sum([f.size for f in unfiltered])
        print 'Total:'.rjust(80),human_size(size).rjust(8)

if __name__ == '__main__':
    if len(sys.argv) == 2:
        main(sys.argv[1])
    else:
        main()