Ticket #6680: mythvidexport.4.py

File mythvidexport.4.py, 12.7 KB (added by Raymond Wagner <raymond@…>, 11 years ago)
Line 
1#!/usr/local/bin/python
2from MythTV import MythDB, MythLog, MythTV, MythVideo, Program, Job
3from socket import gethostname
4import sys, getopt, re, os, time, logging, pprint
5
6#Usage: mythvidexport.py <--chanid <channel id>> <--starttime <start time>> [options]
7#               --fformat <see directory/file name options>
8#               --tformat <see directory/file name options>
9#       moves a single file over to MythVideo with optional directory format, format is discarded after use
10
11#Usage: mythvidexport.py <job id>
12#               WARNING: for internal use by job queue only!
13#       moves a single file, grabbing necessary information from job queue table
14
15#Usage: mythvidexport.py <options>
16#               --setformat <see directory/file name options>
17#                       stored directory format in database for future use
18#               --help
19#               --helpformat
20#                       lengthy discription of directory formatting
21
22
23class VIDEO:
24        mythdb = None
25        dbconn = None
26        job = None
27        jobhost = None
28        chanid = None
29        rtime = None
30        source = None
31        srchost = None
32        srcdb = None
33        dest = None
34        desthost = None
35        destdb = None
36        prog = None
37        ffmt = None
38        tfmt = None
39        log = None
40        epstr = None
41
42        season= None
43        episode = None
44        viddata = {}
45        cast = None
46        genre = None
47        country = None
48
49        def __init__(self, *inp):
50                if len(inp) == 1:
51                        self.job = Job(inp[0]);
52                        self.chanid = self.job.chanid
53                        self.rtime = "%04d%02d%02d%02d%02d%02d" % self.job.starttime.timetuple()[0:6]
54                        self.jobhost = self.job.host
55                        self.job.setStatus(3)
56                elif len(inp) == 2:
57                        self.chanid = inp[0]
58                        self.rtime = inp[1]
59                else:
60                        sys.exit(2)
61                self.get_source()
62                self.log = MythLog(logging.CRITICAL, '#%(levelname)s - %(message)s', 'MythTV')
63
64        def dbconnect(self):
65                self.mythdb = MythDB()
66                self.dbconn = self.mythdb.db
67
68        def get_source(self):
69                if not self.dbconn:
70                        self.dbconnect()
71                if not self.jobhost:
72                        self.jobhost = gethostname()
73
74                mythinst = MythTV()
75                self.prog = mythinst.getRecording(self.chanid,int(self.rtime))
76
77                self.srcdb = self.prog.filename
78                self.srchost = self.prog.hostname
79
80                self.source = mythinst.getCheckfile(self.prog)
81
82        def find_ttvdb(self):
83                if os.access('/usr/local/share/mythtv/mythvideo/scripts/ttvdb.py',os.F_OK):
84                        self.ttvdb = '/usr/local/share/mythtv/mythvideo/scripts/ttvdb.py'
85                elif os.access('/usr/share/mythtv/mythvideo/scripts/ttvdb.py',os.F_OK):
86                        self.ttvdb = '/usr/local/share/mythtv/mythvideo/scripts/ttvdb.py'
87                else:
88                        self.log.Msg(logging.CRITICAL, "Could not find ttvdb.py")
89                        self.exit(2)
90
91        def get_meta(self, use_ttvdb):
92                regex = re.compile('S(?P<season>[0-9]*)E(?P<episode>[0-9]*)')
93                match = None
94                if (use_ttvdb == 0) and (self.epstr):
95                        match = regex.search(self.epstr)
96                elif use_ttvdb in (1,2):
97                        self.find_ttvdb()
98                        fp = os.popen("%s -N \"%s\" \"%s\"" % (self.ttvdb,self.prog.title,self.prog.subtitle),"r")
99                        match = regex.search(fp.read())
100                        fp.close()
101                if match is None:
102                        if self.job:
103                                self.job.setComment("TTVDB.py failed to get season/episode numbers")
104                                self.job.setStatus(304)
105                                self.log.Msg(logging.CRITICAL, "TTVDB.py failed to get season/episode numbers")
106                        else:
107                                print("TTVDB.py failed to get season/episode numbers")
108                        sys.exit(2)
109                self.season = int(match.group('season'))
110                self.episode = int(match.group('episode'))
111
112                if use_ttvdb == 2:
113                        print("calling '%s -mD '%s' %d %d'" % (self.ttvdb,self.prog.title,self.season,self.episode))
114                        fp = os.popen("%s -mD '%s' %d %d" % (self.ttvdb,self.prog.title,self.season,self.episode))
115                        time.sleep(2)
116                        res = fp.read()
117                        fp.close()
118                        ttvdbdat = {}
119                        if len(res) == 0:
120                                self.get_metadata(1)
121                                return
122                        for point in res.split('\n')[:-1]:
123                                key,dat = point.split(':',1)
124                                ttvdbdat[key] = dat
125                        self.viddata['director'] = ttvdbdat['Director']
126                        self.viddata['plot'] = ttvdbdat['Plot']
127                        self.viddata['year'] = ttvdbdat['Year']
128                        self.viddata['length'] = ttvdbdat['Runtime']
129                        self.viddata['userrating'] = ttvdbdat['UserRating']
130                        self.cast = tuple(ttvdbdat['Cast'].split(', '))
131                        self.genre = tuple(ttvdbdat['Genres'].split(', '))
132                        self.country = ()
133                else:
134                        self.viddata['director'] = self.mythdb.getCast(self.chanid, int(self.rtime), roles='director')
135                        if len(self.viddata['director']) == 0:
136                                self.viddata['director'] = 'NULL'
137                        else:
138                                self.viddata['director'] = self.viddata['director'][0]
139                        self.viddata['plot'] = self.prog.description
140                        self.viddata['year'] = self.prog.year
141                        self.viddata['length'] = str(int((self.prog.recendts-self.prog.recstartts).seconds/60))
142                        self.cast = self.mythdb.getCast(self.chanid, int(self.rtime), roles=('actor','guest_star','host','commentator','guest'))
143                        self.genre = ()
144                        self.country = ()
145
146                self.viddata['showlevel'] = 1
147                self.viddata['rating'] = 'NR'
148                self.viddata['inetref'] = '00000000'
149                self.viddata['coverfile'] = 'No Cover'
150
151        def get_dest(self):
152                if self.ffmt is None:
153                        self.get_fmt()
154                #process self.fmt and generate self.dest
155                #not programmed yet... just use my own naming scheme
156                ext = self.source[self.source.rfind('.'):]
157                #replace with sequence of str-replace for the formatted destination
158                subpath = self.process_fmt(self.ffmt) + ext
159
160                self.desthost = self.jobhost
161                cursor = self.dbconn.cursor()
162                reslen = cursor.execute("SELECT dirname FROM storagegroup WHERE hostname='%s' and groupname='Videos'" % self.desthost)
163                if reslen:
164                        self.dest = cursor.fetchone()[0]+subpath
165                        self.destdb = subpath
166                else:
167                        self.dest = self.mythdb.getSetting("VideoStartupDir",hostname=self.jobhost)+'/'+subpath
168                        self.destdb = self.dest
169
170
171                tmppath = self.dest[0:self.dest.rfind('/')]
172                if not os.access(tmppath,os.F_OK):
173                        os.makedirs(tmppath)
174
175        def set_tfmt(self, fmt):
176                self.tfmt = fmt
177        def set_ffmt(self, fmt):
178                self.ffmt = fmt
179
180        def get_fmt(self):
181                self.ffmt = self.mythdb.getSetting('mythvideo.exportfmt')
182                if not self.ffmt:
183                        self.ffmt = 'Television/%TITLE%/%TITLE% - %SUBTITLE%'
184                self.tfmt = self.mythdb.getSetting('mythvideo.titlefmt')
185                if not self.tfmt:
186                        self.tfmt = '%TITLE% - %SUBTITLE%'
187
188        def process_fmt(self, fmt):
189                fmt = fmt.replace('%TITLE%',self.prog.title)
190                fmt = fmt.replace('%SUBTITLE%',self.prog.subtitle)
191                fmt = fmt.replace('%SEASON%',"%d" % self.season)
192                fmt = fmt.replace('%EPISODE%',"%02d" % self.episode)
193                fmt = fmt.replace('%YEAR%',self.viddata['year'])
194                fmt = fmt.replace('%DIRECTOR%',self.viddata['director'])
195#               fmt = fmt.replace('%CARDID%',self.prog.cardid)
196#               fmt = fmt.replace('%CARDNAME%',self.prog.cardid)
197#               fmt = fmt.replace('%SOURCEID%',self.prog.cardid)
198#               fmt = fmt.replace('%SOURCENAME%',self.prog.cardid)
199                fmt = fmt.replace('%HOSTNAME%',self.prog.hostname)
200                fmt = fmt.replace('%STORAGEGROUP%',self.prog.storagegroup)
201#               fmt = fmt.replace('%CHANNUM%',self.prog.channum)
202#               fmt = fmt.replace('%CHANNAME%',self.prog.cardid)
203                if len(self.genre):
204                        fmt = fmt.replace('%GENRE%',self.genre[0])
205                else:
206                        fmt = fmt.replace('%GENRE%','')
207#               if len(self.country):
208#                       fmt = fmt.replace('%COUNTRY%',self.country[0])
209#               else:
210#                       fmt = fmt.replace('%COUNTRY%','')
211                return fmt
212
213        def copy(self):
214                cursor = None
215                fp = None
216                srcsize = None
217                dtime = None
218                self.get_dest()
219                if self.desthost != self.jobhost:
220                        if self.job:
221                                self.job.setComment("Job must be run on destination host")
222                                self.log.Msg(logging.CRITICAL, 'MythVidExport failed - must be run on destination host')
223                                self.job.setStatus(304)
224                        else:
225                                print("ERROR: job must be run on destionation host")
226                        sys.exit(2)
227
228                stime = time.time()
229                print("Copying %s to %s..." % (self.source,self.dest))
230#               fp = os.popen("cp \"%s\" \"%s\"" % (self.source,self.dest))
231                if (self.srchost == self.jobhost) or (os.access(self.source,os.F_OK)):
232                        fp = os.popen("cp '%s' '%s'" % (self.source,self.dest))
233                else:
234                        fp = os.popen("scp '%s:%s' '%s'" % (self.srchost, self.source, self.dest))
235                srcsize = self.prog.filesize
236                destsize = [0,0,0,0,0,0,0,0,0,0]
237                if self.job:
238                        self.job.setStatus(4)
239                while srcsize > destsize[9]:
240                        time.sleep(4)
241                        destsize.append(os.stat(self.dest)[6])
242                        rem = srcsize-destsize[10]
243                        if destsize[0]:
244                                dtime = 40
245                        else:
246                                dtime = int(time.time()-stime)
247                        rate = (destsize[10]-destsize.pop(0))/dtime
248                        remt = rem/rate
249                        if self.job:
250                                self.job.setComment("%02d%% complete - %s seconds remaining" % (destsize[9]*100/srcsize, remt))
251                fp.close()
252                if self.job:
253                        self.job.setComment("Complete - %d seconds elapsed" % (int(time.time()-stime)))
254                        self.job.setStatus(256)
255
256
257        def write_meta(self):
258                if self.tfmt is None:
259                        self.get_fmt()
260                mythvid = MythVideo()
261                mythtv = MythTV()
262                pp = pprint.PrettyPrinter(indent=4)
263                self.viddata['title'] = self.process_fmt(self.tfmt)
264                self.viddata['filename'] = self.destdb
265                self.viddata['host'] = self.desthost
266
267
268                pp.pprint(self.viddata)
269                intid = mythvid.getMetadataId(self.destdb)
270                if intid:
271                        mythvid.setMetadata(self.viddata,intid)
272                else:
273                        intid = mythvid.setMetadata(self.viddata)
274                print("Metadata written to intid %d" % intid)
275                pp.pprint(self.viddata)
276
277                print("With cast:")
278                pp.pprint(self.cast)
279                for name in self.cast:
280                        mythvid.setCast(name, intid)
281                print("With genre:")
282                pp.pprint(self.genre)
283#               for genre in self.genre:
284#                       mythvid.setGenre(genre, intid)
285
286                if self.job:
287                        self.job.setStatus(272)
288
289def usage():
290        print("mythvidexport.py [options] [--chanid=<chanid> --starttime=<starttime> | <jobid>]")
291        print("        This script can be run by specifing the channel and start time directly")
292        print("            or by specifing the ID of a job in jobqueue")
293        print("")
294        print("        Run from the command line through the former:")
295        print("            mythvidexport.py --chanid=1002 --starttime=200907010000")
296        print("        Or from a user script through the latter:")
297        print("            mythvidexport.py %JOBID%")
298        print("")
299        print("    Options:")
300        print("        -h/--help and -f/--helpformat:")
301        print("             return this help, or a listing of available formatting strings")
302        print("        --fformat='<string>' and --tformat='<string>':")
303        print("             override the stored formatting string in the database")
304        print("             if no recording is specified, store format string to the database")
305        print("        --episode=<string>:")
306        print("             specify the episode number using the format S00E00")
307        print("             removes the need for ttvdb.py to perform this lookup")
308        print("        --ttvdb:")
309        print("             use ttvdb.py to grab metadata rather than using available data")
310        print("                 from listings source")
311
312def usage_format():
313        print("The default strings are:")
314        print("    fformat: 'TV/%TITLE%/Season %SEASON%/%SEASON%x%EPISODE% - %SUBTITLE%'")
315        print("    tformat: '%SEASON%x%EPISODE% - %SUBTITLE%'")
316        print("")
317        print("Available strings:")
318        print("    %TITLE%:         series title")
319        print("    %SUBTITLE%:      episode title")
320        print("    %SEASON%:        season number")
321        print("    %EPISODE%:       episode number, padded to 2 digits")
322        print("    %YEAR%:          year")
323        print("    %DIRECTOR%:      director")
324#       print("    %CARDID%:        ID of tuner card used to record show")
325#       print("    %CARDNAME%:      name of tuner card used to record show")
326#       print("    %SOURCEID%:      ID of video source used to record show")
327#       print("    %SOURCENAME%:    name of video source used to record show")
328        print("    %HOSTNAME%:      backend used to record show")
329        print("    %STORAGEGROUP%:  storage group containing recorded show")
330#       print("    %CHANNUM%:       ID of channel used to record show")
331#       print("    %CHANNAME%:      name of channel used to record show")
332        print("    %GENRE%:         first genre listed for recording")
333#       print("    %COUNTRY%:       first country listed for recording")
334
335def main():
336        fformat = None
337        tformat = None
338        jobid = None
339        chanid = None
340        rtime = None
341        episode = None
342        ttvdb = 1
343        skip = False
344        try:
345                opts, args = getopt.getopt(sys.argv[1:], "hf", ["help","helpformat","fformat=g","tformat=","chanid=","starttime=","noepisode","episode=","ttvdb","skip"])
346        except getopt.GetoptError, err:
347                print(str(err))
348                usage()
349                sys.exit(2)
350        for opt, arg in opts:
351                if opt in ("-h", "--help"):
352                        usage()
353                        sys.exit()
354                elif opt in ('-f', '--helpformat'):
355                        usage_format()
356                        sys.exit()
357                elif opt == '--tformat':
358                        tformat = arg
359                elif opt == '--fformat':
360                        fformat = arg
361                elif opt == '--chanid':
362                        chanid = int(arg)
363                elif opt == '--starttime':
364                        rtime = int(arg)
365                elif opt == '--episode':
366                        ttvdb = 0
367                        episode = arg
368                elif opt == '--noepisode':
369                        ttvdb = 0
370                elif opt == '--ttvdb':
371                        ttvdb = 2
372                elif opt == '--skip':
373                        skip = True
374               
375        if len(args):
376                jobid = int(args[0])
377
378        if chanid and rtime:
379                export = VIDEO(chanid,rtime)
380        elif jobid:
381                export = VIDEO(jobid)
382        elif fformat or tformat:
383                if fformat:
384                        mythdb = MythDB()
385                        mythdb.setSetting('mythvideo.exportfmt', fmt)
386                if tformat:
387                        mythdb = MythDB()
388                        mythdb.setSetting('mythvideo.titlefmt', fmt)
389                sys.exit()
390        else:
391                usage()
392                sys.exit(2)
393
394        if episode:
395                export.epstr = episode
396        export.get_meta(ttvdb)
397        if skip:
398                export.get_dest()
399        else:
400                export.copy()
401        export.write_meta()
402
403if __name__ == "__main__":
404        main()