Ticket #6680: mythvidexport.9.py

File mythvidexport.9.py, 13.8 KB (added by Raymond Wagner <raymond@…>, 11 years ago)

Fixes a mistake with the formatting fields from the database

Line 
1#!/usr/local/bin/python
2from MythTV import MythTV, MythDB, MythLog, MythVideo, Program, FileTransfer, Job, NOTICE, INFO, DEBUG
3from socket import gethostname
4from urllib import urlretrieve
5import sys, getopt, re, os, time#, pprint
6
7#Usage: mythvidexport.py <--chanid <channel id>> <--starttime <start time>> [options]
8#               --fformat <see directory/file name options>
9#               --tformat <see directory/file name options>
10#       moves a single file over to MythVideo with optional directory format, format is discarded after use
11
12#Usage: mythvidexport.py <job id>
13#               WARNING: for internal use by job queue only!
14#       moves a single file, grabbing necessary information from job queue table
15
16#Usage: mythvidexport.py <options>
17#               --setformat <see directory/file name options>
18#                       stored directory format in database for future use
19#               --help
20#               --helpformat
21#                       lengthy discription of directory formatting
22#               --delete
23
24log = MythLog(NOTICE, 'MythVidExport.py')
25
26class VIDEO:
27        mythdb = None
28        dbconn = None
29        job = None
30        jobhost = None
31        chanid = None
32        rtime = None
33
34        prog = None
35        source = None
36        srchost = None
37        dest = None
38        desthost = None
39
40        ttvdb = None
41        ffmt = None
42        dfmt = None
43 
44        viddata = {}
45        cast = []
46        genre = []
47        country = []
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
63        def dbconnect(self):
64                self.mythdb = MythDB()
65                self.dbconn = self.mythdb.db
66
67        def get_source(self):
68                ## connects to myth data socket and finds recording
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.srchost = self.prog.hostname
78                self.source = mythinst.getCheckfile(self.prog)
79
80        def find_ttvdb(self):
81                ## searches for ttvdb.py script, and optional config file
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                        log.critical('Dependancy error', "Could not find ttvdb.py")
88                        self.exit(2)
89                if os.access(os.path.expanduser("~/.mythtv/ttvdb.conf"),os.F_OK):
90                        self.ttvdb += ' -c ' + os.path.expanduser("~/.mythtv/ttvdb.conf")
91                log.info("using '%s'" % self.ttvdb)
92
93        def get_meta(self):
94                ## grab season and episode number from thetvdb.com, exit if failed
95                regex = re.compile('S(?P<season>[0-9]*)E(?P<episode>[0-9]*)')
96                match = None
97                if self.ttvdb == None:
98                        self.find_ttvdb()
99                fp = os.popen("%s -N \"%s\" \"%s\"" % (self.ttvdb,self.prog.title,self.prog.subtitle),"r")
100                match = regex.search(fp.read())
101                fp.close()
102                if match is None:
103                        log.warning('Grabber error', "TTVDB.py failed to get season/episode numbers for %s - %s" % (self.prog.title, self.prog.subtitle))
104                        if self.job:
105                                self.job.setComment("TTVDB.py failed to get season/episode numbers")
106                                self.job.setStatus(304)
107                        sys.exit(2)
108
109                self.viddata['season'] = int(match.group('season'))
110                self.viddata['episode'] = int(match.group('episode'))
111
112                ## gather remaining data
113                fp = os.popen("%s -mD '%s' %d %d" % (self.ttvdb,self.prog.title,self.viddata['season'],self.viddata['episode']))
114                time.sleep(2)
115                res = fp.read()
116                fp.close()
117                if len(res) == 0:
118                        ## ttvdb failed to grab data, use listings information
119                        self.viddata['director'] = self.mythdb.getCast(self.chanid, int(self.rtime), roles='director')
120                        if len(self.viddata['director']) == 0:
121                                self.viddata['director'] = 'NULL'
122                        else:
123                                self.viddata['director'] = self.viddata['director'][0]
124                        self.viddata['title'] = self.prog.title
125                        self.viddata['subtitle'] = self.prog.subtitle
126                        self.viddata['plot'] = self.prog.description
127                        self.viddata['year'] = self.prog.year
128                        self.viddata['length'] = str(int((self.prog.recendts-self.prog.recstartts).seconds/60))
129                        self.cast = self.mythdb.getCast(self.chanid, int(self.rtime), roles=('actor','guest_star','host','commentator','guest'))
130                        self.viddata['inetref'] = '00000000'
131                        self.genre = ()
132                        self.country = ()
133                else:
134                        ttvdbdat = {}
135                        for point in res.split('\n')[:-1]:
136                                key,dat = point.split(':',1)
137                                ttvdbdat[key] = dat
138                        self.viddata['title'] = self.prog.title
139                        self.viddata['subtitle'] = ttvdbdat['Subtitle']
140                        self.viddata['director'] = ttvdbdat['Director']
141                        self.viddata['plot'] = ttvdbdat['Plot']
142                        self.viddata['year'] = ttvdbdat['Year']
143                        self.viddata['length'] = ttvdbdat['Runtime']
144                        self.viddata['userrating'] = ttvdbdat['UserRating']
145                        self.cast = tuple(ttvdbdat['Cast'].split(', '))
146                        self.genre = tuple(ttvdbdat['Genres'].split(', '))
147                        self.viddata['inetref'] = ttvdbdat['Seriesid']
148                        self.country = ()
149
150                self.viddata['showlevel'] = 1
151                self.viddata['rating'] = 'NR'
152                self.viddata['coverfile'] = 'No Cover'
153
154        def get_dest(self):
155                if self.ffmt is None:
156                        self.get_fmt()
157                ext = self.source[self.source.rfind('.'):]
158                subpath = self.process_fmt(self.dfmt) + "/" + 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                        destdir = cursor.fetchone()[0]
165                        self.destdb = subpath
166                else:
167                        destdir = self.mythdb.getSetting("VideoStartupDir",hostname=self.jobhost)+'/'
168                        self.destdb = self.dest
169                self.dest = destdir+subpath
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_dfmt(self, fmt):
176                self.dfmt = fmt
177        def set_ffmt(self, fmt):
178                self.ffmt = fmt
179
180        def get_fmt(self):
181                self.ffmt = self.mythdb.getSetting('TV Export File Format')
182                if not self.ffmt:
183                        self.ffmt = '%TITLE% - %SEASON%x%EPISODEPAD% - %SUBTITLE%'
184                self.dfmt = self.mythdb.getSetting('TV Export Directory Format')
185                if not self.dfmt:
186                        self.dfmt = 'Television/%TITLE%'
187
188        def process_fmt(self, fmt):
189                fmt = fmt.replace('%TITLE%',self.viddata['title'])
190                fmt = fmt.replace('%SUBTITLE%',self.viddata['subtitle'])
191                fmt = fmt.replace('%SEASON%',"%d" % self.viddata['season'])
192                fmt = fmt.replace('%SEASONPAD%',"%02d" % self.viddata['season'])
193                fmt = fmt.replace('%EPISODE%',"%d" % self.viddata['episode'])
194                fmt = fmt.replace('%EPISODEPAD%',"%02d" % self.viddata['episode'])
195                fmt = fmt.replace('%YEAR%',self.viddata['year'])
196                fmt = fmt.replace('%DIRECTOR%',self.viddata['director'])
197#               fmt = fmt.replace('%CARDID%',self.prog.cardid)
198#               fmt = fmt.replace('%CARDNAME%',self.prog.cardid)
199#               fmt = fmt.replace('%SOURCEID%',self.prog.cardid)
200#               fmt = fmt.replace('%SOURCENAME%',self.prog.cardid)
201                fmt = fmt.replace('%HOSTNAME%',self.prog.hostname)
202                fmt = fmt.replace('%STORAGEGROUP%',self.prog.storagegroup)
203#               fmt = fmt.replace('%CHANNUM%',self.prog.channum)
204#               fmt = fmt.replace('%CHANNAME%',self.prog.cardid)
205                if len(self.genre):
206                        fmt = fmt.replace('%GENRE%',self.genre[0])
207                else:
208                        fmt = fmt.replace('%GENRE%','')
209#               if len(self.country):
210#                       fmt = fmt.replace('%COUNTRY%',self.country[0])
211#               else:
212#                       fmt = fmt.replace('%COUNTRY%','')
213                return fmt
214
215        def copy(self):
216                cursor = None
217                srcp = None
218                srcsize = None
219                dtime = None
220                self.get_meta()
221                self.get_dest()
222                if self.desthost != self.jobhost:
223                        log.warning('failed','must be run on destination host')
224                        if self.job:
225                                self.job.setComment("Job must be run on destination host")
226                                self.job.setStatus(304)
227                        sys.exit(2)
228
229                log.notice('Starting transfer','from %s:%s to %s' % (self.srchost,self.source,self.dest))
230                stime = time.time()
231                ctime = time.time()
232                if os.access(self.source,os.F_OK):
233                        srcp = open(self.source,'r')
234                else:
235                        srcp = FileTransfer(self.prop)
236                destp = open(self.dest,'w')
237                srcsize = self.prog.filesize
238                destsize = [0,0,0,0,0,0,0,0,0,0]
239                if self.job:
240                        self.job.setStatus(4)
241                tsize = 2**18
242                while tsize == 2**18:
243                        if (srcsize - destp.tell()) < tsize:
244                                tsize = srcsize - destp.tell()
245                        if time.time() - ctime > 4:
246                                ctime = time.time()
247                                destsize.append(destp.tell())
248                                rem = srcsize - destp.tell()
249                                if destsize[0]:
250                                        dtime = 40
251                                else:
252                                        dtime = int(ctime - stime)
253                                rate = (destp.tell() - destsize.pop(0))/dtime
254                                remt = rem/rate
255                                if self.job:
256                                        self.job.setComment("%02d%% complete - %s seconds remaining" % (destsize[9]*100/srcsize, remt))
257                        destp.write(srcp.read(tsize))
258
259                srcp.close()
260                destp.close()
261                log.notice('Transfer complete','%d seconds elapsed' % int(time.time()-stime))
262                if self.job:
263                        self.job.setComment("Complete - %d seconds elapsed" % (int(time.time()-stime)))
264                        self.job.setStatus(256)
265
266
267        def write_meta(self):
268                mythvid = MythVideo()
269                mythtv = MythTV()
270#               pp = pprint.PrettyPrinter(indent=4)
271                self.viddata['filename'] = self.destdb
272                self.viddata['host'] = self.desthost
273
274
275#               pp.pprint(self.viddata)
276                intid = mythvid.getMetadataId(self.destdb)
277                if intid:
278                        mythvid.setMetadata(self.viddata,intid)
279                else:
280                        intid = mythvid.setMetadata(self.viddata)
281#               print("Metadata written to intid %d" % intid)
282#               pp.pprint(self.viddata)
283
284#               print("With cast:")
285#               pp.pprint(self.cast)
286                for name in self.cast:
287                        mythvid.setCast(name, intid)
288#               print("With genre:")
289#               pp.pprint(self.genre)
290#               for genre in self.genre:
291#                       mythvid.setGenre(genre, intid)
292                self.write_image('coverfile',intid)
293                self.write_image('banner',intid)
294                self.write_image('fanart',intid)
295                if self.job:
296                        self.job.setStatus(272)
297
298        def write_image(self,mode,intid):
299                destdir = None
300                SG = {'coverfile':'Coverart','banner':'Banners','fanart':'Fanart'}
301                ttvdb = {'coverfile':'-mP','banner':'-mB','fanart':'-mtF'}
302                mythvid = MythVideo()
303                cursor = self.dbconn.cursor()
304                reslen = cursor.execute("SELECT dirname FROM storagegroup WHERE hostname='%s' and groupname='%s'" % (self.desthost,SG[mode]))
305                if reslen:
306                        destdir = cursor.fetchone()[0]
307                else:
308                        return
309
310                fp = os.popen("%s %s '%s' %d %d" % (self.ttvdb,ttvdb[mode],self.prog.title,self.viddata['season'],self.viddata['episode']))
311                time.sleep(2)
312                res = fp.read()
313                fp.close()
314
315                match = re.match('http://images.thetvdb.com.nyud.net:8080/[a-zA-Z/]*/(?P<file>[a-zA-Z0-9-.]*)',res)
316                if not match:
317                        return
318                path = destdir+match.group('file')
319                if not os.access(path,os.F_OK):
320                        urlretrieve(match.group(),path)
321                if mode == 'coverfile':
322                        if not os.access(destdir+'folder.jpg',os.F_OK):
323                                urlretrieve(match.group(),destdir+'folder.jpg')
324
325                mythvid.setMetadata({mode:match.group('file')},intid)
326
327        def delete():
328                mythtv = MythTV()
329                mythtv.deleteRecording(self.prog)
330
331def usage():
332        print("mythvidexport.py [options] [--chanid=<chanid> --starttime=<starttime> | <jobid>]")
333        print("        This script can be run by specifing the channel and start time directly")
334        print("            or by specifing the ID of a job in jobqueue")
335        print("")
336        print("        Run from the command line through the former:")
337        print("            mythvidexport.py --chanid=1002 --starttime=200907010000")
338        print("        Or from a user script through the latter:")
339        print("            mythvidexport.py %JOBID%")
340        print("")
341        print("    Options:")
342        print("        -h/--help and -f/--helpformat:")
343        print("             return this help, or a listing of available formatting strings")
344        print("        --fformat='<string>' and --dformat='<string>':")
345        print("             override the stored formatting string in the database")
346        print("             if no recording is specified, store format string to the database")
347
348def usage_format():
349        print("The default strings are:")
350        print("    fformat: '%TITLE% - %SUBTITLE%'")
351        print("    dformat: 'Television/%TITLE%'")
352        print("")
353        print("Available strings:")
354        print("    %TITLE%:         series title")
355        print("    %SUBTITLE%:      episode title")
356        print("    %SEASON%:        season number")
357        print("    %EPISODE%:       episode number, padded to 2 digits")
358        print("    %YEAR%:          year")
359        print("    %DIRECTOR%:      director")
360#       print("    %CARDID%:        ID of tuner card used to record show")
361#       print("    %CARDNAME%:      name of tuner card used to record show")
362#       print("    %SOURCEID%:      ID of video source used to record show")
363#       print("    %SOURCENAME%:    name of video source used to record show")
364        print("    %HOSTNAME%:      backend used to record show")
365        print("    %STORAGEGROUP%:  storage group containing recorded show")
366#       print("    %CHANNUM%:       ID of channel used to record show")
367#       print("    %CHANNAME%:      name of channel used to record show")
368        print("    %GENRE%:         first genre listed for recording")
369#       print("    %COUNTRY%:       first country listed for recording")
370
371def main():
372        fformat = None
373        tformat = None
374        jobid = None
375        chanid = None
376        rtime = None
377        skip = False
378        delete = False
379        try:
380                opts, args = getopt.getopt(sys.argv[1:], "hf", ["help","helpformat","fformat=","dformat=","chanid=","starttime=","skip","delete"])
381        except getopt.GetoptError, err:
382                print(str(err))
383                usage()
384                sys.exit(2)
385        for opt, arg in opts:
386                if opt in ("-h", "--help"):
387                        usage()
388                        sys.exit()
389                elif opt in ('-f', '--helpformat'):
390                        usage_format()
391                        sys.exit()
392                elif opt == '--dformat':
393                        dformat = arg
394                elif opt == '--fformat':
395                        fformat = arg
396                elif opt == '--chanid':
397                        chanid = int(arg)
398                elif opt == '--starttime':
399                        rtime = int(arg)
400                elif opt == '--skip':
401                        skip = True
402                elif opt == '--delete':
403                        delete = True
404               
405        if len(args):
406                jobid = int(args[0])
407
408        if chanid and rtime:
409                export = VIDEO(chanid,rtime)
410        elif jobid:
411                export = VIDEO(jobid)
412        elif fformat or dformat:
413                if fformat:
414                        mythdb = MythDB()
415                        mythdb.setSetting('TV Export File Format', fmt)
416                if dformat:
417                        mythdb = MythDB()
418                        mythdb.setSetting('TV Export Directory Format', fmt)
419                sys.exit()
420        else:
421                usage()
422                sys.exit(2)
423
424        if skip:
425                export.get_meta()
426                export.get_dest()
427        else:
428                export.copy()
429        export.write_meta()
430#       if delete:
431#               export.delete()
432
433if __name__ == "__main__":
434        main()