summaryrefslogtreecommitdiffstats
path: root/linhes/linhes-system/find_orphans.py
blob: 786f01b7695afd7c2bcffe4d43cb0afb8752a0d8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
#!/usr/bin/python3
# based on https://www.mythtv.org/wiki/Find_orphans.py

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','TB')[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 = '%s: %s' % (self.host, os.path.join(self.path, self))
        print('  {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 = '{0.hostname}: {0.title}'.format(self)
        if self.subtitle:
            name += ' - '+self.subtitle
        print('  {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('{0:>87}{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('{0:>87}{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, 'BackendServerAddr'))
            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='BackendServerAddr'""")
            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',\
                                'Music','MusicArt', 'Photographs',\
                                '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') or f.endswith('.ts')):
            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? (yes/no)')
    try:
        res = input('> ')
        while True:
            if res == 'yes':
                for rec in recs:
                    rec.delete(True, True)
                break
            elif res == 'no':
                break
            else:
                res = input("'yes' or 'no' > ")
    except MythError:
        name = '{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? (yes/no)')
    try:
        res = input('> ')
        while True:
            if res == 'yes':
                for f in files:
                    f.delete()
                break
            elif res == 'no':
                break
            else:
                res = 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):
                if opt[0] == "Refresh list":
                    print(' R. {1}'.format(i+1, opt[0]))
                    refreshNum=i+1
                else:
                    print(' {0}. {1}'.format(i+1, opt[0]))

            print(' Q. Quit')
            try:
                inner = True
                res = input('> ')
                while inner:
                    try:
                        res = int(res)
                    except:
                        if res == "Q" or res == "q":
                            sys.exit(0)
                        elif res == "R" or res == "r":
                            res = refreshNum
                        else:
                            res = input('Invalid selection > ')
                        continue
                    if (res <= 0) or (res > len(opts)):
                        res = input('Invalid selection > ')
                        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()