Ticket #6680: mythvidexport.3.py

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