* morituri/program/cdrdao.py:
read all sessions by reading session 9. * morituri/image/toc.py: Parse ZERO statements properly. Also set absolute offsets when we know them from the .toc file. Properly set audio flag on tracks. FIXME: probably doesn't work if the .toc does not have lengths. * morituri/image/table.py: Add some debug to cddb disc id calculation. Fix absolutize function, it was going one index too far. raise ValueError when overriding .absolute with a wrong value. * examples/readdisc.py: Show CDDB disc id at the start. Assert when toc and table have different disc id's (to be fixed) * morituri/test/test_image_cue.py: Update for having the table already with absolute values. * morituri/test/test_image_toc.py: Add Ladyhawke CDDB test, it has a data track.
This commit is contained in:
21
ChangeLog
21
ChangeLog
@@ -1,3 +1,24 @@
|
||||
2009-05-16 Thomas Vander Stichele <thomas at apestaart dot org>
|
||||
|
||||
* morituri/program/cdrdao.py:
|
||||
read all sessions by reading session 9.
|
||||
* morituri/image/toc.py:
|
||||
Parse ZERO statements properly.
|
||||
Also set absolute offsets when we know them from the .toc file.
|
||||
Properly set audio flag on tracks.
|
||||
FIXME: probably doesn't work if the .toc does not have lengths.
|
||||
* morituri/image/table.py:
|
||||
Add some debug to cddb disc id calculation.
|
||||
Fix absolutize function, it was going one index too far.
|
||||
raise ValueError when overriding .absolute with a wrong value.
|
||||
* examples/readdisc.py:
|
||||
Show CDDB disc id at the start.
|
||||
Assert when toc and table have different disc id's (to be fixed)
|
||||
* morituri/test/test_image_cue.py:
|
||||
Update for having the table already with absolute values.
|
||||
* morituri/test/test_image_toc.py:
|
||||
Add Ladyhawke CDDB test, it has a data track.
|
||||
|
||||
2009-05-16 Thomas Vander Stichele <thomas at apestaart dot org>
|
||||
|
||||
* examples/readdisc.py:
|
||||
|
||||
@@ -229,6 +229,7 @@ def main(argv):
|
||||
assert ittoc.hasTOC()
|
||||
|
||||
# already show us some info based on this
|
||||
print "CDDB disc id", ittoc.getCDDBDiscId()
|
||||
metadata = musicbrainz(ittoc.getMusicBrainzDiscId())
|
||||
|
||||
# now, read the complete index table, which is slower
|
||||
@@ -241,6 +242,11 @@ def main(argv):
|
||||
|
||||
assert itable.hasTOC()
|
||||
|
||||
assert itable.getCDDBDiscId() == ittoc.getCDDBDiscId(), \
|
||||
"full table's id %s differs from toc id %s" % (
|
||||
itable.getCDDBDiscId(), ittoc.getCDDBDiscId())
|
||||
assert itable.getMusicBrainzDiscId() == ittoc.getMusicBrainzDiscId()
|
||||
|
||||
lastTrackStart = 0
|
||||
|
||||
# check for hidden track one audio
|
||||
|
||||
@@ -208,19 +208,28 @@ class Table(object, log.Loggable):
|
||||
#if self.getTrackStart(1) > 0:
|
||||
# delta = 0
|
||||
|
||||
debug = [str(len(self.tracks))]
|
||||
for track in self.tracks:
|
||||
offset = self.getTrackStart(track.number) + delta
|
||||
debug.append(str(offset))
|
||||
seconds = offset / common.FRAMES_PER_SECOND
|
||||
n += self._cddbSum(seconds)
|
||||
|
||||
last = self.tracks[-1]
|
||||
# the 'real' leadout, not offset by 150 frames
|
||||
# print 'THOMAS: disc leadout', self.leadout
|
||||
leadout = self.getTrackEnd(last.number) + 1
|
||||
self.debug('leadout LBA: %d', leadout)
|
||||
startSeconds = self.getTrackStart(1) / common.FRAMES_PER_SECOND
|
||||
leadoutSeconds = leadout / common.FRAMES_PER_SECOND
|
||||
t = leadoutSeconds - startSeconds
|
||||
debug.append(str(leadoutSeconds + 2)) # 2 is the 150 frame cddb offset
|
||||
|
||||
value = (n % 0xff) << 24 | t << 8 | len(self.tracks)
|
||||
|
||||
# compare this debug line to cd-discid output
|
||||
self.debug('cddb disc id debug: %s',
|
||||
" ".join(["%08x" % value, ] + debug))
|
||||
|
||||
return "%08x" % value
|
||||
|
||||
@@ -457,20 +466,27 @@ class Table(object, log.Loggable):
|
||||
# the first cut is the deepest
|
||||
counter = index.counter
|
||||
|
||||
#for t in self.tracks: print t, t.indexes
|
||||
self.debug('absolutizing')
|
||||
while True:
|
||||
track = self.tracks[t - 1]
|
||||
index = track.getIndex(i)
|
||||
assert track.number == t
|
||||
assert index.number == i
|
||||
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)
|
||||
if index.absolute is not None:
|
||||
if index.absolute != index.relative:
|
||||
msg = 'Track %d, index %d had absolute %d,' \
|
||||
' overriding with %d' % (
|
||||
t, i, index.absolute, index.relative)
|
||||
raise ValueError(msg)
|
||||
index.absolute = index.relative
|
||||
try:
|
||||
t, i = self.getNextTrackIndex(t, i)
|
||||
|
||||
@@ -39,7 +39,7 @@ _CATALOG_RE = re.compile(r'^CATALOG "(?P<catalog>\d+)"$')
|
||||
# records
|
||||
_TRACK_RE = re.compile(r"""
|
||||
^TRACK # TRACK
|
||||
\s(?P<mode>.+)$ # mode (AUDIO, MODEx/2xxx, ...)
|
||||
\s(?P<mode>.+)$ # mode (AUDIO, MODE2_FORM_MIX, MODEx/2xxx, ...)
|
||||
""", re.VERBOSE)
|
||||
|
||||
_ISRC_RE = re.compile(r'^ISRC "(?P<isrc>\w+)"$')
|
||||
@@ -50,6 +50,13 @@ _SILENCE_RE = re.compile(r"""
|
||||
\s(?P<length>.*)$ # pre-gap length
|
||||
""", re.VERBOSE)
|
||||
|
||||
# ZERO is used as pre-gap source when switching mode
|
||||
_ZERO_RE = re.compile(r"""
|
||||
^ZERO # ZERO
|
||||
\s(?P<mode>.+) # mode (AUDIO, MODEx/2xxx, ...)
|
||||
\s(?P<length>.*)$ # zero length
|
||||
""", re.VERBOSE)
|
||||
|
||||
|
||||
_FILE_RE = re.compile(r"""
|
||||
^FILE # FILE
|
||||
@@ -58,6 +65,14 @@ _FILE_RE = re.compile(r"""
|
||||
\s(?P<length>.+)$ # stop offset
|
||||
""", re.VERBOSE)
|
||||
|
||||
_DATAFILE_RE = re.compile(r"""
|
||||
^DATAFILE # DATA FILE
|
||||
\s+"(?P<name>.*)" # 'file name' in quotes
|
||||
\s+(?P<length>\S+) # start offset
|
||||
\s*.* # possible // comment
|
||||
""", re.VERBOSE)
|
||||
|
||||
|
||||
# FIXME: start can be 0
|
||||
_START_RE = re.compile(r"""
|
||||
^START # START
|
||||
@@ -83,7 +98,8 @@ class TocFile(object, log.Loggable):
|
||||
counter = 0
|
||||
trackNumber = 0
|
||||
indexNumber = 0
|
||||
currentOffset = 0 # running absolute offset of where each track starts
|
||||
absoluteOffset = 0 # running absolute offset of where each track starts
|
||||
relativeOffset = 0 # running relative offset, relative to counter source
|
||||
currentLength = 0 # accrued during TRACK record parsing, current track
|
||||
totalLength = 0 # accrued during TRACK record parsing, total disc
|
||||
pregapLength = 0 # length of the pre-gap, current track
|
||||
@@ -128,13 +144,16 @@ class TocFile(object, log.Loggable):
|
||||
# set index 1 of previous track if there was one, using
|
||||
# pregapLength if applicable
|
||||
if currentTrack:
|
||||
# FIXME: why not set absolute offsets too ?
|
||||
currentTrack.index(1, path=currentFile.path,
|
||||
relative=currentOffset + pregapLength, counter=counter)
|
||||
absolute=absoluteOffset + pregapLength,
|
||||
relative=relativeOffset + pregapLength, counter=counter)
|
||||
self.debug('track %d, added index %r',
|
||||
currentTrack.number, currentTrack.getIndex(1))
|
||||
|
||||
trackNumber += 1
|
||||
currentOffset += currentLength
|
||||
absoluteOffset += currentLength
|
||||
relativeOffset += currentLength
|
||||
totalLength += currentLength
|
||||
currentLength = 0
|
||||
indexNumber = 1
|
||||
@@ -142,7 +161,9 @@ class TocFile(object, log.Loggable):
|
||||
pregapLength = 0
|
||||
|
||||
# FIXME: track mode
|
||||
currentTrack = table.Track(trackNumber)
|
||||
self.debug('found track %d, mode %s', trackNumber, trackMode)
|
||||
audio = trackMode == 'AUDIO'
|
||||
currentTrack = table.Track(trackNumber, audio=audio)
|
||||
self.table.tracks.append(currentTrack)
|
||||
continue
|
||||
|
||||
@@ -163,6 +184,16 @@ class TocFile(object, log.Loggable):
|
||||
length = m.group('length')
|
||||
currentLength += common.msfToFrames(length)
|
||||
|
||||
# look for ZERO lines
|
||||
m = _ZERO_RE.search(line)
|
||||
if m:
|
||||
if currentFile is not None:
|
||||
self.debug('ZERO after FILE, increasing counter')
|
||||
counter += 1
|
||||
currentFile = None
|
||||
length = m.group('length')
|
||||
currentLength += common.msfToFrames(length)
|
||||
|
||||
# look for FILE lines
|
||||
m = _FILE_RE.search(line)
|
||||
if m:
|
||||
@@ -174,12 +205,32 @@ class TocFile(object, log.Loggable):
|
||||
common.msfToFrames(length))
|
||||
if not currentFile or filePath != currentFile.path:
|
||||
counter += 1
|
||||
relativeOffset = 0
|
||||
self.debug('track %d, switched to new FILE, increased counter to %d',
|
||||
trackNumber, counter)
|
||||
currentFile = File(filePath, start, length)
|
||||
#currentOffset += common.msfToFrames(start)
|
||||
#absoluteOffset += common.msfToFrames(start)
|
||||
currentLength += common.msfToFrames(length)
|
||||
|
||||
# look for DATAFILE lines
|
||||
m = _DATAFILE_RE.search(line)
|
||||
if m:
|
||||
filePath = m.group('name')
|
||||
length = m.group('length')
|
||||
# print 'THOMAS', length
|
||||
self.debug('FILE %s, length %r',
|
||||
filePath, common.msfToFrames(length))
|
||||
if not currentFile or filePath != currentFile.path:
|
||||
counter += 1
|
||||
relativeOffset = 0
|
||||
self.debug('track %d, switched to new FILE, increased counter to %d',
|
||||
trackNumber, counter)
|
||||
# FIXME: assume that a MODE2_FORM_MIX track always starts at 0
|
||||
currentFile = File(filePath, 0, length)
|
||||
#absoluteOffset += common.msfToFrames(start)
|
||||
currentLength += common.msfToFrames(length)
|
||||
|
||||
|
||||
# look for START lines
|
||||
m = _START_RE.search(line)
|
||||
if m:
|
||||
@@ -190,7 +241,8 @@ class TocFile(object, log.Loggable):
|
||||
|
||||
length = common.msfToFrames(m.group('length'))
|
||||
currentTrack.index(0, path=currentFile.path,
|
||||
relative=currentOffset, counter=counter)
|
||||
absolute=absoluteOffset,
|
||||
relative=relativeOffset, counter=counter)
|
||||
self.debug('track %d, added index %r',
|
||||
currentTrack.number, currentTrack.getIndex(0))
|
||||
# store the pregapLength to add it when we index 1 for this
|
||||
@@ -215,7 +267,8 @@ class TocFile(object, log.Loggable):
|
||||
# handle index 1 of final track, if any
|
||||
if currentTrack:
|
||||
currentTrack.index(1, path=currentFile.path,
|
||||
relative=currentOffset + pregapLength, counter=counter)
|
||||
absolute=absoluteOffset + pregapLength,
|
||||
relative=relativeOffset + pregapLength, counter=counter)
|
||||
self.debug('track %d, added index %r',
|
||||
currentTrack.number, currentTrack.getIndex(1))
|
||||
|
||||
|
||||
@@ -202,7 +202,8 @@ class CDRDAOTask(task.Task):
|
||||
bufsize=bufsize,
|
||||
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE, close_fds=True)
|
||||
self.debug('Started cdrdao with pid %d', self._popen.pid)
|
||||
self.debug('Started cdrdao with pid %d and options %r',
|
||||
self._popen.pid, self.options)
|
||||
|
||||
self.runner.schedule(1.0, self._read, runner)
|
||||
|
||||
@@ -269,7 +270,8 @@ class ReadTableTask(CDRDAOTask):
|
||||
os.close(fd)
|
||||
os.unlink(self._tocfilepath)
|
||||
|
||||
self.options = ['read-toc', self._tocfilepath]
|
||||
self.options = ['read-toc', '--session', '9',
|
||||
self._tocfilepath, ]
|
||||
|
||||
def readbytes(self, bytes):
|
||||
self.parser.read(bytes)
|
||||
@@ -318,7 +320,10 @@ class ReadTOCTask(CDRDAOTask):
|
||||
os.close(fd)
|
||||
os.unlink(self._toc)
|
||||
|
||||
self.options = ['read-toc', '--fast-toc', self._toc]
|
||||
# Reading a non-existent session gives you output for all sessions
|
||||
# 9 should be a safe number
|
||||
self.options = ['read-toc', '--fast-toc', '--session', '9',
|
||||
self._toc, ]
|
||||
|
||||
def readbytes(self, bytes):
|
||||
self.parser.read(bytes)
|
||||
|
||||
@@ -55,20 +55,19 @@ class WriteCueFileTestCase(unittest.TestCase):
|
||||
os.close(fd)
|
||||
|
||||
it = table.Table()
|
||||
|
||||
|
||||
t = table.Track(1)
|
||||
t.index(1, path='track01.wav', relative=0, counter=1)
|
||||
t.index(1, absolute=0, path='track01.wav', relative=0, counter=1)
|
||||
it.tracks.append(t)
|
||||
|
||||
t = table.Track(2)
|
||||
t.index(0, path='track01.wav', relative=1000, counter=1)
|
||||
t.index(1, path='track02.wav', relative=0, counter=2)
|
||||
t.index(0, absolute=1000, path='track01.wav', relative=1000, counter=1)
|
||||
t.index(1, absolute=2000, path='track02.wav', relative=0, counter=2)
|
||||
it.tracks.append(t)
|
||||
it.absolutize()
|
||||
it.leadout = 2000
|
||||
it.leadout = 3000
|
||||
|
||||
self.assertEquals(it.cue(), """REM DISCID 04001A02
|
||||
self.assertEquals(it.cue(), """REM DISCID 0C002802
|
||||
REM COMMENT "Morituri"
|
||||
FILE "track01.wav" WAVE
|
||||
TRACK 01 AUDIO
|
||||
|
||||
@@ -49,9 +49,9 @@ class CureTestCase(unittest.TestCase):
|
||||
self.assertEquals(index.relative, value)
|
||||
|
||||
def testSetFile(self):
|
||||
self._assertAbsolute(1, 1, None)
|
||||
self._assertAbsolute(2, 0, None)
|
||||
self._assertAbsolute(2, 1, None)
|
||||
self._assertAbsolute(1, 1, 0)
|
||||
self._assertAbsolute(2, 0, 28245)
|
||||
self._assertAbsolute(2, 1, 28324)
|
||||
self._assertPath(1, 1, "data.wav")
|
||||
|
||||
def dump():
|
||||
@@ -118,8 +118,7 @@ class BlocTestCase(unittest.TestCase):
|
||||
|
||||
# This disc has a pre-gap, so is a good test for .CUE writing
|
||||
def testConvertCue(self):
|
||||
self.failIf(self.toc.table.hasTOC())
|
||||
self.toc.table.absolutize()
|
||||
#self.toc.table.absolutize()
|
||||
self.failUnless(self.toc.table.hasTOC())
|
||||
cue = self.toc.table.cue()
|
||||
ref = open(os.path.join(os.path.dirname(__file__),
|
||||
@@ -128,6 +127,10 @@ class BlocTestCase(unittest.TestCase):
|
||||
|
||||
def testCDDBId(self):
|
||||
self.toc.table.absolutize()
|
||||
# cd-discid output:
|
||||
# ad0be00d 13 15370 35019 51532 69190 84292 96826 112527 132448
|
||||
# 148595 168072 185539 203331 222103 3244
|
||||
|
||||
self.assertEquals(self.toc.table.getCDDBDiscId(), 'ad0be00d')
|
||||
|
||||
def testAccurateRip(self):
|
||||
@@ -156,10 +159,27 @@ class BreedersTestCase(unittest.TestCase):
|
||||
self.assertEquals(cdt['TITLE'], 'OVERGLAZED')
|
||||
|
||||
def testConvertCue(self):
|
||||
self.failIf(self.toc.table.hasTOC())
|
||||
self.toc.table.absolutize()
|
||||
self.failUnless(self.toc.table.hasTOC())
|
||||
cue = self.toc.table.cue()
|
||||
ref = open(os.path.join(os.path.dirname(__file__),
|
||||
'breeders.cue')).read()
|
||||
self.assertEquals(cue, ref)
|
||||
|
||||
# Ladyhawke has a data track
|
||||
class LadyhawkeTestCase(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.toc = toc.TocFile(os.path.join(os.path.dirname(__file__),
|
||||
'ladyhawke.toc'))
|
||||
self.toc.parse()
|
||||
self.assertEquals(len(self.toc.table.tracks), 13)
|
||||
#import code; code.interact(local=locals())
|
||||
self.failIf(self.toc.table.tracks[-1].audio)
|
||||
|
||||
def testCDDBId(self):
|
||||
self.toc.table.absolutize()
|
||||
self.assertEquals(self.toc.table.getCDDBDiscId(), 'c60af50d')
|
||||
# output from cd-discid:
|
||||
# c60af50d 13 150 15687 31841 51016 66616 81352 99559 116070 133243
|
||||
# 149997 161710 177832 207256 2807
|
||||
|
||||
|
||||
Reference in New Issue
Block a user