diff --git a/ChangeLog b/ChangeLog index 72de491..2b5299b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2009-05-01 Thomas Vander Stichele + + * examples/readtrack.py (added): + Add an example that reads a track using cdparanoia. + 2009-05-01 Thomas Vander Stichele * morituri/test/cdparanoia.progress (added): diff --git a/examples/readtrack.py b/examples/readtrack.py new file mode 100644 index 0000000..40391e6 --- /dev/null +++ b/examples/readtrack.py @@ -0,0 +1,148 @@ +# -*- Mode: Python -*- +# vi:si:et:sw=4:sts=4:ts=4 + +import re +import os +import subprocess +import tempfile + +from morituri.common import task, log, common +from morituri.image import toc +from morituri.program import cdparanoia +from morituri.extern import asyncsub + +states = ['START', 'TRACK', 'LEADOUT', 'DONE'] + +_ANALYZING_RE = re.compile(r'^Analyzing track (?P\d+).*') +_TRACK_RE = re.compile(r""" + ^(?P[\d\s]{2})\s+ # Track + \w+\s+ # Mode + \d\s+ # Flags + \d\d:\d\d:\d\d # Start in HH:MM:FF + \((?P.+)\)\s+ # Start in frames + \d\d:\d\d:\d\d # Length in HH:MM:FF + \(.+\) # Length in frames +""", re.VERBOSE) +_LEADOUT_RE = re.compile(r""" + ^Leadout\s + \w+\s+ # Mode + \d\s+ # Flags + \d\d:\d\d:\d\d # Start in HH:MM:FF + \((?P.+)\) # Start in frames +""", re.VERBOSE) + +# FIXME: handle errors + +class ReadTrackTask(task.Task): + """ + I am a task that reads a track using cdparanoia. + """ + + description = "Reading Track..." + + + def __init__(self, path, start, stop, offset=0): + """ + Read the given track. + + @param path: where to store the ripped track + @type path: str + @param start: first frame to rip + @type start: int + @param stop: last frame to rip (inclusive) + @type stop: int + @param offset: read offset, in samples + @type offset: int + """ + self.path = path + self._start = start + self._stop = stop + self._offset = offset + self._parser = cdparanoia.ProgressParser() + + self._buffer = "" # accumulate characters + + def start(self, runner): + task.Task.start(self, runner) + + bufsize = 1024 + argv = ["cdparanoia", + "--sample-offset=%d" % self._offset, + "--stderr-progress", + "[%s]-[%s]" % ( + common.framesToHMSF(self._start), + common.framesToHMSF(self._stop)), self.path] + self.debug('Running %s' % (" ".join(argv), )) + self._popen = asyncsub.Popen(argv, + bufsize=bufsize, + stdin=subprocess.PIPE, stdout=subprocess.PIPE, + stderr=subprocess.PIPE, close_fds=True) + + self.runner.schedule(1.0, 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.runner.schedule(0.01, self._read, runner) + return + + self._buffer += ret + + # 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) + # FIXME: self._parser.read *will* go past self._stop, + # and only indicates read frames, not written frames. + # but we can't rely on anything else. + num = float(self._parser.read) - self._start + den = float(self._stop) - self._start + progress = num / den + if progress < 1.0: + self.setProgress(progress) + + # 0 does not give us output before we complete, 1.0 gives us output + # too late + self.runner.schedule(0.01, self._read, runner) + + def _poll(self, runner): + if self._popen.poll() is None: + self.runner.schedule(1.0, self._poll, runner) + return + + self._done() + + def _done(self): + self.setProgress(1.0) + if self._popen.returncode != 0: + if self._errors: + print "\n".join(self._errors) + else: + print 'ERROR: exit code %r' % self._popen.returncode + else: + print + print 'done' + + self.stop() + return + +def main(): + log.init() + runner = task.SyncRunner() + t = ReadTrackTask('/tmp/track.wav', 1000, 3000, offset=0) + runner.run(t) + print 'runner done' + + +main()