Example #1
0
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()
Example #2
0
      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!'
Example #3
0
    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
Example #4
0
    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