From f01f4a6a837ccbdc55107bb0738846b5a56f1462 Mon Sep 17 00:00:00 2001
From: James Meyer <james.meyer@operamail.com>
Date: Fri, 30 Dec 2011 13:02:22 -0600
Subject: linhes-scripts: add find_orphans script

---
 abs/core/linhes-scripts/PKGBUILD        |   6 +-
 abs/core/linhes-scripts/find_orphans.py | 238 ++++++++++++++++++++++++++++++++
 2 files changed, 242 insertions(+), 2 deletions(-)
 create mode 100644 abs/core/linhes-scripts/find_orphans.py

diff --git a/abs/core/linhes-scripts/PKGBUILD b/abs/core/linhes-scripts/PKGBUILD
index 0ac8275..80321a3 100644
--- a/abs/core/linhes-scripts/PKGBUILD
+++ b/abs/core/linhes-scripts/PKGBUILD
@@ -3,7 +3,7 @@
 
 pkgname=linhes-scripts
 pkgver=7
-pkgrel=21
+pkgrel=22
 pkgdesc="Various scripts that help to make LinHES, LinHES."
 arch=('i686' 'x86_64')
 license=('GPL2')
@@ -47,6 +47,7 @@ dtheme.sh
 xwin_find.sh
 upgrade_screen_rc
 upgrade_linhes_script.sh
+find_orphans.py
 )
 
 build() {
@@ -97,4 +98,5 @@ md5sums=('f56985b2d602e11dc1e10d3e7848b2a5'
          '35693f50939d5827aeabfce8c5dce589'
          'a94fe6d980f4b810f2e2ae5352084b39'
          '974c137d3fcb85942ba8945a1bc815fe'
-         'aad75575c9af70aa8bf3a06718985664')
+         'aad75575c9af70aa8bf3a06718985664'
+         'd8838461af9d446a1fd7e7883fdc75d1')
diff --git a/abs/core/linhes-scripts/find_orphans.py b/abs/core/linhes-scripts/find_orphans.py
new file mode 100644
index 0000000..ec2cae1
--- /dev/null
+++ b/abs/core/linhes-scripts/find_orphans.py
@@ -0,0 +1,238 @@
+#!/usr/bin/env python
+
+from MythTV import MythDB, MythBE, Recorded, MythError
+from socket import timeout
+
+import os
+import sys
+
+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]
+
+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 pprint(self):
+        name = u'%s: %s' % (self.host, os.path.join(self.path, self))
+        print u'  {0:<90}{1:>8}'.format(name, human_size(self.size))
+    def delete(self):
+        be = MythBE(self.host, db=DB)
+        be.deleteFile(self, self.group)
+
+class MyRecorded( Recorded ):
+    _table = 'recorded'
+    def pprint(self):
+        name = u'{0.hostname}: {0.title}'.format(self)
+        if self.subtitle:
+            name += u' - '+self.subtitle
+        print u'  {0:<70}{1:>28}'.format(name,self.basename)
+
+def printrecs(title, recs):
+    print title
+    for rec in sorted(recs, key=lambda x: x.title):
+        rec.pprint()
+    print u'{0:>88}{1:>12}'.format('Count:',len(recs))
+
+def printfiles(title, files):
+    print title
+    for f in sorted(files, key=lambda x: x.path):
+        f.pprint()
+    size = sum([f.size for f in files])
+    print u'{0:>88}{1:>12}'.format('Total:',human_size(size))
+
+def populate(host=None):
+    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)
+
+    return (recs, zerorecs, orphvids, orphimgs, dbbackup, unfiltered)
+
+def delete_recs(recs):
+    printrecs('The following recordings will be deleted', recs)
+    print 'Are you sure you want to continue?'
+    try:
+        res = raw_input('> ')
+        while True:
+            if res == 'yes':
+                for rec in recs:
+                    rec.delete(True, True)
+                break
+            elif res == 'no':
+                break
+            else:
+                res = raw_input("'yes' or 'no' > ")
+    except MythError:
+        name = u'{0.hostname}: {0.title}'.format(rec)
+        if rec.subtitle:
+            name += ' - '+rec.subtitle
+        print "Warning: Failed to delete '" + name + "'"
+    except KeyboardInterrupt:
+        pass
+    except EOFError:
+        sys.exit(0)
+
+def delete_files(files):
+    printfiles('The following files will be deleted', files)
+    print 'Are you sure you want to continue?'
+    try:
+        res = raw_input('> ')
+        while True:
+            if res == 'yes':
+                for f in files:
+                    f.delete()
+                break
+            elif res == 'no':
+                break
+            else:
+                res = raw_input("'yes' or 'no' > ")
+    except KeyboardInterrupt:
+        pass
+    except EOFError:
+        sys.exit(0)
+
+def main(host=None):
+   while True:
+        recs, zerorecs, orphvids, orphimgs, dbbackup, unfiltered = populate(host)
+
+        if len(recs):
+            printrecs("Recordings with missing files", recs)
+        if len(zerorecs):
+            printrecs("Zero byte recordings", zerorecs)
+        if len(orphvids):
+            printfiles("Orphaned video files", orphvids)
+        if len(orphimgs):
+            printfiles("Orphaned snapshots", orphimgs)
+        if len(dbbackup):
+            printfiles("Database backups", dbbackup)
+        if len(unfiltered):
+            printfiles("Other files", unfiltered)
+        if not printOnly:
+            opts = []
+            if len(recs):
+                opts.append(['Delete orphaned recording entries', delete_recs, recs])
+            if len(zerorecs):
+                opts.append(['Delete zero byte recordings', delete_recs, zerorecs])
+            if len(orphvids):
+                opts.append(['Delete orphaned video files', delete_files, orphvids])
+            if len(orphimgs):
+                opts.append(['Delete orphaned snapshots', delete_files, orphimgs])
+            if len(unfiltered):
+                opts.append(['Delete other files', delete_files, unfiltered])
+            opts.append(['Refresh list', None, None])
+            print 'Please select from the following'
+            for i, opt in enumerate(opts):
+                print ' {0}. {1}'.format(i+1, opt[0])
+
+            try:
+                inner = True
+                res = raw_input('> ')
+                while inner:
+                    try:
+                        res = int(res)
+                    except:
+                        res = raw_input('input number. ctrl-c to exit > ')
+                        continue
+                    if (res <= 0) or (res > len(opts)):
+                        res = raw_input('input number within range > ')
+                        continue
+                    break
+                opt = opts[res-1]
+                if opt[1] is None:
+                    continue
+                else:
+                    opt[1](opt[2])
+
+            except KeyboardInterrupt:
+                break
+            except EOFError:
+                sys.exit(0)
+        else:
+            sys.exit(0)
+DB = MythDB()
+BE = MythBE(db=DB)
+DB.searchRecorded.handler = MyRecorded
+DB.searchRecorded.dbclass = MyRecorded
+
+if __name__ == '__main__':
+    global printOnly
+    if "--printonly" in sys.argv :
+        printOnly=True
+    else:
+        printOnly=False
+
+
+    if len(sys.argv) == 2 and sys.argv[1] != "--printonly":
+        main(sys.argv[1])
+    else:
+        main()
-- 
cgit v0.12