diff --git a/ChangeLog b/ChangeLog index a436a12..51a2820 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2009-04-11 Thomas Vander Stichele + + * examples/ARcue.py: + * morituri/common/task.py: + * morituri/image/image.py (added): + Add an object for handling an Image based on a .cue file. + Create a Task for CRC'ing the whole Image. + Make the example use this new task instead. + 2009-04-11 Thomas Vander Stichele * examples/gtkcrc.py: diff --git a/examples/ARcue.py b/examples/ARcue.py index 65088bf..6e52a5a 100644 --- a/examples/ARcue.py +++ b/examples/ARcue.py @@ -23,45 +23,22 @@ import os import sys -from morituri.image import cue +from morituri.image import image from morituri.common import task, crc import gobject gobject.threads_init() def main(path): - cuefile = cue.Cue(path) - cuefile.parse() + cueImage = image.Image(path) - for trackIndex, track in enumerate(cuefile.tracks): - index = track._indexes[1] - length = cuefile.getTrackLength(track) - file = index[1] - offset = index[0] + runner = task.SyncRunner() + cuetask = image.AudioRipCRCTask(cueImage) - # find an actual potential file - crctask = None + runner.run(cuetask) - # .cue FILE statements have Windows-style path separators - path = os.path.join(*file.path.split('\\')) - noext, _ = os.path.splitext(path) - for ext in ['wav', 'flac']: - path = '%s.%s' % (noext, ext) - if os.path.exists(path): - print 'CRCing %s from CD frame %r for %r' % (path, offset, length) - crctask = crc.CRCAudioRipTask(path, - trackNumber=trackIndex + 1, trackCount=len(cuefile.tracks), - frameStart=offset * crc.FRAMES_PER_DISC_FRAME, - frameLength=length * crc.FRAMES_PER_DISC_FRAME) - - if not crctask: - print 'ERROR: path %s not found' % file.path - continue - - runner = task.SyncRunner() - runner.run(crctask) - - print "%08x" % crctask.crc + for i, crc in enumerate(cuetask.crcs): + print "Track %2d: %08x" % (i, crc) path = 'test.cue' diff --git a/morituri/common/task.py b/morituri/common/task.py index c7c8582..62d9cb9 100644 --- a/morituri/common/task.py +++ b/morituri/common/task.py @@ -113,21 +113,31 @@ class TaskRunner: """ class SyncRunner(TaskRunner): - def run(self, task): + """ + I run the task synchronously in a gobject MainLoop. + """ + def run(self, task, verbose=True, skip=False): self._task = task + self._verbose = verbose + self._skip = skip + self._loop = gobject.MainLoop() self._task.addListener(self) self._task.start() self._loop.run() def progressed(self, task, value): + if not self._verbose: + return + sys.stdout.write('%s %3d %%\r' % ( self._task.description, value * 100.0)) sys.stdout.flush() if value >= 1.0: - sys.stdout.write('%s %3d %%\n' % ( - self._task.description, 100.0)) + if self._skip: + sys.stdout.write('%s %3d %%\n' % ( + self._task.description, 100.0)) def stopped(self, task): self._loop.quit() diff --git a/morituri/image/image.py b/morituri/image/image.py new file mode 100644 index 0000000..e0ea5df --- /dev/null +++ b/morituri/image/image.py @@ -0,0 +1,118 @@ +# -*- Mode: Python; test-case-name: morituri.test.test_image_image -*- +# vi:si:et:sw=4:sts=4:ts=4 + +# Morituri - for those about to RIP + +# Copyright (C) 2009 Thomas Vander Stichele + +# This file is part of morituri. +# +# morituri is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# morituri is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with morituri. If not, see . + +""" +Wrap on-disk CD images based on the .cue file. +""" + +import os + +from morituri.common import task, crc +from morituri.image import cue + +class Image: + def __init__(self, path): + """ + @param path: .cue path + """ + self._path = path + self.cue = cue.Cue(path) + self.cue.parse() + + def getRealPath(self, path): + """ + Translate the .cue's FILE to an existing path. + """ + if os.path.exists(path): + return path + + # .cue FILE statements have Windows-style path separators + tpath = os.path.join(*path.split('\\')) + noext, _ = os.path.splitext(tpath) + for ext in ['wav', 'flac']: + cpath = '%s.%s' % (noext, ext) + if os.path.exists(cpath): + return cpath + + raise KeyError, "Cannot find file for %s" % path + + def setup(self, runner): + """ + Do initial setup, like figuring out track lengths. + """ + +class AudioRipCRCTask(task.Task): + """ + I calculate the AudioRip CRC's of all tracks. + """ + def __init__(self, image): + self._image = image + cue = image.cue + self._tasks = [] + self._track = 0 + self._tracks = len(cue.tracks) + self.description = "CRC'ing %d tracks..." % len(cue.tracks) + self.crcs = [] + + for trackIndex, track in enumerate(cue.tracks): + index = track._indexes[1] + length = cue.getTrackLength(track) + file = index[1] + offset = index[0] + + path = image.getRealPath(file.path) + crctask = crc.CRCAudioRipTask(path, + trackNumber=trackIndex + 1, trackCount=len(cue.tracks), + frameStart=offset * crc.FRAMES_PER_DISC_FRAME, + frameLength=length * crc.FRAMES_PER_DISC_FRAME) + + self._tasks.append(crctask) + + def start(self): + task.Task.start(self) + self._next() + + def _next(self): + # start next task + task = self._tasks[0] + del self._tasks[0] + self._track += 1 + self.description = "CRC'ing track %2d of %d..." % ( + self._track, self._tracks) + task.addListener(self) + task.start() + + ### listener methods + def started(self, task): + pass + + def progressed(self, task, value): + self.setProgress(value) + + def stopped(self, task): + self.crcs.append(task.crc) + if not self._tasks: + self.stop() + return + + # pick another + self.start()