Ticket #6680: mythvidexport.5.py

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