def test_Dataheap_VideoGrabber_002_importMetadata_01(self): """Test 'VideoGrabber.grabInetref' method and 'Video.importMetadata' methods using predefined values. See 'mythvidexport.py' as well. See MythtTV trac tickets #12243 and #12245. Note: Use a video with French accents in title and cast. """ from MythTV import MythDB, Video, VideoGrabber with add_log_flags(): title = u"Le Dernier Métro" castlist = [u"Catherine Deneuve", u"Gérard Depardieu", u"Andréa Ferréol"] inetstr = "tmdb3.py_1716" lang = "fr" filename = title + u".mkv" # create a new Video instance self.mydb = MythDB() vid = Video(db=self.mydb).create ({'title' : title, 'filename': filename, 'host' : self.mydb.getMasterBackend()}) # grab details from www.themoviedb.org grab = VideoGrabber('MOVIE', lang=lang, db=self.mydb) metadata = grab.grabInetref(inetstr) #print(type(metadata)) --> <class 'MythTV.system.VideoMetadata'> # import metadata into Video instance vid.importMetadata(metadata) #print(vid.cast[1].cast) ### ---> Gérard Depardieu # create a list of cast members from metadata vidcastmembers = [c.cast for c in vid.cast] # check for members in cast list self.assertTrue(self.is_sublist(castlist, vidcastmembers)) # delete previously created Video object in database vid.delete() #print(metadata.inetref) # --> 1716 self.assertEqual(metadata.inetref, inetstr.split('_')[-1]) # run the grabber again, but based on a 'VideoMetadata' object new_metadata = grab.grabInetref(metadata) # check if collected metadata are the equal self.assertEqual(new_metadata, metadata)
class VIDEO: def __init__(self, opts, jobid=None): if jobid: self.job = Job(jobid) self.chanid = self.job.chanid self.starttime = self.job.starttime self.job.update(status=Job.STARTING) else: self.job = None self.chanid = opts.chanid self.starttime = opts.starttime self.opts = opts self.db = MythDB() self.log = MythLog(module='mythvidexport.py', db=self.db) # load setting strings self.get_format() # prep objects self.rec = Recorded((self.chanid,self.starttime), db=self.db) self.log(MythLog.GENERAL, MythLog.INFO, 'Using recording', '%s - %s' % (self.rec.title, self.rec.subtitle)) self.vid = Video(db=self.db).create({'title':'', 'filename':'', 'host':gethostname()}) # process data self.get_meta() self.get_dest() # bug fix to work around limitation in the bindings where DBDataRef classes # are mapped to the filename at time of Video element creation. since the # filename is specified as blank when the video is created, the markup # handler is not properly initialized self.vid.markup._refdat = (self.vid.filename,) # save file self.copy() if opts.seekdata: self.copy_seek() if opts.skiplist: self.copy_markup(static.MARKUP.MARK_COMM_START, static.MARKUP.MARK_COMM_END) if opts.cutlist: self.copy_markup(static.MARKUP.MARK_CUT_START, static.MARKUP.MARK_CUT_END) self.vid.update() # delete old file if opts.delete: self.rec.delete() def get_format(self): host = self.db.gethostname() # TV Format if self.opts.tformat: self.tfmt = self.opts.tformat elif self.db.settings[host]['mythvideo.TVexportfmt']: self.tfmt = self.db.settings[host]['mythvideo.TVexportfmt'] else: self.tfmt = 'Television/%TITLE%/Season %SEASON%/'+\ '%TITLE% - S%SEASON%E%EPISODEPAD% - %SUBTITLE%' # Movie Format if self.opts.mformat: self.mfmt = self.opts.mformat elif self.db.settings[host]['mythvideo.MOVIEexportfmt']: self.mfmt = self.db.settings[host]['mythvideo.MOVIEexportfmt'] else: self.mfmt = 'Movies/%TITLE%' # Generic Format if self.opts.gformat: self.gfmt = self.opts.gformat elif self.db.settings[host]['mythvideo.GENERICexportfmt']: self.gfmt = self.db.settings[host]['mythvideo.GENERICexportfmt'] else: self.gfmt = 'Videos/%TITLE%' def get_meta(self): self.vid.hostname = self.db.gethostname() if self.rec.inetref: # good data is available, use it if self.rec.season is not None: self.log(self.log.GENERAL, self.log.INFO, 'Performing TV export with local data.') self.type = 'TV' else: self.log(self.log.GENERAL, self.log.INFO, 'Performing Movie export with local data.') self.type = 'MOVIE' metadata = self.rec.exportMetadata() elif self.opts.listingonly: # force use of local data if self.rec.subtitle: self.log(self.log.GENERAL, self.log.INFO, 'Forcing TV export with local data.') self.type = 'TV' else: self.log(self.log.GENERAL, self.log.INFO, 'Forcing Movie export with local data.') self.type = 'MOVIE' metadata = self.rec.exportMetadata() else: if self.rec.subtitle: # subtitle exists, assume tv show self.type = 'TV' self.log(self.log.GENERAL, self.log.INFO, 'Attempting TV export.') grab = VideoGrabber(self.type) match = grab.sortedSearch(self.rec.title, self.rec.subtitle) else: # assume movie self.type = 'MOVIE' self.log(self.log.GENERAL, self.log.INFO, 'Attempting Movie export.') grab = VideoGrabber(self.type) match = grab.sortedSearch(self.rec.title) if len(match) == 0: # no match found self.log(self.log.GENERAL, self.log.INFO, 'Falling back to generic export.') self.type = 'GENERIC' metadata = self.rec.exportMetadata() elif (len(match) > 1) & (match[0].levenshtein > 0): # multiple matches found, and closest is not exact self.vid.delete() raise MythError('Multiple metadata matches found: '\ +self.rec.title) else: self.log(self.log.GENERAL, self.log.INFO, 'Importing content from', match[0].inetref) metadata = grab.grabInetref(match[0]) self.vid.importMetadata(metadata) self.log(self.log.GENERAL, self.log.INFO, 'Import complete') def get_dest(self): if self.type == 'TV': self.vid.filename = self.process_fmt(self.tfmt) elif self.type == 'MOVIE': self.vid.filename = self.process_fmt(self.mfmt) elif self.type == 'GENERIC': self.vid.filename = self.process_fmt(self.gfmt) def process_fmt(self, fmt): # replace fields from viddata #print self.vid.data ext = '.'+self.rec.basename.rsplit('.',1)[1] rep = ( ('%TITLE%','title','%s'), ('%SUBTITLE%','subtitle','%s'), ('%SEASON%','season','%d'), ('%SEASONPAD%','season','%02d'), ('%EPISODE%','episode','%d'), ('%EPISODEPAD%','episode','%02d'), ('%YEAR%','year','%s'), ('%DIRECTOR%','director','%s')) for tag, data, format in rep: if self.vid[data]: fmt = fmt.replace(tag,format % self.vid[data]) else: fmt = fmt.replace(tag,'') # replace fields from program data rep = ( ('%HOSTNAME%', 'hostname', '%s'), ('%STORAGEGROUP%','storagegroup','%s')) for tag, data, format in rep: data = getattr(self.rec, data) fmt = fmt.replace(tag,format % data) # fmt = fmt.replace('%CARDID%',self.rec.cardid) # fmt = fmt.replace('%CARDNAME%',self.rec.cardid) # fmt = fmt.replace('%SOURCEID%',self.rec.cardid) # fmt = fmt.replace('%SOURCENAME%',self.rec.cardid) # fmt = fmt.replace('%CHANNUM%',self.rec.channum) # fmt = fmt.replace('%CHANNAME%',self.rec.cardid) if len(self.vid.genre): fmt = fmt.replace('%GENRE%',self.vid.genre[0].genre) else: fmt = fmt.replace('%GENRE%','') # if len(self.country): # fmt = fmt.replace('%COUNTRY%',self.country[0]) # else: # fmt = fmt.replace('%COUNTRY%','') return fmt+ext def copy(self): stime = time.time() srcsize = self.rec.filesize htime = [stime,stime,stime,stime] self.log(MythLog.GENERAL|MythLog.FILE, MythLog.INFO, "Copying myth://%s@%s/%s"\ % (self.rec.storagegroup, self.rec.hostname, self.rec.basename)\ +" to myth://Videos@%s/%s"\ % (self.vid.host, self.vid.filename)) srcfp = self.rec.open('r') dstfp = self.vid.open('w') if self.job: self.job.setStatus(Job.RUNNING) tsize = 2**24 while tsize == 2**24: tsize = min(tsize, srcsize - dstfp.tell()) dstfp.write(srcfp.read(tsize)) htime.append(time.time()) rate = float(tsize*4)/(time.time()-htime.pop(0)) remt = (srcsize-dstfp.tell())/rate if self.job: self.job.setComment("%02d%% complete - %d seconds remaining" %\ (dstfp.tell()*100/srcsize, remt)) srcfp.close() dstfp.close() self.vid.hash = self.vid.getHash() self.log(MythLog.GENERAL|MythLog.FILE, MythLog.INFO, "Transfer Complete", "%d seconds elapsed" % int(time.time()-stime)) if self.opts.reallysafe: if self.job: self.job.setComment("Checking file hashes") self.log(MythLog.GENERAL|MythLog.FILE, MythLog.INFO, "Checking file hashes.") srchash = hashfile(self.rec.open('r')) dsthash = hashfile(self.rec.open('r')) if srchash != dsthash: raise MythError('Source hash (%s) does not match destination hash (%s)' \ % (srchash, dsthash)) elif self.opts.safe: self.log(MythLog.GENERAL|MythLog.FILE, MythLog.INFO, "Checking file sizes.") be = MythBE(db=self.vid._db) try: srcsize = be.getSGFile(self.rec.hostname, self.rec.storagegroup, \ self.rec.basename)[1] dstsize = be.getSGFile(self.vid.host, 'Videos', self.vid.filename)[1] except: raise MythError('Could not query file size from backend') if srcsize != dstsize: raise MythError('Source size (%d) does not match destination size (%d)' \ % (srcsize, dstsize)) if self.job: self.job.setComment("Complete - %d seconds elapsed" % \ (int(time.time()-stime))) self.job.setStatus(Job.FINISHED) def copy_seek(self): for seek in self.rec.seek: self.vid.markup.add(seek.mark, seek.offset, seek.type) def copy_markup(self, start, stop): for mark in self.rec.markup: if mark.type in (start, stop): self.vid.markup.add(mark.mark, 0, mark.type)
class VIDEO: def __init__(self, opts, jobid=None): if jobid: self.job = Job(jobid) self.chanid = self.job.chanid self.starttime = self.job.starttime self.job.update(status=3) else: self.job = None self.chanid = opts.chanid self.starttime = opts.starttime self.opts = opts self.db = MythDB() self.log = MythLog(module='mythvidexport.py', db=self.db) # load setting strings self.get_format() # prep objects self.rec = Recorded((self.chanid, self.starttime), db=self.db) self.log(MythLog.IMPORTANT, 'Using recording', '%s - %s' % (self.rec.title, self.rec.subtitle)) self.vid = Video(db=self.db).create({'title':'', 'filename':'', 'host':gethostname()}) # process data self.get_meta() self.get_dest() # kludgy fix for issue with altered filenames in the bindings self.vid.markup._refdat = (self.vid.filename,) # save file self.copy() if opts.seekdata: self.copy_seek() if opts.skiplist: self.copy_markup(static.MARKUP.MARK_COMM_START, static.MARKUP.MARK_COMM_END) if opts.cutlist: self.copy_markup(static.MARKUP.MARK_CUT_START, static.MARKUP.MARK_CUT_END) self.vid.update() def get_format(self): host = self.db.gethostname() # TV Format if self.opts.tformat: self.tfmt = self.opts.tformat elif self.db.settings[host]['mythvideo.TVexportfmt']: self.tfmt = self.db.settings[host]['mythvideo.TVexportfmt'] else: self.tfmt = 'Television/%TITLE%/Season %SEASON%/' + \ '%TITLE% - S%SEASON%E%EPISODEPAD% - %SUBTITLE%' # Movie Format if self.opts.mformat: self.mfmt = self.opts.mformat elif self.db.settings[host]['mythvideo.MOVIEexportfmt']: self.mfmt = self.db.settings[host]['mythvideo.MOVIEexportfmt'] else: self.mfmt = 'Movies/%TITLE%' # Generic Format if self.opts.gformat: self.gfmt = self.opts.gformat elif self.db.settings[host]['mythvideo.GENERICexportfmt']: self.gfmt = self.db.settings[host]['mythvideo.GENERICexportfmt'] else: self.gfmt = 'Videos/%TITLE%' def get_meta(self): self.vid.hostname = self.db.gethostname() if self.rec.subtitle: # subtitle exists, assume tv show self.type = 'TV' self.log(self.log.IMPORTANT, 'Attempting TV export.') if self.opts.listingonly: self.log(self.log.IMPORTANT, 'Forcing listing data only.') self.get_generic(False) return grab = VideoGrabber(self.type) match = grab.sortedSearch(self.rec.title, self.rec.subtitle) else: # assume movie self.type = 'MOVIE' self.log(self.log.IMPORTANT, 'Attempting Movie export.') if self.opts.listingonly: self.log(self.log.IMPORTANT, 'Forcing listing data only.') self.get_generic(False) return grab = VideoGrabber(self.type) match = grab.sortedSearch(self.rec.title) if len(match) == 0: # no match found self.log(self.log.IMPORTANT, 'Falling back to generic export.') self.get_generic() elif (len(match) > 1) & (match[0].levenshtein > 0): # multiple matches found, and closest is not exact self.vid.delete() raise MythError('Multiple metadata matches found: '\ + self.rec.title) else: self.log(self.log.IMPORTANT, 'Importing content from', match[0].inetref) self.vid.importMetadata(grab.grabInetref(match[0])) def get_generic(self, name_as_generic=True): self.vid.title = self.rec.title if self.rec.subtitle: self.vid.subtitle = self.rec.subtitle if self.rec.description: self.vid.plot = self.rec.description if self.rec.originalairdate: self.vid.year = self.rec.originalairdate.year self.vid.releasedate = self.rec.originalairdate lsec = (self.rec.endtime - self.rec.starttime).seconds self.vid.length = str(lsec / 60) for member in self.rec.cast: if member.role == 'director': self.vid.director = member.name elif member.role == 'actor': self.vid.cast.append(member.name) if name_as_generic: self.type = 'GENERIC' def get_dest(self): if self.type == 'TV': self.vid.filename = self.process_fmt(self.tfmt) elif self.type == 'MOVIE': self.vid.filename = self.process_fmt(self.mfmt) elif self.type == 'GENERIC': self.vid.filename = self.process_fmt(self.gfmt) def process_fmt(self, fmt): # replace fields from viddata # print self.vid.data ext = '.' + self.rec.basename.rsplit('.', 1)[1] rep = (('%TITLE%', 'title', '%s'), ('%SUBTITLE%', 'subtitle', '%s'), ('%SEASON%', 'season', '%d'), ('%SEASONPAD%', 'season', '%02d'), ('%EPISODE%', 'episode', '%d'), ('%EPISODEPAD%', 'episode', '%02d'), ('%YEAR%', 'year', '%s'), ('%DIRECTOR%', 'director', '%s')) for tag, data, format in rep: if self.vid[data]: fmt = fmt.replace(tag, format % self.vid[data]) else: fmt = fmt.replace(tag, '') # replace fields from program data rep = (('%HOSTNAME', 'hostname', '%s'), ('%STORAGEGROUP%', 'storagegroup', '%s')) for tag, data, format in rep: data = getattr(self.rec, data) fmt = fmt.replace(tag, format % data) # fmt = fmt.replace('%CARDID%',self.rec.cardid) # fmt = fmt.replace('%CARDNAME%',self.rec.cardid) # fmt = fmt.replace('%SOURCEID%',self.rec.cardid) # fmt = fmt.replace('%SOURCENAME%',self.rec.cardid) # fmt = fmt.replace('%CHANNUM%',self.rec.channum) # fmt = fmt.replace('%CHANNAME%',self.rec.cardid) if len(self.vid.genre): fmt = fmt.replace('%GENRE%', self.vid.genre[0].genre) else: fmt = fmt.replace('%GENRE%', '') # if len(self.country): # fmt = fmt.replace('%COUNTRY%',self.country[0]) # else: # fmt = fmt.replace('%COUNTRY%','') return fmt + ext def copy(self): stime = time.time() srcsize = self.rec.filesize htime = [stime, stime, stime, stime] self.log.log(MythLog.IMPORTANT | MythLog.FILE, "Copying myth://%s@%s/%s"\ % (self.rec.storagegroup, self.rec.hostname, self.rec.basename)\ + " to myth://Videos@%s/%s"\ % (self.vid.host, self.vid.filename)) srcfp = self.rec.open('r') dstfp = self.vid.open('w') if self.job: self.job.setStatus(4) tsize = 2 ** 24 while tsize == 2 ** 24: tsize = min(tsize, srcsize - dstfp.tell()) dstfp.write(srcfp.read(tsize)) htime.append(time.time()) rate = float(tsize * 4) / (time.time() - htime.pop(0)) remt = (srcsize - dstfp.tell()) / rate if self.job: self.job.setComment("%02d%% complete - %d seconds remaining" % \ (dstfp.tell() * 100 / srcsize, remt)) srcfp.close() dstfp.close() self.vid.hash = self.vid.getHash() self.log(MythLog.IMPORTANT | MythLog.FILE, "Transfer Complete", "%d seconds elapsed" % int(time.time() - stime)) if self.job: self.job.setComment("Complete - %d seconds elapsed" % \ (int(time.time() - stime))) self.job.setStatus(256) def copy_seek(self): for seek in self.rec.seek: self.vid.markup.add(seek.mark, seek.offset, seek.type) def copy_markup(self, start, stop): for mark in self.rec.markup: if mark.type in (start, stop): self.vid.markup.add(mark.mark, 0, mark.type)
class VIDEO: def __init__(self, opts, jobid=None): if jobid: self.job = Job(jobid) self.chanid = self.job.chanid self.starttime = self.job.starttime self.job.update(status=Job.STARTING) else: self.job = None self.chanid = opts.chanid self.starttime = opts.starttime self.opts = opts self.db = MythDB() self.log = MythLog(module='mythvidexport.py', db=self.db) # load setting strings self.get_format() # prep objects self.rec = Recorded((self.chanid, self.starttime), db=self.db) self.log( MythLog.GENERAL, MythLog.INFO, 'Using recording', '%s - %s' % (self.rec.title.encode('utf-8'), self.rec.subtitle.encode('utf-8'))) self.vid = Video(db=self.db).create({ 'title': '', 'filename': '', 'host': gethostname() }) # process data self.get_meta() self.get_dest() # bug fix to work around limitation in the bindings where DBDataRef classes # are mapped to the filename at time of Video element creation. since the # filename is specified as blank when the video is created, the markup # handler is not properly initialized self.vid.markup._refdat = (self.vid.filename, ) # save file self.copy() if opts.seekdata: self.copy_seek() if opts.skiplist: self.copy_markup(static.MARKUP.MARK_COMM_START, static.MARKUP.MARK_COMM_END) if opts.cutlist: self.copy_markup(static.MARKUP.MARK_CUT_START, static.MARKUP.MARK_CUT_END) self.vid.update() # delete old file if opts.delete: self.rec.delete() def get_format(self): host = self.db.gethostname() # TV Format if self.opts.tformat: self.tfmt = self.opts.tformat elif self.db.settings[host]['mythvideo.TVexportfmt']: self.tfmt = self.db.settings[host]['mythvideo.TVexportfmt'] else: self.tfmt = 'Television/%TITLE%/Season %SEASON%/'+\ '%TITLE% - S%SEASON%E%EPISODEPAD% - %SUBTITLE%' # Movie Format if self.opts.mformat: self.mfmt = self.opts.mformat elif self.db.settings[host]['mythvideo.MOVIEexportfmt']: self.mfmt = self.db.settings[host]['mythvideo.MOVIEexportfmt'] else: self.mfmt = 'Movies/%TITLE%' # Generic Format if self.opts.gformat: self.gfmt = self.opts.gformat elif self.db.settings[host]['mythvideo.GENERICexportfmt']: self.gfmt = self.db.settings[host]['mythvideo.GENERICexportfmt'] else: self.gfmt = 'Videos/%TITLE%' def get_meta(self): self.vid.hostname = self.db.gethostname() if self.rec.inetref: # good data is available, use it if self.rec.season > 0 or self.rec.episode > 0: self.log(self.log.GENERAL, self.log.INFO, 'Performing TV export with local data.') self.type = 'TV' grab = VideoGrabber(self.type) metadata = grab.grabInetref(self.rec.inetref, self.rec.season, self.rec.episode) else: self.log(self.log.GENERAL, self.log.INFO, 'Performing Movie export with local data.') self.type = 'MOVIE' grab = VideoGrabber(self.type) metadata = grab.grabInetref(self.rec.inetref) elif self.opts.listingonly: # force use of local data if self.rec.subtitle: self.log(self.log.GENERAL, self.log.INFO, 'Forcing TV export with local data.') self.type = 'TV' else: self.log(self.log.GENERAL, self.log.INFO, 'Forcing Movie export with local data.') self.type = 'MOVIE' metadata = self.rec.exportMetadata() else: if self.rec.subtitle: # subtitle exists, assume tv show self.type = 'TV' self.log(self.log.GENERAL, self.log.INFO, 'Attempting TV export.') grab = VideoGrabber(self.type) match = grab.sortedSearch(self.rec.title, self.rec.subtitle) else: # assume movie self.type = 'MOVIE' self.log(self.log.GENERAL, self.log.INFO, 'Attempting Movie export.') grab = VideoGrabber(self.type) match = grab.sortedSearch(self.rec.title) if len(match) == 0: # no match found self.log(self.log.GENERAL, self.log.INFO, 'Falling back to generic export.') self.type = 'GENERIC' metadata = self.rec.exportMetadata() elif (len(match) > 1) & (match[0].levenshtein > 0): # multiple matches found, and closest is not exact self.vid.delete() raise MythError('Multiple metadata matches found: '\ +self.rec.title) else: self.log(self.log.GENERAL, self.log.INFO, 'Importing content from', match[0].inetref) metadata = grab.grabInetref(match[0]) self.vid.importMetadata(metadata) self.log(self.log.GENERAL, self.log.INFO, 'Import complete') def get_dest(self): if self.type == 'TV': self.vid.filename = self.process_fmt(self.tfmt) elif self.type == 'MOVIE': self.vid.filename = self.process_fmt(self.mfmt) elif self.type == 'GENERIC': self.vid.filename = self.process_fmt(self.gfmt) def process_fmt(self, fmt): # replace fields from viddata #print self.vid.data ext = '.' + self.rec.basename.rsplit('.', 1)[1] rep = (('%TITLE%', 'title', '%s'), ('%SUBTITLE%', 'subtitle', '%s'), ('%SEASON%', 'season', '%d'), ('%SEASONPAD%', 'season', '%02d'), ('%EPISODE%', 'episode', '%d'), ('%EPISODEPAD%', 'episode', '%02d'), ('%YEAR%', 'year', '%s'), ('%DIRECTOR%', 'director', '%s')) for tag, data, format in rep: if self.vid[data]: fmt = fmt.replace(tag, format % self.vid[data]) else: fmt = fmt.replace(tag, '') # replace fields from program data rep = (('%HOSTNAME%', 'hostname', '%s'), ('%STORAGEGROUP%', 'storagegroup', '%s')) for tag, data, format in rep: data = getattr(self.rec, data) fmt = fmt.replace(tag, format % data) # fmt = fmt.replace('%CARDID%',self.rec.cardid) # fmt = fmt.replace('%CARDNAME%',self.rec.cardid) # fmt = fmt.replace('%SOURCEID%',self.rec.cardid) # fmt = fmt.replace('%SOURCENAME%',self.rec.cardid) # fmt = fmt.replace('%CHANNUM%',self.rec.channum) # fmt = fmt.replace('%CHANNAME%',self.rec.cardid) if len(self.vid.genre): fmt = fmt.replace('%GENRE%', self.vid.genre[0].genre) else: fmt = fmt.replace('%GENRE%', '') # if len(self.country): # fmt = fmt.replace('%COUNTRY%',self.country[0]) # else: # fmt = fmt.replace('%COUNTRY%','') return fmt + ext def copy(self): stime = time.time() srcsize = self.rec.filesize htime = [stime, stime, stime, stime] self.log(MythLog.GENERAL|MythLog.FILE, MythLog.INFO, "Copying myth://%s@%s/%s"\ % (self.rec.storagegroup, self.rec.hostname, self.rec.basename)\ +" to myth://Videos@%s/%s"\ % (self.vid.host, self.vid.filename)) srcfp = self.rec.open('r') dstfp = self.vid.open('w', nooverwrite=True) if self.job: self.job.setStatus(Job.RUNNING) tsize = 2**24 while tsize == 2**24: tsize = min(tsize, srcsize - dstfp.tell()) dstfp.write(srcfp.read(tsize)) htime.append(time.time()) rate = float(tsize * 4) / (time.time() - htime.pop(0)) remt = (srcsize - dstfp.tell()) / rate if self.job: self.job.setComment("%02d%% complete - %d seconds remaining" %\ (dstfp.tell()*100/srcsize, remt)) srcfp.close() dstfp.close() self.vid.hash = self.vid.getHash() self.log(MythLog.GENERAL | MythLog.FILE, MythLog.INFO, "Transfer Complete", "%d seconds elapsed" % int(time.time() - stime)) if self.opts.reallysafe: if self.job: self.job.setComment("Checking file hashes") self.log(MythLog.GENERAL | MythLog.FILE, MythLog.INFO, "Checking file hashes.") srchash = hashfile(self.rec.open('r')) dsthash = hashfile(self.vid.open('r')) if srchash != dsthash: raise MythError('Source hash (%s) does not match destination hash (%s)' \ % (srchash, dsthash)) elif self.opts.safe: self.log(MythLog.GENERAL | MythLog.FILE, MythLog.INFO, "Checking file sizes.") be = MythBE(db=self.vid._db) try: srcsize = be.getSGFile(self.rec.hostname, self.rec.storagegroup, \ self.rec.basename)[1] dstsize = be.getSGFile(self.vid.host, 'Videos', self.vid.filename)[1] except: raise MythError('Could not query file size from backend') if srcsize != dstsize: raise MythError('Source size (%d) does not match destination size (%d)' \ % (srcsize, dstsize)) if self.job: self.job.setComment("Complete - %d seconds elapsed" % \ (int(time.time()-stime))) self.job.setStatus(Job.FINISHED) def copy_seek(self): for seek in self.rec.seek: self.vid.markup.add(seek.mark, seek.offset, seek.type) def copy_markup(self, start, stop): for mark in self.rec.markup: if mark.type in (start, stop): self.vid.markup.add(mark.mark, 0, mark.type)
class VIDEO: def __init__(self, opts, jobid=None): if jobid: self.job = Job(jobid) self.chanid = self.job.chanid self.starttime = self.job.starttime self.job.update(status=3) else: self.job = None self.chanid = opts.chanid self.starttime = opts.starttime self.opts = opts self.db = MythDB() self.log = MythLog(module='mythvidexport.py', db=self.db) # load setting strings self.get_format() # prep objects self.rec = Recorded((self.chanid, self.starttime), db=self.db) self.log(MythLog.IMPORTANT, 'Using recording', '%s - %s' % (self.rec.title, self.rec.subtitle)) self.vid = Video(db=self.db).create({ 'title': '', 'filename': '', 'host': gethostname() }) # process data self.get_meta() self.get_dest() # save file self.copy() if opts.seekdata: self.copy_seek() if opts.skiplist: self.copy_markup(static.MARKUP.MARK_COMM_START, static.MARKUP.MARK_COMM_END) if opts.cutlist: self.copy_markup(static.MARKUP.MARK_CUT_START, static.MARKUP.MARK_CUT_END) self.vid.update() def get_format(self): host = self.db.gethostname() # TV Format if self.opts.tformat: self.tfmt = self.opts.tformat elif self.db.settings[host]['mythvideo.TVexportfmt']: self.tfmt = self.db.settings[host]['mythvideo.TVexportfmt'] else: self.tfmt = 'Television/%TITLE%/Season %SEASON%/'+\ '%TITLE% - S%SEASON%E%EPISODEPAD% - %SUBTITLE%' # Movie Format if self.opts.mformat: self.mfmt = self.opts.mformat elif self.db.settings[host]['mythvideo.MOVIEexportfmt']: self.mfmt = self.db.settings[host]['mythvideo.MOVIEexportfmt'] else: self.mfmt = 'Movies/%TITLE%' # Generic Format if self.opts.gformat: self.gfmt = self.opts.gformat elif self.db.settings[host]['mythvideo.GENERICexportfmt']: self.gfmt = self.db.settings[host]['mythvideo.GENERICexportfmt'] else: self.gfmt = 'Videos/%TITLE%' def get_meta(self): self.vid.hostname = self.db.gethostname() if self.rec.subtitle: # subtitle exists, assume tv show self.type = 'TV' self.log(self.log.IMPORTANT, 'Attempting TV export.') if self.opts.listingonly: self.log(self.log.IMPORTANT, 'Forcing listing data only.') self.get_generic(False) return grab = VideoGrabber(self.type) match = grab.sortedSearch(self.rec.title, self.rec.subtitle) else: # assume movie self.type = 'MOVIE' self.log(self.log.IMPORTANT, 'Attempting Movie export.') if self.opts.listingonly: self.log(self.log.IMPORTANT, 'Forcing listing data only.') self.get_generic(False) return grab = VideoGrabber(self.type) match = grab.sortedSearch(self.rec.title) if len(match) == 0: # no match found self.log(self.log.IMPORTANT, 'Falling back to generic export.') self.get_generic() elif (len(match) > 1) & (match[0].levenshtein > 0): # multiple matches found, and closest is not exact self.vid.delete() raise MythError('Multiple metadata matches found: '\ +self.rec.title) else: self.log(self.log.IMPORTANT, 'Importing content from', match[0].inetref) self.vid.importMetadata(grab.grabInetref(match[0])) def get_generic(self, name_as_generic=True): self.vid.title = self.rec.title if self.rec.subtitle: self.vid.subtitle = self.rec.subtitle if self.rec.description: self.vid.plot = self.rec.description if self.rec.originalairdate: self.vid.year = self.rec.originalairdate.year self.vid.releasedate = self.rec.originalairdate lsec = (self.rec.endtime - self.rec.starttime).seconds self.vid.length = str(lsec / 60) for member in self.rec.cast: if member.role == 'director': self.vid.director = member.name elif member.role == 'actor': self.vid.cast.append(member.name) if name_as_generic: self.type = 'GENERIC' def get_dest(self): if self.type == 'TV': self.vid.filename = self.process_fmt(self.tfmt) elif self.type == 'MOVIE': self.vid.filename = self.process_fmt(self.mfmt) elif self.type == 'GENERIC': self.vid.filename = self.process_fmt(self.gfmt) def process_fmt(self, fmt): # replace fields from viddata #print self.vid.data ext = '.' + self.rec.basename.rsplit('.', 1)[1] rep = (('%TITLE%', 'title', '%s'), ('%SUBTITLE%', 'subtitle', '%s'), ('%SEASON%', 'season', '%d'), ('%SEASONPAD%', 'season', '%02d'), ('%EPISODE%', 'episode', '%d'), ('%EPISODEPAD%', 'episode', '%02d'), ('%YEAR%', 'year', '%s'), ('%DIRECTOR%', 'director', '%s')) for tag, data, format in rep: if self.vid[data]: fmt = fmt.replace(tag, format % self.vid[data]) else: fmt = fmt.replace(tag, '') # replace fields from program data rep = (('%HOSTNAME', 'hostname', '%s'), ('%STORAGEGROUP%', 'storagegroup', '%s')) for tag, data, format in rep: data = getattr(self.rec, data) fmt = fmt.replace(tag, format % data) # fmt = fmt.replace('%CARDID%',self.rec.cardid) # fmt = fmt.replace('%CARDNAME%',self.rec.cardid) # fmt = fmt.replace('%SOURCEID%',self.rec.cardid) # fmt = fmt.replace('%SOURCENAME%',self.rec.cardid) # fmt = fmt.replace('%CHANNUM%',self.rec.channum) # fmt = fmt.replace('%CHANNAME%',self.rec.cardid) if len(self.vid.genre): fmt = fmt.replace('%GENRE%', self.vid.genre[0].genre) else: fmt = fmt.replace('%GENRE%', '') # if len(self.country): # fmt = fmt.replace('%COUNTRY%',self.country[0]) # else: # fmt = fmt.replace('%COUNTRY%','') return fmt + ext def copy(self): stime = time.time() srcsize = self.rec.filesize htime = [stime, stime, stime, stime] self.log.log(MythLog.IMPORTANT|MythLog.FILE, "Copying myth://%s@%s/%s"\ % (self.rec.storagegroup, self.rec.hostname, self.rec.basename)\ +" to myth://Videos@%s/%s"\ % (self.vid.host, self.vid.filename)) srcfp = self.rec.open('r') dstfp = self.vid.open('w') if self.job: self.job.setStatus(4) tsize = 2**24 while tsize == 2**24: tsize = min(tsize, srcsize - dstfp.tell()) dstfp.write(srcfp.read(tsize)) htime.append(time.time()) rate = float(tsize * 4) / (time.time() - htime.pop(0)) remt = (srcsize - dstfp.tell()) / rate if self.job: self.job.setComment("%02d%% complete - %d seconds remaining" %\ (dstfp.tell()*100/srcsize, remt)) srcfp.close() dstfp.close() self.vid.hash = self.vid.getHash() self.log(MythLog.IMPORTANT | MythLog.FILE, "Transfer Complete", "%d seconds elapsed" % int(time.time() - stime)) if self.job: self.job.setComment("Complete - %d seconds elapsed" % \ (int(time.time()-stime))) self.job.setStatus(256) def copy_seek(self): for seek in self.rec.seek: self.vid.markup.add(seek.mark, seek.offset, seek.type) def copy_markup(self, start, stop): for mark in self.rec.markup: if mark.type in (start, stop): self.vid.markup.add(mark.mark, 0, mark.type)
class VIDEO: def __init__(self, opts, jobid=None): # Setup for the job to run if jobid: self.thisJob = Job(jobid) self.chanID = self.thisJob.chanid self.startTime = self.thisJob.starttime self.thisJob.update(status=Job.STARTING) # If no job ID given, must be a command line run else: self.thisJob = jobid self.chanID = opts.chanid self.startTime = opts.startdate + " " + opts.starttime + opts.offset self.opts = opts self.type = "none" self.db = MythDB() self.log = MythLog(module='Myth-Rec-to-Vid.py', db=self.db) # Capture the backend host name self.host = self.db.gethostname() # prep objects self.rec = Recorded((self.chanID, self.startTime), db=self.db) self.log( MythLog.GENERAL, MythLog.INFO, 'Using recording', '%s - %s' % (self.rec.title.encode('utf-8'), self.rec.subtitle.encode('utf-8'))) self.vid = Video(db=self.db).create({ 'title': '', 'filename': '', 'host': self.host }) self.bend = MythBE(db=self.db) def check_hash(self): self.log(self.log.GENERAL, self.log.INFO, 'Performing copy validation.') srchash = self.bend.getHash(self.rec.basename, self.rec.storagegroup) dsthash = self.bend.getHash(self.vid.filename, 'Videos') if srchash != dsthash: return False else: return True def copy(self): stime = time.time() srcsize = self.rec.filesize htime = [stime, stime, stime, stime] self.log(MythLog.GENERAL|MythLog.FILE, MythLog.INFO, "Copying myth://%s@%s/%s"\ % (self.rec.storagegroup, self.rec.hostname, self.rec.basename)\ +" to myth://Videos@%s/%s"\ % (self.host, self.vid.filename)) srcfp = self.rec.open('r') dstfp = self.vid.open('w') if self.thisJob: self.set_job_status(Job.RUNNING) tsize = 2**24 while tsize == 2**24: tsize = min(tsize, srcsize - dstfp.tell()) dstfp.write(srcfp.read(tsize)) htime.append(time.time()) rate = float(tsize * 4) / (time.time() - htime.pop(0)) remt = (srcsize - dstfp.tell()) / rate if self.thisJob: self.thisJob.setComment("%02d%% complete - %d seconds remaining" %\ (dstfp.tell()*100/srcsize, remt)) srcfp.close() dstfp.close() self.vid.hash = self.vid.getHash() self.log(MythLog.GENERAL | MythLog.FILE, MythLog.INFO, "Transfer Complete", "%d seconds elapsed" % int(time.time() - stime)) if self.thisJob: self.thisJob.setComment("Complete - %d seconds elapsed" % \ (int(time.time()-stime))) def copy_markup(self, start, stop): for mark in self.rec.markup: if mark.type in (start, stop): self.vid.markup.add(mark.mark, 0, mark.type) def copy_seek(self): for seek in self.rec.seek: self.vid.markup.add(seek.mark, seek.offset, seek.type) def delete_vid(self): self.vid.delete() def delete_rec(self): self.rec.delete() def dup_check(self): self.log(MythLog.GENERAL, MythLog.INFO, 'Processing new file name ', '%s' % (self.vid.filename)) self.log( MythLog.GENERAL, MythLog.INFO, 'Checking for duplication of ', '%s - %s' % (self.rec.title.encode('utf-8'), self.rec.subtitle.encode('utf-8'))) if self.bend.fileExists(self.vid.filename, 'Videos'): self.log(MythLog.GENERAL, MythLog.INFO, 'Recording already exists in Myth Videos') if self.thisJob: self.thisJob.setComment( "Action would result in duplicate entry") return True else: self.log( MythLog.GENERAL, MythLog.INFO, 'No duplication found for ', '%s - %s' % (self.rec.title.encode('utf-8'), self.rec.subtitle.encode('utf-8'))) return False def get_dest(self): if self.type == 'TV': self.vid.filename = self.process_fmt(TVFMT) elif self.type == 'MOVIE': self.vid.filename = self.process_fmt(MVFMT) self.vid.markup._refdat = (self.vid.filename, ) def get_meta(self): import_info = 'Listing only MetaData import complete' metadata = self.rec.exportMetadata() yrInfo = self.rec.getProgram() metadata['year'] = yrInfo.get('year') self.vid.importMetadata(metadata) if self.type == 'MOVIE': grab = VideoGrabber('Movie') results = grab.sortedSearch(self.rec.title) if len(results) > 0: for i in results: if i.year == yrInfo.get( 'year') and i.title == self.rec.get('title'): self.vid.importMetadata(i) match = grab.grabInetref(i.get('inetref')) length = len(match.people) for p in range(length - 2): self.vid.cast.add(match.people[p].get('name')) self.vid.director = match.people[length - 1].get('name') import_info = 'Full MetaData Import complete' else: grab = VideoGrabber('TV') results = grab.sortedSearch(self.rec.title, self.rec.subtitle) if len(results) > 0: for i in results: if i.title == self.rec.get( 'title') and i.subtitle == self.rec.get( 'subtitle'): self.vid.importMetadata(i) match = grab.grabInetref(grab.grabInetref(i.get('inetref'), \ season=i.get('season'),episode=i.get('episode'))) length = len(match.people) for p in range(length - 2): self.vid.cast.add(match.people[p].get('name')) self.vid.director = match.people[length - 1].get('name') import_info = 'Full MetaData Import complete' self.vid.category = self.rec.get('category') self.log(self.log.GENERAL, self.log.INFO, import_info) def get_type(self): if self.rec.seriesid != None and self.rec.programid[:2] != 'MV': self.type = 'TV' self.log(self.log.GENERAL, self.log.INFO, 'Performing TV type migration.') else: self.type = 'MOVIE' self.log(self.log.GENERAL, self.log.INFO, 'Performing Movie type migration.') def process_fmt(self, fmt): # replace fields from viddata ext = '.' + self.rec.basename.rsplit('.', 1)[1] rep = (('%TITLE%', 'title', '%s'), ('%SUBTITLE%', 'subtitle', '%s'), ('%SEASON%', 'season', '%d'), ('%SEASONPAD%', 'season', '%02d'), ('%EPISODE%', 'episode', '%d'), ('%EPISODEPAD%', 'episode', '%02d'), ('%YEAR%', 'year', '%s'), ('%DIRECTOR%', 'director', '%s')) for tag, data, format in rep: if self.vid[data]: fmt = fmt.replace(tag, format % self.vid[data]) else: fmt = fmt.replace(tag, '') # replace fields from program data rep = (('%HOSTNAME%', 'hostname', '%s'), ('%STORAGEGROUP%', 'storagegroup', '%s')) for tag, data, format in rep: data = getattr(self.rec, data) fmt = fmt.replace(tag, format % data) if len(self.vid.genre): fmt = fmt.replace('%GENRE%', self.vid.genre[0].genre) else: fmt = fmt.replace('%GENRE%', '') return fmt + ext def set_job_status(self, status): self.thisJob.setStatus(status) def set_vid_hash(self): self.vid.hash = self.vid.getHash() def update_vid(self): self.vid.update()
class VIDEO: def __init__(self, opts, jobid=None): # Setup for the job to run if jobid: self.thisJob = Job(jobid) self.chanID = self.thisJob.chanid self.startTime = self.thisJob.starttime self.thisJob.update(status=Job.STARTING) # If no job ID given, must be a command line run else: self.thisJob = jobid self.chanID = opts.chanid self.startTime = opts.startdate + " " + opts.starttime + opts.offset self.opts = opts self.type = "none" self.db = MythDB() self.log = MythLog(module='Myth-Rec-to-Vid.py', db=self.db) # Capture the backend host name self.host = self.db.gethostname() # prep objects self.rec = Recorded((self.chanID,self.startTime), db=self.db) self.log(MythLog.GENERAL, MythLog.INFO, 'Using recording', '%s - %s' % (self.rec.title.encode('utf-8'), self.rec.subtitle.encode('utf-8'))) self.vid = Video(db=self.db).create({'title':'', 'filename':'', 'host':self.host}) self.bend = MythBE(db=self.db) def check_hash(self): self.log(self.log.GENERAL, self.log.INFO, 'Performing copy validation.') srchash = self.bend.getHash(self.rec.basename, self.rec.storagegroup) dsthash = self.bend.getHash(self.vid.filename, 'Videos') if srchash != dsthash: return False else: return True def copy(self): stime = time.time() srcsize = self.rec.filesize htime = [stime,stime,stime,stime] self.log(MythLog.GENERAL|MythLog.FILE, MythLog.INFO, "Copying myth://%s@%s/%s"\ % (self.rec.storagegroup, self.rec.hostname, self.rec.basename)\ +" to myth://Videos@%s/%s"\ % (self.host, self.vid.filename)) srcfp = self.rec.open('r') dstfp = self.vid.open('w') if self.thisJob: self.set_job_status(Job.RUNNING) tsize = 2**24 while tsize == 2**24: tsize = min(tsize, srcsize - dstfp.tell()) dstfp.write(srcfp.read(tsize)) htime.append(time.time()) rate = float(tsize*4)/(time.time()-htime.pop(0)) remt = (srcsize-dstfp.tell())/rate if self.thisJob: self.thisJob.setComment("%02d%% complete - %d seconds remaining" %\ (dstfp.tell()*100/srcsize, remt)) srcfp.close() dstfp.close() self.vid.hash = self.vid.getHash() self.log(MythLog.GENERAL|MythLog.FILE, MythLog.INFO, "Transfer Complete", "%d seconds elapsed" % int(time.time()-stime)) if self.thisJob: self.thisJob.setComment("Complete - %d seconds elapsed" % \ (int(time.time()-stime))) def copy_markup(self, start, stop): for mark in self.rec.markup: if mark.type in (start, stop): self.vid.markup.add(mark.mark, 0, mark.type) def copy_seek(self): for seek in self.rec.seek: self.vid.markup.add(seek.mark, seek.offset, seek.type) def delete_vid(self): self.vid.delete() def delete_rec(self): self.rec.delete() def dup_check(self): self.log(MythLog.GENERAL, MythLog.INFO, 'Processing new file name ', '%s' % (self.vid.filename)) self.log(MythLog.GENERAL, MythLog.INFO, 'Checking for duplication of ', '%s - %s' % (self.rec.title.encode('utf-8'), self.rec.subtitle.encode('utf-8'))) if self.bend.fileExists(self.vid.filename, 'Videos'): self.log(MythLog.GENERAL, MythLog.INFO, 'Recording already exists in Myth Videos') if self.thisJob: self.thisJob.setComment("Action would result in duplicate entry" ) return True else: self.log(MythLog.GENERAL, MythLog.INFO, 'No duplication found for ', '%s - %s' % (self.rec.title.encode('utf-8'), self.rec.subtitle.encode('utf-8'))) return False def get_dest(self): if self.type == 'TV': self.vid.filename = self.process_fmt(TVFMT) elif self.type == 'MOVIE': self.vid.filename = self.process_fmt(MVFMT) self.vid.markup._refdat = (self.vid.filename,) def get_meta(self): import_info = 'Listing only MetaData import complete' metadata = self.rec.exportMetadata() yrInfo = self.rec.getProgram() metadata['year'] = yrInfo.get('year') self.vid.importMetadata(metadata) if self.type == 'MOVIE': grab = VideoGrabber('Movie') results = grab.sortedSearch(self.rec.title) if len(results) > 0: for i in results: if i.year == yrInfo.get('year') and i.title == self.rec.get('title'): self.vid.importMetadata(i) match = grab.grabInetref(i.get('inetref')) length = len(match.people) for p in range(length-2): self.vid.cast.add(match.people[p].get('name')) self.vid.director = match.people[length - 1].get('name') import_info = 'Full MetaData Import complete' else: grab = VideoGrabber('TV') results = grab.sortedSearch(self.rec.title, self.rec.subtitle) if len(results) > 0: for i in results: if i.title == self.rec.get('title') and i.subtitle == self.rec.get('subtitle'): self.vid.importMetadata(i) match = grab.grabInetref(grab.grabInetref(i.get('inetref'), \ season=i.get('season'),episode=i.get('episode'))) length = len(match.people) for p in range(length-2): self.vid.cast.add(match.people[p].get('name')) self.vid.director = match.people[length - 1].get('name') import_info = 'Full MetaData Import complete' self.vid.category = self.rec.get('category') self.log(self.log.GENERAL, self.log.INFO, import_info) def get_type(self): if self.rec.seriesid != None and self.rec.programid[:2] != 'MV': self.type = 'TV' self.log(self.log.GENERAL, self.log.INFO, 'Performing TV type migration.') else: self.type = 'MOVIE' self.log(self.log.GENERAL, self.log.INFO, 'Performing Movie type migration.') def process_fmt(self, fmt): # replace fields from viddata ext = '.'+self.rec.basename.rsplit('.',1)[1] rep = ( ('%TITLE%','title','%s'), ('%SUBTITLE%','subtitle','%s'), ('%SEASON%','season','%d'), ('%SEASONPAD%','season','%02d'), ('%EPISODE%','episode','%d'), ('%EPISODEPAD%','episode','%02d'), ('%YEAR%','year','%s'), ('%DIRECTOR%','director','%s')) for tag, data, format in rep: if self.vid[data]: fmt = fmt.replace(tag,format % self.vid[data]) else: fmt = fmt.replace(tag,'') # replace fields from program data rep = ( ('%HOSTNAME%', 'hostname', '%s'), ('%STORAGEGROUP%','storagegroup','%s')) for tag, data, format in rep: data = getattr(self.rec, data) fmt = fmt.replace(tag,format % data) if len(self.vid.genre): fmt = fmt.replace('%GENRE%',self.vid.genre[0].genre) else: fmt = fmt.replace('%GENRE%','') return fmt+ext def set_job_status(self, status): self.thisJob.setStatus(status) def set_vid_hash(self): self.vid.hash = self.vid.getHash() def update_vid(self): self.vid.update()