def read_toc(device, fast_toc=False): """ Return cdrdao-generated table of contents for 'device'. """ # cdrdao MUST be passed a non-existing filename as its last argument # to write the TOC to; it does not support writing to stdout or # overwriting an existing file, nor does linux seem to support # locking a non-existant file. Thus, this race-condition introducing # hack is carried from morituri to whipper and will be removed when # cdrdao is fixed. fd, tocfile = tempfile.mkstemp(suffix=u'.cdrdao.read-toc.whipper') os.close(fd) os.unlink(tocfile) cmd = [CDRDAO, 'read-toc'] + (['--fast-toc'] if fast_toc else []) + [ '--device', device, tocfile] # PIPE is the closest to >/dev/null we can get logger.debug("executing %r", cmd) p = Popen(cmd, stdout=PIPE, stderr=PIPE) _, stderr = p.communicate() if p.returncode != 0: msg = 'cdrdao read-toc failed: return code is non-zero: ' + \ str(p.returncode) logger.critical(msg) # Gracefully handle missing disc if "ERROR: Unit not ready, giving up." in stderr: raise EjectError(device, "no disc detected") raise IOError(msg) toc = TocFile(tocfile) toc.parse() os.unlink(tocfile) return toc
def read_toc(device, fast_toc=False): """ Return cdrdao-generated table of contents for 'device'. """ # cdrdao MUST be passed a non-existing filename as its last argument # to write the TOC to; it does not support writing to stdout or # overwriting an existing file, nor does linux seem to support # locking a non-existant file. Thus, this race-condition introducing # hack is carried from morituri to whipper and will be removed when # cdrdao is fixed. fd, tocfile = tempfile.mkstemp(suffix=u'.cdrdao.read-toc.whipper') os.close(fd) os.unlink(tocfile) cmd = [CDRDAO, 'read-toc'] + (['--fast-toc'] if fast_toc else []) + ['--device', device, tocfile] # PIPE is the closest to >/dev/null we can get logger.debug("executing %r", cmd) p = Popen(cmd, stdout=PIPE, stderr=PIPE) _, stderr = p.communicate() if p.returncode != 0: msg = 'cdrdao read-toc failed: return code is non-zero: ' + \ str(p.returncode) logger.critical(msg) # Gracefully handle missing disc if "ERROR: Unit not ready, giving up." in stderr: raise EjectError(device, "no disc detected") raise IOError(msg) toc = TocFile(tocfile) toc.parse() os.unlink(tocfile) return toc
class ReadTOCTask(task.Task): """ Task that reads the TOC of the disc using cdrdao """ description = "Reading TOC" toc = None def __init__(self, device, fast_toc=False, toc_path=None): """ Read the TOC for 'device'. :param device: block device to read TOC from :type device: str :param fast_toc: If to use fast-toc cdrdao mode :type fast_toc: bool :param toc_path: Where to save TOC if wanted. :type toc_path: str """ self.device = device self.fast_toc = fast_toc self.toc_path = toc_path self._buffer = "" # accumulate characters self._parser = ProgressParser() self.fd, self.tocfile = tempfile.mkstemp( suffix='.cdrdao.read-toc.whipper.task') def start(self, runner): task.Task.start(self, runner) os.close(self.fd) os.unlink(self.tocfile) cmd = ([CDRDAO, 'read-toc'] + (['--fast-toc'] if self.fast_toc else []) + ['--device', self.device, self.tocfile]) self._popen = asyncsub.Popen(cmd, bufsize=1024, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True) self.schedule(0.01, self._read, runner) def _read(self, runner): ret = self._popen.recv_err() if not ret: if self._popen.poll() is not None: self._done() return self.schedule(0.01, self._read, runner) return self._buffer += ret.decode() # parse buffer into lines if possible, and parse them if "\n" in self._buffer: lines = self._buffer.split('\n') if lines[-1] != "\n": # last line didn't end yet self._buffer = lines[-1] del lines[-1] else: self._buffer = "" for line in lines: self._parser.parse(line) if (self._parser.currentTrack != 0 and self._parser.tracks != 0): progress = (float('%d' % self._parser.currentTrack) / float(self._parser.tracks)) if progress < 1.0: self.setProgress(progress) # 0 does not give us output before we complete, 1.0 gives us output # too late self.schedule(0.01, self._read, runner) def _poll(self, runner): if self._popen.poll() is None: self.schedule(1.0, self._poll, runner) return self._done() def _done(self): self.setProgress(1.0) self.toc = TocFile(self.tocfile) self.toc.parse() if self.toc_path is not None: t_comp = os.path.abspath(self.toc_path).split(os.sep) t_dirn = os.sep.join(t_comp[:-1]) # If the output path doesn't exist, make it recursively os.makedirs(t_dirn, exist_ok=True) t_dst = truncate_filename(os.path.join(t_dirn, t_comp[-1] + '.toc')) shutil.copy(self.tocfile, os.path.join(t_dirn, t_dst)) os.unlink(self.tocfile) self.stop() return