def ripDVD(device, destDir, tmpDir, extraOptions=[], ejectDisk=True, procMgr=DFT_MGR): Msg("Reading metadata from %s" % device) dvd_data = dvdDiscProperties(device, procMgr) if dvd_data is None: return False name1 = dvd_data['dvd_title'] name2 = dvd_data['dvd_alt_title'] # find main_feature title main_title = 'unknown' for title, props in dvd_data['titles'].iteritems(): if 'main_feature' in props and props['main_feature']: main_title = title if len(name1) > 0: name = name1 elif len(name2) > 0: name = name2 else: name = "Unknown DVD" tmpfile = uniquePath(os.path.join(tmpDir,"%s.mp4" % name)) Msg("Ripping title %s of %s to %s" % (main_title, name, tmpDir)) retcode, sout, serr = procMgr.call( ['HandBrakeCLI', '-i', device, '-o', tmpfile] + extraOptions) if retcode != 0: Error("HandBrake failed to rip title '%s' of disc '%s'" % (main_title, name)) Error("HandBrake output:\n %s" % serr) # autoripd will clear up the temp directory return None else: # move movie back to destination final_file = uniquePath(os.path.join(destDir, "%s.mp4" % name)) os.rename(tmpfile, final_file) if ejectDisk: # not process logged, but probably safe. subp.call(['eject', device]) return os.path.abspath(final_file)
def ripDVD(device, destDir, tmpDir, extraOptions=[], ejectDisk=True, procMgr=DFT_MGR): Msg("Reading metadata from %s" % device) dvd_data = dvdDiscProperties(device, procMgr) if dvd_data is None: return False name1 = dvd_data['dvd_title'] name2 = dvd_data['dvd_alt_title'] # find main_feature title main_title = 'unknown' for title, props in dvd_data['titles'].iteritems(): if 'main_feature' in props and props['main_feature']: main_title = title if len(name1) > 0: name = name1 elif len(name2) > 0: name = name2 else: name = "Unknown DVD" tmpfile = uniquePath(os.path.join(tmpDir, "%s.mp4" % name)) Msg("Ripping title %s of %s to %s" % (main_title, name, tmpDir)) retcode, sout, serr = procMgr.call( ['HandBrakeCLI', '-i', device, '-o', tmpfile] + extraOptions) if retcode != 0: Error("HandBrake failed to rip title '%s' of disc '%s'" % (main_title, name)) Error("HandBrake output:\n %s" % serr) # autoripd will clear up the temp directory return None else: # move movie back to destination final_file = uniquePath(os.path.join(destDir, "%s.mp4" % name)) os.rename(tmpfile, final_file) if ejectDisk: # not process logged, but probably safe. subp.call(['eject', device]) return os.path.abspath(final_file)
def processRip(self, mediaFilePath, mediaMetadata, programSettings, workingDir, previousPluginData): """Remux a rip into an .m2ts file.""" self.assignSettings(programSettings) self.origFile = mediaFilePath # should we perform the mux? media_base = os.path.basename(mediaFilePath) fname, ext = os.path.splitext(media_base) if mediaMetadata['format'] != 'Matroska': return {} # find a name/place for the new file outdir = self.m2tsDir if outdir is None: # ship .m2ts files to the same place as the other rips outdir = self.autoripd_settings.destDir if not os.path.exists(outdir): os.makedirs(outdir) outname = fname + ".m2ts" outfile = common_util.uniquePath(os.path.join(outdir, outname)) # perform the mux newfile = self.remux(mediaFilePath, outfile, mediaMetadata, workingDir) # backup and/or delete the original source if newfile is not None: bkupdir = self.srcBackupDir if bkupdir is not None and \ self.preserveSrc and \ not os.path.samefile(bkupdir, os.path.dirname(mediaFilePath)): if not os.path.isdir(bkupdir): os.path.makedirs(bkupdir) dstpath = common_util.uniquePath(os.path.join(bkupdir, media_base)) os.rename(mediaFilePath, dstpath) elif not self.preserveSrc: os.unlink(mediaFilePath) common_util.Msg("Removed %s" % mediaFilePath) else: raise Exception("remuxer encountered a problem; aborted") return {'mux_new_m2tsfile' : newfile}
def ripBluRay(device, destDir, workingDir, ejectDisc=True, procManager=DFT_MGR): """Use makemkvcon to rip a blu-ray movie from the given device. <destDir> is the path of the folder into which finished ripped movies will be moved. <tmpDir> is the path of a folder where unfinished rips will reside until they are complete. Returns path of the ripped media file, or None.""" properties = bluRayDiscProperties(device, procManager) if properties is None: # failure. brdProperties() will have reported the error. return None disc, titles = properties feature_title_id = detectBluRayMainFeature(titles) name = disc['name'] if 'name' in disc else 'Unknown Blu-Ray' Msg("Ripping title %s of %s to %s" % (feature_title_id, name, tmpdest)) retcode, sout, serr = procManager.call([ "makemkvcon", "mkv", "dev:%s" % device, str(feature_title_id), workingDir]) if retcode != 0: Error("Failed to rip from '%s' %s" % (disc['title'], device)) Error("makemkvcon output:\n%s" % serr) # unfinished mkv laying around for debugging. autoripd will delete the # working directory if the user has chosen so with a config setting return None else: # move tmp mkv to final location f_output = titles[feature_title_id]['outputFileName'] f_output = os.path.join(workingDir, f_output) final_filename = "%s.mkv" % name final_path = uniquePath(os.path.join(destDir, final_filename)) os.rename(f_output, final_path) if ejectDisc: # not process logged, but probably safe. subp.call(['eject', device]) Msg("Ripped %s successfully" % name) return os.path.abspath(final_path)
def ripBluRay(device, destDir, workingDir, ejectDisc=True, procManager=DFT_MGR): """Use makemkvcon to rip a blu-ray movie from the given device. <destDir> is the path of the folder into which finished ripped movies will be moved. <tmpDir> is the path of a folder where unfinished rips will reside until they are complete. Returns path of the ripped media file, or None.""" properties = bluRayDiscProperties(device, procManager) if properties is None: # failure. brdProperties() will have reported the error. return None disc, titles = properties feature_title_id = detectBluRayMainFeature(titles) name = disc['name'] if 'name' in disc else 'Unknown Blu-Ray' Msg("Ripping title %s of %s to %s" % (feature_title_id, name, tmpdest)) retcode, sout, serr = procManager.call([ "makemkvcon", "mkv", "dev:%s" % device, str(feature_title_id), workingDir ]) if retcode != 0: Error("Failed to rip from '%s' %s" % (disc['title'], device)) Error("makemkvcon output:\n%s" % serr) # unfinished mkv laying around for debugging. autoripd will delete the # working directory if the user has chosen so with a config setting return None else: # move tmp mkv to final location f_output = titles[feature_title_id]['outputFileName'] f_output = os.path.join(workingDir, f_output) final_filename = "%s.mkv" % name final_path = uniquePath(os.path.join(destDir, final_filename)) os.rename(f_output, final_path) if ejectDisc: # not process logged, but probably safe. subp.call(['eject', device]) Msg("Ripped %s successfully" % name) return os.path.abspath(final_path)
def processTrack(self, titlename, srcfile, track, workingdir): """Return the .meta file line associated with this track, an optional filename to extract the track to, and a function (or None) representing an action to perform on the extracted track. Return None if this track should be skipped. Track extraction should not be performed here; we extract tracks all at once when track analysis is complete (it's faster this way)-- then describe any processing on the extracted tracks with a callback.""" # we'll return this object: tkinfo = struct() tkinfo.metaline = "" # this track's data for the .meta file tkinfo.extractTo = None # to what file shall this track be extracted? tkinfo.doOnExtracted = None # fn to call when extraction complete # (will be passed the tkinfo object) # (shall return false on error, true otherwise) tkinfo.cleanupFiles = set() # extra files to delete after mux completed tkinfo.metadata = track codec = track['codec id'] id = track['unique id'] if codec not in self.allowedCodecs: common_util.Warn("Not muxing track %s; codec '%s' not recognized" %\ (id, codec)) return None if track['type'] == 'video': tracksrc = srcfile trackid = track['unique id'] # differences in naming convention if codec == 'V_MPEG2': codec = 'V_MPEG-2' elif codec == 'WVC1' or codec == 'V_MS/VFW/WVC1': if self.transcodeVC1: h264name = "%s.%s.x264.mkv" % (titlename, id) h264dest = common_util.uniquePath( os.path.join(workingdir, h264name)) tracksrc = h264dest trackid = 1 codec = 'V_MPEG4/ISO/AVC' self.doTranscodeVC1(srcfile, h264dest, track) tkinfo.cleanupFiles.add(h264dest) else: codec = 'V_MS/VFW/WVC1' # extra flags for H.264 if codec == 'V_MPEG4/ISO/AVC': extra = ', insertSEI, contSPS' else: extra = '' framerate = track['frame rate'].rsplit('fps', 1)[0].strip() template = '%s, "%s", fps=%s, track=%s, lang=%s%s\n' tkinfo.metaline = template % (codec, tracksrc, framerate, trackid, track['language'], extra) elif track['type'] in ('audio','text'): if self.subtitleLangs is not None and \ track['type'] == 'text' and \ track['language'] not in self.subtitleLangs: # skip this language return None elif self.audioLanguages is not None and \ track['type'] == 'audio' and \ track['language'] not in self.audioLanguages: # skip this language return None if codec == 'S_HDMV/PGS' and self.subtitleWorkaround: # invent a .sup file name # save it for when we perform extraction # (we will extract them all at once) pgsname = "%s.%s.%s.pgs" % (titlename, track['language'], id) tracksrc = common_util.uniquePath( os.path.join(workingdir, pgsname)) tkinfo.extractTo = tracksrc tracktag = '' # not needed; src file only has one track elif codec == 'A_DTS' and self.transcodeDTS: # we extract to one file (.dts), and mux from another (the # eventual converted .ac3). the meta file must reflect this dtsname = "%s.%s.%s.dts" % (titlename, track['language'], id) ac3name = "%s.%s.%s.ac3" % (titlename, track['language'], id) tracksrc = common_util.uniquePath( os.path.join(workingdir, ac3name)) tkinfo.extractTo = common_util.uniquePath( os.path.join(workingdir, dtsname)) tkinfo.ac3file = tracksrc # extra info for the callback tkinfo.doOnExtracted = self.doTranscodeDTS codec = 'A_AC3' tracktag = '' else: tracksrc = srcfile tracktag = ', track=%s' % id lang = (', lang=%s' % track['language']) if 'language' in track else '' tkinfo.metaline = '%s, "%s"%s%s\n' % (codec, tracksrc, tracktag, lang) return tkinfo
def remux(self, infile, outfile, info, workingdir): """Remux <infile> into an .m2ts at <outfile>, returning the complete path of <outfile> upon success, or None upon failure.""" fpath = os.path.abspath(infile) outfpath = os.path.abspath(common_util.uniquePath(outfile)) if not os.path.isfile(fpath): common_util.Error('File %s could not be found for remuxing' % fpath) return None titlename = os.path.splitext(info['file name'])[0] tkProcessData = {} # generate the .meta file # per spec at http://forum.doom9.org/archive/index.php/t-142559.html meta = "MUXOPT --no-pcr-on-video-pid --new-audio-pes --vbr --vbv-len=500\n" for track in info['tracks']: if 'unique id' not in track or 'codec id' not in track: # not a mux-able track; i.e. a menu track continue id = track['unique id'] result = self.processTrack(titlename, fpath, track, workingdir) if result is None: continue else: meta += result.metaline tkProcessData[id] = result # do the remux with tempfile.NamedTemporaryFile(mode='w', suffix='.meta', dir=workingdir) as tmpf: # write metafile metaname = os.path.abspath(tmpf.name) tmpf.write(meta) tmpf.flush() # extract/process streams ok = self.extractTracks(fpath, tkProcessData) if not ok: return None common_util.Msg("Remuxing %s to %s" % (fpath, outfpath)) common_util.Babble("Metafile:\n%s\n" % meta) # do remux mgr = self.getProcessManager() retcode, sout, serr = mgr.call([self.tsMuxeR, metaname, outfpath]) if retcode != 0: common_util.Error('Failure to remux %s to %s' % (infile, outfpath)) common_util.Msg('tsMuxeR output: %s\n%s' % (sout,serr)) return None # clean up extracted tracks / extra files: for trackdat in tkProcessData.itervalues(): if trackdat.extractTo and os.path.exists(trackdat.extractTo): os.unlink(trackdat.extractTo) for extraf in trackdat.cleanupFiles: if os.path.exists(extraf): os.unlink(extraf) # .meta tmpfile is deleted automatically common_util.Msg("%s remuxed successfully" % outfpath) return outfpath