def transcode_flacs(flaclist, prefs): if not prefs['output_path'] and prefs['output_type']: raise Error('must specify output path and type') xfmmod = import_xfm(prefs['output_type']) xfmmod.outpath = prefs['output_path'] if not xfmmod.ready(): raise Error('%s module failed self-tests' % prefs['output_type']) print 'output type is %s' % xfmmod.description artdir = tempfile.mkdtemp('', 'flacart.') joblist = [] class EncodeJob: pass for f in flaclist: for c, t in enumerate(f.tracks): j = EncodeJob() j.tracknum = c + 1 j.artist = f.artist j.title = t or 'Track %d' % j.tracknum j.album = f.album j.flacfile = f.filename j.coverart = f.extractThumbnail(artdir) fname = '%02d %s.%s' % (j.tracknum, t, xfmmod.extension) j.outfile = os.path.join(prefs['output_path'], flaclib.filequote(f.artist), flaclib.filequote(f.album), flaclib.filequote(fname)) j.failures = 0 if not os.path.isfile(j.outfile): joblist.append(j) num_threads = prefs.get('threads', 1) print 'Prepared %d jobs' % len(joblist) if num_threads > 3: print 'Randomizing jobs to reduce I/O bottlenecks' random.shuffle(joblist) jobq = Queue.Queue() while joblist: jobq.put(joblist.pop()) bag_o_threads = set() while len(bag_o_threads) < prefs.get('threads', 1): t = TranscodeWorker(jobq, xfmmod, len(bag_o_threads)) t.start() bag_o_threads.add(t) # we wait for all the threads to exit, rather than wait for the # queue to be empty, because the worker threads might crash. while bag_o_threads: t = bag_o_threads.pop() t.join()
for t in f.tracks: c += 1 j = EncodeJob() j.artist = f.artist j.title = t or 'Track %d' % c j.album = f.album j.tracknum = c j.flacfile = f.filename try: j.coverart = f.extractThumbnail(artdir) except flaclib.MetaflacFailed: j.coverart = None # j.listindex = f.listindex fname = '%02d - %s.%s' % (c, t, xfmmod.extension) j.outfile = os.path.join(output_path, flaclib.filequote(f.artist), flaclib.filequote(f.album), flaclib.filequote(fname)) j.failures = 0 if not os.path.isfile(j.outfile): jobs.append(j) print 'Prepared %d jobs' % len(jobs) while jobs: j = jobs.pop() print j.outfile child = os.fork() if child == 0: # encodeFile() is expected to exec an encoder process and # exit with a status code. xfmmod.encodeFile(j) assert False, 'Unpossible! encodeFile() returned!'
def OnIdle(self, e): """ Note that OnIdle() used to be installed as a wxWidgets "idle" handler, but that never worked reliably, so it is now fired from a timer and is thus misnamed. OnIdle() does different things depending on the current state: nothing in STATE_IDLE, or keep an eye on the child process doing the actual work in STATE_RIPPING or STATE_ENCODING. """ if self.state == STATE_IDLE: return elif self.state == STATE_CANCELLED: # kill child process, delete work directory, return to idle state wx.LogMessage("cancelled by user request") os.kill(self.child.pid, signal.SIGTERM) self.cleanUpWorkDir() self.setUIState(STATE_IDLE) self.timer.Stop() del self.child del self.childbuffer return # if we got here, we are waiting on either a ripper or encoder child self.updateProgress() ret = self.child.poll() if ret == None: # child still running; just wait return # child exited: state change of some kind if self.state == STATE_RIPPING: if ret == 0: # setting idle state sooner messes up lblProgress text self.setUIState(STATE_IDLE) # or idle handler will puke exceptions del self.child self.childbuffer = "" wx.LogMessage("cdparanoia finished successfully") # rip went ok, now set up the flac process and change state to # STATE_ENCODING if self.chkEject.GetValue(): cmd = ["eject"] if self.cddevice: cmd.append(self.cddevice) self.runCommand(cmd) t = self.txtTags.GetValue() t += "RIPSTATUS=" + paranoia.getFinalStatus() + "\n" self.txtTags.SetValue(t) info = cdinfo() info.parseFromTags(self.txtTags.GetValue()) if not (info.artist and info.album): wx.LogError("missing artist or album tag; can't continue") return f = flaclib.filequote(info.artist + " - " + info.album + ".flac") wx.LogMessage("output file is %s" % f) self.outfile = os.path.join(self.txtOutPath.GetValue(), f) self.goToWorkDir() cmd = [ flaccfg.BIN_FLAC, "-o", self.outfile, "--delete-input-file", "-V", "--padding", "262144", "cdda.wav", ] self.child = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) wx.LogMessage("spawned flac process %d" % self.child.pid) self.setUIState(STATE_ENCODING) else: self.setUIState(STATE_IDLE) del self.child self.childbuffer = "" wx.LogMessage("cdparanoia exited with status %s" % ret) wx.LogMessage("Not cleaning up work directory %s" % self.workdir) elif self.state == STATE_ENCODING: self.setUIState(STATE_IDLE) del self.child self.childbuffer = "" if ret == 0: wx.LogMessage("encoder finished successfully") else: wx.LogError("encoder exited with status %s" % ret) # use metaflac to load tags from text box. We could have done # this with many -T arguments to flac, but then we would not # be able to edit them while the encoding is going on. wx.LogMessage("inserting tags") self.goToWorkDir() open("tags", "w").write(self.txtTags.GetValue().encode("utf8")) self.runCommand([flaccfg.BIN_METAFLAC, "--import-tags-from=tags", self.outfile]) wx.LogMessage("inserting cuesheet") self.runCommand([flaccfg.BIN_METAFLAC, "--import-cuesheet-from=cue", self.outfile]) if self.coverart: wx.LogMessage("inserting cover art image") self.runCommand([flaccfg.BIN_METAFLAC, "--import-picture-from", self.coverart, self.outfile]) del self.outfile if not TESTMODE: self.cleanUpWorkDir() return
def OnIdle(self, e): """ Note that OnIdle() used to be installed as a wxWidgets "idle" handler, but that never worked reliably, so it is now fired from a timer and is thus misnamed. OnIdle() does different things depending on the current state: nothing in STATE_IDLE, or keep an eye on the child process doing the actual work in STATE_RIPPING or STATE_ENCODING. """ if self.state == STATE_IDLE: return elif self.state == STATE_CANCELLED: # kill child process, delete work directory, return to idle state wx.LogMessage("cancelled by user request") os.kill(self.child.pid, signal.SIGTERM) self.cleanUpWorkDir() self.setUIState(STATE_IDLE) self.timer.Stop() del self.child del self.childbuffer return # if we got here, we are waiting on either a ripper or encoder child self.updateProgress() ret = self.child.poll() if ret == None: # child still running; just wait return # child exited: state change of some kind if self.state == STATE_RIPPING: if ret == 0: # setting idle state sooner messes up lblProgress text self.setUIState( STATE_IDLE) # or idle handler will puke exceptions del self.child self.childbuffer = "" wx.LogMessage("cdparanoia finished successfully") # rip went ok, now set up the flac process and change state to # STATE_ENCODING if self.chkEject.GetValue(): cmd = ['eject'] if self.cddevice: cmd.append(self.cddevice) self.runCommand(cmd) t = self.txtTags.GetValue() t += "RIPSTATUS=" + paranoia.getFinalStatus() + "\n" self.txtTags.SetValue(t) info = cdinfo() info.parseFromTags(self.txtTags.GetValue()) if not (info.artist and info.album): wx.LogError("missing artist or album tag; can't continue") return f = flaclib.filequote(info.artist + " - " + info.album + ".flac") wx.LogMessage("output file is %s" % f) self.outfile = os.path.join(self.txtOutPath.GetValue(), f) self.goToWorkDir() cmd = [ flaccfg.BIN_FLAC, '-o', self.outfile, '--delete-input-file', '-V', '--padding', '262144', 'cdda.wav' ] self.child = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) wx.LogMessage("spawned flac process %d" % self.child.pid) self.setUIState(STATE_ENCODING) else: self.setUIState(STATE_IDLE) del self.child self.childbuffer = "" wx.LogMessage("cdparanoia exited with status %s" % ret) wx.LogMessage("Not cleaning up work directory %s" \ % self.workdir) elif self.state == STATE_ENCODING: self.setUIState(STATE_IDLE) del self.child self.childbuffer = "" if ret == 0: wx.LogMessage("encoder finished successfully") else: wx.LogError("encoder exited with status %s" % ret) # use metaflac to load tags from text box. We could have done # this with many -T arguments to flac, but then we would not # be able to edit them while the encoding is going on. wx.LogMessage("inserting tags") self.goToWorkDir() open("tags", "w").write(self.txtTags.GetValue().encode('utf8')) self.runCommand([ flaccfg.BIN_METAFLAC, '--import-tags-from=tags', self.outfile ]) wx.LogMessage("inserting cuesheet") self.runCommand([ flaccfg.BIN_METAFLAC, '--import-cuesheet-from=cue', self.outfile ]) if self.coverart: wx.LogMessage("inserting cover art image") self.runCommand([ flaccfg.BIN_METAFLAC, '--import-picture-from', self.coverart, self.outfile ]) del self.outfile if not TESTMODE: self.cleanUpWorkDir() return