* 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!
This commit is contained in:
30
ChangeLog
30
ChangeLog
@@ -1,3 +1,33 @@
|
||||
2009-05-05 Thomas Vander Stichele <thomas at apestaart dot org>
|
||||
|
||||
* 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 <thomas at apestaart dot org>
|
||||
|
||||
* 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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 '<Index %02d, absolute %r, path %r, relative %r>' % (
|
||||
self.number, self.absolute, self.path, self.relative)
|
||||
return '<Index %02d, absolute %r, path %r, relative %r, counter %r>' % (
|
||||
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
|
||||
|
||||
@@ -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<catalog>\d+)"$')
|
||||
@@ -64,16 +65,17 @@ _INDEX_RE = re.compile(r"""
|
||||
\s(?P<offset>.+)$ # 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 '<File "%s">' % (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.
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user