diff --git a/ChangeLog b/ChangeLog index 2b20081..f3ef80b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,33 @@ +2009-05-05 Thomas Vander Stichele + + * morituri/image/table.py: + Add logging. + Add methods to clear a table of files, and to absolutize indexes + as long as the source is the same file, and to set a File on a + given index, adjusting all following indexes that match the + duration, and check if the IndexTable has all information for a TOC. + * morituri/image/toc.py: + Add logging. + Use a counter for the source. + Fix up index offset calculation. + * morituri/program/cdrdao.py: + Use a real IndexTable as the result, instead of a TocFile. + * morituri/image/cue.py: + Use a real IndexTable to store tracks. + * morituri/test/test_image_toc.py: + The toc file now has a table which has the tracks. + Fix the tests to adjust for wrong index calculations. + * morituri/test/test_image_cue.py: + * morituri/test/test_image_image.py: + * morituri/image/image.py: + The cue file now has a table which has the tracks. + * morituri/test/test_image_table.py: + Add assertions to make sure when the table can serve as a TOC. + * examples/readdisc.py: + Adjust for changes. Fix up to include AccurateRip results. + First time we can do a complete normal rip including verifying + against AccurateRip results! + 2009-05-05 Thomas Vander Stichele * examples/readdisc.py: diff --git a/examples/readdisc.py b/examples/readdisc.py index 8a7c679..40b52da 100644 --- a/examples/readdisc.py +++ b/examples/readdisc.py @@ -121,9 +121,11 @@ def main(argv): if not ptable.object: t = cdrdao.ReadIndexTableTask() function(runner, t) - ptable.persist(t.toc) + ptable.persist(t.table) itable = ptable.object + assert itable.hasTOC() + lastTrackStart = 0 for i, track in enumerate(itable.tracks): @@ -139,36 +141,16 @@ def main(argv): if t.checksum: print 'Checksums match for track %d' % (i + 1) - ittrack = table.ITTrack(i + 1) - # we know the .toc file represents a single wav rip, so all offsets - # are absolute since beginning of disc + # overlay this rip onto the IndexTable + itable.setFile(i + 1, 1, path, ittoc.getTrackLength(i + 1)) - # copy over indexes, adjusting the offset - tocTrack = itable.tracks[i] + print itable.tracks + for t in itable.tracks: + print t, t.indexes.values() - # first copy over index 0 if there is any - try: - sector, _ = tocTrack.getIndex(0) - ittrack.index(0, path=path, relative=sector - lastTrackStart) - except KeyError: - pass - lastTrackStart, _ = tocTrack.getIndex(1) - - indexes = itable.tracks[i]._indexes - numbers = indexes.keys() - numbers.sort() - if 0 in numbers: - del numbers[0] - for number in numbers: - sector, _ = tocTrack.getIndex(number) - ittrack.index(number, path=path, relative=sector - lastTrackStart) - #itable.tracks.append(ittrack) - - - - # FIXME: this is the part where our IndexTable reader should convert - # a .toc file to a IndexTable we can dump .cue from - print ittoc.cue() + handle = open('morituri.cue', 'w') + handle.write(itable.cue()) + handle.close() # verify using accuraterip print "CDDB disc id", itable.getCDDBDiscId() @@ -196,7 +178,55 @@ def main(argv): responses[0].cddbDiscId - + # FIXME: put accuraterip verification into a separate task/function + # and apply here + cueImage = image.Image('morituri.cue') + verifytask = image.ImageVerifyTask(cueImage) + cuetask = image.AccurateRipChecksumTask(cueImage) + function(runner, verifytask) + function(runner, cuetask) + + response = None # track which response matches, for all tracks + + # loop over tracks + for i, checksum in enumerate(cuetask.checksums): + status = 'rip NOT accurate' + + confidence = None + archecksum = None + + # match against each response's checksum + for j, r in enumerate(responses): + if "%08x" % checksum == r.checksums[i]: + if not response: + response = r + else: + assert r == response, \ + "checksum %s for %d matches wrong response %d, "\ + "checksum %s" % ( + checksum, i + 1, j + 1, response.checksums[i]) + status = 'rip accurate ' + archecksum = checksum + confidence = response.confidences[i] + + c = "(not found)" + ar = "(not in database)" + if responses: + if not response: + print 'ERROR: none of the responses matched.' + else: + maxConfidence = max(r.confidences[i] for r in responses) + + c = "(confidence %3d)" % maxConfidence + if confidence is not None: + if confidence < maxConfidence: + c = "(confidence %3d of %3d)" % (confidence, maxConfidence) + + ar = ", AR [%s]" % response.checksums[i] + print "Track %2d: %s %s [%08x]%s" % ( + i + 1, status, c, checksum, ar) + + main(sys.argv) diff --git a/morituri/image/cue.py b/morituri/image/cue.py index 53fea20..11307d3 100644 --- a/morituri/image/cue.py +++ b/morituri/image/cue.py @@ -58,12 +58,18 @@ _INDEX_RE = re.compile(r""" class CueFile(object, log.Loggable): + """ + I represent a .cue file as an object. + + @type table: L{table.IndexTable} + @ivar table: the index table. + """ def __init__(self, path): self._path = path self._rems = {} self._messages = [] - self.tracks = [] self.leadout = None + self.table = table.IndexTable() def parse(self): state = 'HEADER' @@ -109,7 +115,7 @@ class CueFile(object, log.Loggable): self.debug('found track %d', trackNumber) currentTrack = table.ITTrack(trackNumber) - self.tracks.append(currentTrack) + self.table.tracks.append(currentTrack) continue # look for INDEX lines @@ -145,13 +151,13 @@ class CueFile(object, log.Loggable): # returns track length in frames, or -1 if can't be determined and # complete file should be assumed # FIXME: this assumes a track can only be in one file; is this true ? - i = self.tracks.index(track) - if i == len(self.tracks) - 1: + i = self.table.tracks.index(track) + if i == len(self.table.tracks) - 1: # last track, so no length known return -1 thisIndex = track.indexes[1] # FIXME: could be more - nextIndex = self.tracks[i + 1].indexes[1] # FIXME: could be 0 + nextIndex = self.table.tracks[i + 1].indexes[1] # FIXME: could be 0 c = thisIndex.counter if c is not None and c == nextIndex.counter: diff --git a/morituri/image/image.py b/morituri/image/image.py index 50f0080..34afc0a 100644 --- a/morituri/image/image.py +++ b/morituri/image/image.py @@ -73,19 +73,19 @@ class Image(object, log.Loggable): # CD's have a standard lead-in time of 2 seconds; # checksums that use it should add it there - offset = self.cue.tracks[0].getIndex(1).relative + offset = self.cue.table.tracks[0].getIndex(1).relative tracks = [] - for i in range(len(self.cue.tracks)): - length = self.cue.getTrackLength(self.cue.tracks[i]) + for i in range(len(self.cue.table.tracks)): + length = self.cue.getTrackLength(self.cue.table.tracks[i]) if length == -1: length = verify.lengths[i + 1] t = table.ITTrack(i + 1, audio=True) tracks.append(t) # FIXME: this probably only works for non-compliant .CUE files # where pregap is put at end of previous file - t.index(1, absolute=offset, path=self.cue.tracks[i].getIndex(1).path, + t.index(1, absolute=offset, path=self.cue.table.tracks[i].getIndex(1).path, relative=0) offset += length @@ -107,15 +107,15 @@ class AccurateRipChecksumTask(task.MultiSeparateTask): cue = image.cue self.checksums = [] - self.debug('Checksumming %d tracks' % len(cue.tracks)) - for trackIndex, track in enumerate(cue.tracks): + self.debug('Checksumming %d tracks' % len(cue.table.tracks)) + for trackIndex, track in enumerate(cue.table.tracks): index = track.indexes[1] length = cue.getTrackLength(track) self.debug('track %d has length %d' % (trackIndex + 1, length)) path = image.getRealPath(index.path) checksumTask = checksum.AccurateRipChecksumTask(path, - trackNumber=trackIndex + 1, trackCount=len(cue.tracks), + trackNumber=trackIndex + 1, trackCount=len(cue.table.tracks), frameStart=index.relative * checksum.SAMPLES_PER_FRAME, frameLength=length * checksum.SAMPLES_PER_FRAME) self.addTask(checksumTask) @@ -176,7 +176,7 @@ class ImageVerifyTask(task.MultiSeparateTask): self._tasks = [] self.lengths = {} - for trackIndex, track in enumerate(cue.tracks): + for trackIndex, track in enumerate(cue.table.tracks): self.debug('verifying track %d', trackIndex + 1) index = track.indexes[1] length = cue.getTrackLength(track) diff --git a/morituri/image/table.py b/morituri/image/table.py index 7775d0e..924fb08 100644 --- a/morituri/image/table.py +++ b/morituri/image/table.py @@ -29,7 +29,9 @@ import struct import gst -from morituri.common import task, checksum, common +from morituri.common import task, checksum, common, log + +from morituri.test import common as tcommon class DeleteMeTrack: """ @@ -249,12 +251,12 @@ class Index: self.counter = counter def __repr__(self): - return '' % ( - self.number, self.absolute, self.path, self.relative) + return '' % ( + self.number, self.absolute, self.path, self.relative, self.counter) -class IndexTable: +class IndexTable(object, log.Loggable): """ - I represent the Table of Contents of a CD. + I represent a table of indexes on a CD. @ivar tracks: tracks on this CD @type tracks: list of L{ITTrack} @@ -417,6 +419,140 @@ class IndexTable: common.framesToMSF(index.relative))) lines.append("") + return "\n".join(lines) + ### methods that modify the table + def clearFiles(self): + """ + Clear all file backings. + Resets indexes paths and relative offsets. + """ + # FIXME: do a loop over track indexes better, with a pythonic + # construct that allows you to do for t, i in ... + t = self.tracks[0].number + index = self.tracks[0].getFirstIndex() + i = index.number + # the first cut is the deepest + counter = index.counter + self.debug('clearing path') + while True: + track = self.tracks[t - 1] + index = track.getIndex(i) + self.debug('Clearing path on track %d, index %d', t, i) + index.path = None + index.relative = None + try: + t, i = self.getNextTrackIndex(t, i) + except IndexError: + break + + + def setFile(self, track, index, path, length): + """ + Sets the given file as the source from the given index on. + Will loop over all indexes that fall within the given length, + to adjust the path. + + Assumes all indexes have an absolute offset and will raise if not. + """ + t = self.tracks[track - 1] + i = t.indexes[index] + start = i.absolute + assert start is not None, "index %r is missing absolute offset" % i + end = start + length + + # FIXME: check border conditions here, esp. wrt. toc's off-by-one bug + while i.absolute <= end: + self.debug('Setting path and relative on track %d, index %d', + track, index) + i.path = path + i.relative = i.absolute - start + try: + track, index = self.getNextTrackIndex(track, index) + t = self.tracks[track - 1] + i = t.indexes[index] + except IndexError: + break + + def absolutize(self): + """ + Calculate absolute offsets on indexes as much as possible. + Only possible for as long as tracks draw from the same file. + """ + t = self.tracks[0].number + index = self.tracks[0].getFirstIndex() + i = index.number + # the first cut is the deepest + counter = index.counter + + self.debug('absolutizing') + while True: + if index.counter is None: + self.debug('Track %d, index %d has no counter', t, i) + break + if index.counter != counter: + self.debug('Track %d, index %d has a different counter', t, i) + break + track = self.tracks[t - 1] + index = track.getIndex(i) + assert track.number == t + assert index.number == i + self.debug('Setting absolute offset %d on track %d, index %d', + index.relative, t, i) + index.absolute = index.relative + try: + t, i = self.getNextTrackIndex(t, i) + except IndexError: + break + + ### lookups + def getNextTrackIndex(self, track, index): + """ + Return the next track and index. + + @param track: track number, 1-based + + @raises IndexError: on last index + + @rtype: tuple of (int, int) + """ + t = self.tracks[track - 1] + indexes = t.indexes.keys() + position = indexes.index(index) + + if position + 1 < len(indexes): + return track, indexes[position + 1] + + track += 1 + if track > len(self.tracks): + raise IndexError, "No index beyond track %d, index %d" % ( + track - 1, index) + + t = self.tracks[track - 1] + indexes = t.indexes.keys() + + return track, indexes[0] + + + # various tests for types of IndexTable + def hasTOC(self): + """ + Check if the Index Table has a complete TOC. + a TOC is a list of all tracks and their Index 01, with absolute + offsets, as well as the leadout. + """ + if not self.leadout: + self.debug('no leadout, no TOC') + return False + + for t in self.tracks: + if 1 not in t.indexes.keys(): + self.debug('no index 1, no TOC') + return False + if t.indexes[1].absolute is None: + self.debug('no absolute index 1, no TOC') + return False + + return True diff --git a/morituri/image/toc.py b/morituri/image/toc.py index 754e13e..879e5d3 100644 --- a/morituri/image/toc.py +++ b/morituri/image/toc.py @@ -27,7 +27,8 @@ Reading .toc files import os import re -from morituri.common import common +from morituri.common import common, log +from morituri.image import table # header _CATALOG_RE = re.compile(r'^CATALOG "(?P\d+)"$') @@ -64,16 +65,17 @@ _INDEX_RE = re.compile(r""" \s(?P.+)$ # start offset """, re.VERBOSE) -class TocFile(object): +class TocFile(object, log.Loggable): def __init__(self, path): self._path = path self._messages = [] - self.tracks = [] + self.table = table.IndexTable() def parse(self): state = 'HEADER' currentFile = None currentTrack = None + counter = 0 trackNumber = 0 indexNumber = 0 currentOffset = 0 # running absolute offset of where each track starts @@ -100,8 +102,8 @@ class TocFile(object): # handle index 1 of previous track, if any if currentTrack: - currentTrack.index(1, currentOffset + pregapLength, - currentFile) + currentTrack.index(1, path=currentFile.path, + relative=currentOffset + pregapLength, counter=counter) trackNumber += 1 currentOffset += currentLength @@ -109,13 +111,18 @@ class TocFile(object): indexNumber = 1 trackMode = m.group('mode') - currentTrack = Track(trackNumber) - self.tracks.append(currentTrack) + # FIXME: track mode + currentTrack = table.ITTrack(trackNumber) + self.table.tracks.append(currentTrack) continue # look for SILENCE lines m = _SILENCE_RE.search(line) if m: + if currentFile is not None: + self.debug('SILENCE after FILE, increasing counter') + counter += 1 + currentFile = None length = m.group('length') currentLength += common.msfToFrames(length) @@ -125,6 +132,13 @@ class TocFile(object): filePath = m.group('name') start = m.group('start') length = m.group('length') + self.debug('FILE %s, start %r, length %r', + filePath, common.msfToFrames(start), + common.msfToFrames(length)) + if not currentFile or filePath != currentFile.path: + counter += 1 + self.debug('track %d, switched to new FILE, increased counter to %d', + trackNumber, counter) currentFile = File(filePath, start, length) #currentOffset += common.msfToFrames(start) currentLength += common.msfToFrames(length) @@ -138,8 +152,9 @@ class TocFile(object): continue length = common.msfToFrames(m.group('length')) - currentTrack.index(0, currentOffset, currentFile) - currentLength += length + currentTrack.index(0, path=currentFile.path, + relative=currentOffset - length, counter=counter) + #currentLength += length pregapLength = length # look for INDEX lines @@ -149,11 +164,13 @@ class TocFile(object): self.message(number, 'INDEX without preceding TRACK') indexNumber += 1 offset = common.msfToFrames(m.group('offset')) - currentTrack.index(indexNumber, offset, currentFile) + currentTrack.index(indexNumber, path=currentFile.path, + relative=offset, counter=counter) # handle index 1 of final track, if any if currentTrack: - currentTrack.index(1, currentOffset + pregapLength, currentFile) + currentTrack.index(1, path=currentFile.path, + relative=currentOffset + pregapLength, counter=counter) def message(self, number, message): """ @@ -167,16 +184,18 @@ class TocFile(object): # returns track length in frames, or -1 if can't be determined and # complete file should be assumed # FIXME: this assumes a track can only be in one file; is this true ? - i = self.tracks.index(track) - if i == len(self.tracks) - 1: + i = self.table.tracks.index(track) + if i == len(self.table.tracks) - 1: # last track, so no length known return -1 - thisIndex = track._indexes[1] # FIXME: could be more - nextIndex = self.tracks[i + 1]._indexes[1] # FIXME: could be 0 + thisIndex = track.indexes[1] # FIXME: could be more + nextIndex = self.table.tracks[i + 1].indexes[1] # FIXME: could be 0 - if thisIndex[1] == nextIndex[1]: # same file - return nextIndex[0] - thisIndex[0] + c = thisIndex.counter + if c is not None and c == nextIndex.counter: + # they belong to the same source, so their relative delta is length + return nextIndex.relative - thisIndex.relative # FIXME: more logic return -1 @@ -218,15 +237,15 @@ class File: """ def __init__(self, path, start, length): self.path = path - self.start = start - self.length = length + #self.start = start + #self.length = length def __repr__(self): return '' % (self.path, ) # FIXME: add type ? separate AUDIO from others -class Track: +class DeleteMeTrack: """ I represent a track in a cue file. I have index points. diff --git a/morituri/program/cdrdao.py b/morituri/program/cdrdao.py index f940040..ac33d39 100644 --- a/morituri/program/cdrdao.py +++ b/morituri/program/cdrdao.py @@ -69,7 +69,7 @@ class OutputParser(object, log.Loggable): self._track = None # which track are we analyzing? self._task = taskk - self.toc = table.IndexTable() # the index table for the TOC + self.table = table.IndexTable() # the index table for the TOC def read(self, bytes): self.log('received %d bytes in state %s', len(bytes), self._state) @@ -95,7 +95,7 @@ class OutputParser(object, log.Loggable): # we need both a position reported and an Analyzing line # to have been parsed to report progress if m and self._track is not None: - track = self.toc.tracks[self._track - 1] + track = self.table.tracks[self._track - 1] frame = (track.getIndex(1).absolute or 0) \ + int(m.group('hh')) * 60 * 75 \ + int(m.group('mm')) * 75 \ @@ -146,7 +146,7 @@ class OutputParser(object, log.Loggable): self._tracks = int(m.group('track')) track = table.ITTrack(self._tracks) track.index(1, absolute=int(m.group('start'))) - self.toc.tracks.append(track) + self.table.tracks.append(track) self.debug('Found track %d', self._tracks) m = _LEADOUT_RE.search(line) @@ -155,7 +155,7 @@ class OutputParser(object, log.Loggable): self._state = 'LEADOUT' self._frames = int(m.group('start')) self.debug('Found leadout at offset %r', self._frames) - self.toc.leadout = self._frames + self.table.leadout = self._frames self.info('%d tracks found', self._tracks) return @@ -238,21 +238,21 @@ class ReadIndexTableTask(CDRDAOTask): """ I am a task that reads all indexes of a CD. - @ivar toc: the .toc file object - @type toc: L{toc.TOC} + @ivar table: the index table + @type table: L{table.IndexTable} """ description = "Scanning indexes..." + table = None def __init__(self): CDRDAOTask.__init__(self) self.parser = OutputParser(self) - self.toc = None # result - (fd, self._toc) = tempfile.mkstemp(suffix='.morituri') + (fd, self._tocfilepath) = tempfile.mkstemp(suffix='.morituri') os.close(fd) - os.unlink(self._toc) + os.unlink(self._tocfilepath) - self.options = ['read-toc', self._toc] + self.options = ['read-toc', self._tocfilepath] def readbytes(self, bytes): self.parser.read(bytes) @@ -260,13 +260,34 @@ class ReadIndexTableTask(CDRDAOTask): def done(self): # FIXME: instead of reading only a TOC, output a complete IndexTable # by merging the TOC info. - self.toc = toc.TocFile(self._toc) - self.toc.parse() - os.unlink(self._toc) + self._tocfile = toc.TocFile(self._tocfilepath) + self._tocfile.parse() + os.unlink(self._tocfilepath) + self.table = self._tocfile.table + + # we know the .toc file represents a single wav rip, so all offsets + # are absolute since beginning of disc + self.table.absolutize() + # we unset relative since there is no real file backing this toc + for t in self.table.tracks: + for i in t.indexes.values(): + #i.absolute = i.relative + i.relative = None + + # copy the leadout from the parser's table + # FIXME: how do we get the length of the last audio track in the case + # of a data track ? + self.table.leadout = self.parser.table.leadout + + # we should have parsed it from the initial output + assert self.table.leadout is not None class ReadTOCTask(CDRDAOTask): """ I am a task that reads the TOC of a CD, without pregaps. + + @ivar table: the index table that matches the TOC. + @type table: L{table.IndexTable} """ description = "Reading TOC..." @@ -287,4 +308,6 @@ class ReadTOCTask(CDRDAOTask): def done(self): os.unlink(self._toc) - self.table = self.parser.toc + self.table = self.parser.table + + assert self.table.hasTOC(), "This Table Index should be a TOC" diff --git a/morituri/test/test_image_cue.py b/morituri/test/test_image_cue.py index bcd4ae5..3a8c06c 100644 --- a/morituri/test/test_image_cue.py +++ b/morituri/test/test_image_cue.py @@ -14,13 +14,13 @@ class KingsSingleTestCase(unittest.TestCase): self.cue = cue.CueFile(os.path.join(os.path.dirname(__file__), 'kings-single.cue')) self.cue.parse() - self.assertEquals(len(self.cue.tracks), 11) + self.assertEquals(len(self.cue.table.tracks), 11) def testGetTrackLength(self): - t = self.cue.tracks[0] + t = self.cue.table.tracks[0] self.assertEquals(self.cue.getTrackLength(t), 17811) # last track has unknown length - t = self.cue.tracks[-1] + t = self.cue.table.tracks[-1] self.assertEquals(self.cue.getTrackLength(t), -1) class KingsSeparateTestCase(unittest.TestCase): @@ -28,13 +28,13 @@ class KingsSeparateTestCase(unittest.TestCase): self.cue = cue.CueFile(os.path.join(os.path.dirname(__file__), 'kings-separate.cue')) self.cue.parse() - self.assertEquals(len(self.cue.tracks), 11) + self.assertEquals(len(self.cue.table.tracks), 11) def testGetTrackLength(self): # all tracks have unknown length - t = self.cue.tracks[0] + t = self.cue.table.tracks[0] self.assertEquals(self.cue.getTrackLength(t), -1) - t = self.cue.tracks[-1] + t = self.cue.table.tracks[-1] self.assertEquals(self.cue.getTrackLength(t), -1) class KanyeMixedTestCase(unittest.TestCase): @@ -42,10 +42,10 @@ class KanyeMixedTestCase(unittest.TestCase): self.cue = cue.CueFile(os.path.join(os.path.dirname(__file__), 'kanye.cue')) self.cue.parse() - self.assertEquals(len(self.cue.tracks), 13) + self.assertEquals(len(self.cue.table.tracks), 13) def testGetTrackLength(self): - t = self.cue.tracks[0] + t = self.cue.table.tracks[0] self.assertEquals(self.cue.getTrackLength(t), -1) diff --git a/morituri/test/test_image_image.py b/morituri/test/test_image_image.py index 01695c5..86db997 100644 --- a/morituri/test/test_image_image.py +++ b/morituri/test/test_image_image.py @@ -61,7 +61,7 @@ class TrackSeparateTestCase(unittest.TestCase): self.assertEquals(h(checksumtask.checksums[3]), '0x7271db39') def testLength(self): - tracks = self.image.cue.tracks + tracks = self.image.cue.table.tracks self.assertEquals(self.image.table.getTrackLength(1), 10) self.assertEquals(self.image.table.getTrackLength(2), 10) self.assertEquals(self.image.table.getTrackLength(3), 10) diff --git a/morituri/test/test_image_table.py b/morituri/test/test_image_table.py index 13db8ba..ea13199 100644 --- a/morituri/test/test_image_table.py +++ b/morituri/test/test_image_table.py @@ -30,8 +30,12 @@ class LadyhawkeTestCase(unittest.TestCase): for i, offset in enumerate(offsets): t[i].index(1, absolute=offset) + self.failIf(self.table.hasTOC()) + self.table.leadout = 210385 + self.failUnless(self.table.hasTOC()) + def testCDDB(self): self.assertEquals(self.table.getCDDBDiscId(), "c60af50d") diff --git a/morituri/test/test_image_toc.py b/morituri/test/test_image_toc.py index 08a5b9d..1c9971a 100644 --- a/morituri/test/test_image_toc.py +++ b/morituri/test/test_image_toc.py @@ -11,24 +11,71 @@ class CureTestCase(unittest.TestCase): self.toc = toc.TocFile(os.path.join(os.path.dirname(__file__), 'cure.toc')) self.toc.parse() - self.assertEquals(len(self.toc.tracks), 13) + self.assertEquals(len(self.toc.table.tracks), 13) def testGetTrackLength(self): - t = self.toc.tracks[0] - self.assertEquals(self.toc.getTrackLength(t), -1) + t = self.toc.table.tracks[0] + # first track has known length because the .toc is a single file + self.assertEquals(self.toc.getTrackLength(t), 28324) # last track has unknown length - t = self.toc.tracks[-1] + t = self.toc.table.tracks[-1] self.assertEquals(self.toc.getTrackLength(t), -1) def testIndexes(self): # track 2, index 0 is at 06:16:45 # FIXME: cdrdao seems to get length of FILE 1 frame too many, # and START value one frame less - t = self.toc.tracks[1] - (offset, file) = t.getIndex(0) - self.assertEquals(offset, 28245) - (offset, file) = t.getIndex(1) - self.assertEquals(offset, 28324) + t = self.toc.table.tracks[1] + self.assertEquals(t.getIndex(0).relative, 28245) + self.assertEquals(t.getIndex(1).relative, 28324) + + def _getIndex(self, t, i): + track = self.toc.table.tracks[t - 1] + return track.getIndex(i) + + def _assertAbsolute(self, t, i, value): + index = self._getIndex(t, i) + self.assertEquals(index.absolute, value) + + def _assertPath(self, t, i, value): + index = self._getIndex(t, i) + self.assertEquals(index.path, value) + + def _assertRelative(self, t, i, value): + index = self._getIndex(t, i) + self.assertEquals(index.relative, value) + + def testSetFile(self): + self._assertAbsolute(1, 1, None) + self._assertAbsolute(2, 0, None) + self._assertAbsolute(2, 1, None) + self._assertPath(1, 1, "data.wav") + + def dump(): + for t in self.toc.table.tracks: + print t + print t.indexes.values() + + self.toc.table.absolutize() + self.toc.table.clearFiles() + + self._assertAbsolute(1, 1, 0) + self._assertAbsolute(2, 0, 28166) + self._assertAbsolute(2, 1, 28324) + self._assertAbsolute(3, 1, 46110) + self._assertAbsolute(4, 1, 66767) + self._assertPath(1, 1, None) + self._assertRelative(1, 1, None) + + # adding a file to the table should fix up to including 2, 0 + self.toc.table.setFile(1, 1, 'track01.wav', 28245) + self._assertPath(1, 1, 'track01.wav') + self._assertRelative(1, 1, 0) + self._assertPath(2, 0, 'track01.wav') + self._assertAbsolute(2, 0, 28166) + + self._assertPath(2, 1, None) + self._assertRelative(2, 1, None) # Bloc Party - Silent Alarm has a Hidden Track One Audio class BlocTestCase(unittest.TestCase): @@ -36,18 +83,17 @@ class BlocTestCase(unittest.TestCase): self.toc = toc.TocFile(os.path.join(os.path.dirname(__file__), 'bloc.toc')) self.toc.parse() - self.assertEquals(len(self.toc.tracks), 13) + self.assertEquals(len(self.toc.table.tracks), 13) def testGetTrackLength(self): - t = self.toc.tracks[0] - self.assertEquals(self.toc.getTrackLength(t), -1) + t = self.toc.table.tracks[0] + # first track has known length because the .toc is a single file + self.assertEquals(self.toc.getTrackLength(t), 50089) # last track has unknown length - t = self.toc.tracks[-1] + t = self.toc.table.tracks[-1] self.assertEquals(self.toc.getTrackLength(t), -1) def testIndexes(self): - t = self.toc.tracks[0] - (offset, file) = t.getIndex(0) - self.assertEquals(offset, 0) - (offset, file) = t.getIndex(1) - self.assertEquals(offset, 15220) + t = self.toc.table.tracks[0] + self.assertEquals(t.getIndex(0).relative, 0) + self.assertEquals(t.getIndex(1).relative, 15220)