* morituri/rip/cd.py:

Add asserts for comparing id's between the simple toc and
	  the full table.
	  Create the output directory before ripping the htoa.
	  Ignore data tracks for now.
	  Don't fail if we have no AccurateRip responses.
	* morituri/image/table.py:
	  Add a session ivar to Track.
	  Factor in session leadin when calculating track length
	  of last track in a session.
	  add getMusicBrainzSubmitURL()
	  add _getSessionGap() because the session gap size is different
	  for session 2 and all following.
	  Use it in merge() to get offsets right.
	  Fix getAccurateRipURL by only using the audio tracks for the
	  'length in tracks' number
	  Temporarily disable writing out data tracks to a .cue file,
	  since it's not implemented yet.
	  Add canCue to see if we can write a .cue file from the given table,
	  and debug why not if not.
	* morituri/program/cdrdao.py:
	  Rework to rip each session separately instead of using session 9.
	  This fixes session 9 read-toc missing the pregap.
	  Add a simple LineParser for handling output from disk-info.
	  Count tracks relatively for the session, because the output for
	  session 2 for track numbers picks up where session 1 left off.
	  Don't set leadout from TOC printing since for the same reason
	  session 2's leadout is absolute, not relative to start of session.
	  Add a DiscInfoTask.
	  Convert Table and Toc reading tasks to multitasks, first getting the
	  number of sessions, then reading table/toc for each session.
	* morituri/test/test_image_table.py:
	  Fix up MusicBrainz disc id for my Ladyhawke disc.
	  Add AccurateRip URL verification, compared against EAC's.
	* morituri/test/test_image_toc.py:
	  Use two separate session read-toc output files to verify
	  the case of Das Capital.
	  Verify musicbrainz URL.
This commit is contained in:
Thomas Vander Stichele
2009-05-25 14:59:45 +00:00
parent ed7d5cedad
commit 262801e554
6 changed files with 417 additions and 117 deletions

View File

@@ -1,3 +1,45 @@
2009-05-25 Thomas Vander Stichele <thomas at apestaart dot org>
* morituri/rip/cd.py:
Add asserts for comparing id's between the simple toc and
the full table.
Create the output directory before ripping the htoa.
Ignore data tracks for now.
Don't fail if we have no AccurateRip responses.
* morituri/image/table.py:
Add a session ivar to Track.
Factor in session leadin when calculating track length
of last track in a session.
add getMusicBrainzSubmitURL()
add _getSessionGap() because the session gap size is different
for session 2 and all following.
Use it in merge() to get offsets right.
Fix getAccurateRipURL by only using the audio tracks for the
'length in tracks' number
Temporarily disable writing out data tracks to a .cue file,
since it's not implemented yet.
Add canCue to see if we can write a .cue file from the given table,
and debug why not if not.
* morituri/program/cdrdao.py:
Rework to rip each session separately instead of using session 9.
This fixes session 9 read-toc missing the pregap.
Add a simple LineParser for handling output from disk-info.
Count tracks relatively for the session, because the output for
session 2 for track numbers picks up where session 1 left off.
Don't set leadout from TOC printing since for the same reason
session 2's leadout is absolute, not relative to start of session.
Add a DiscInfoTask.
Convert Table and Toc reading tasks to multitasks, first getting the
number of sessions, then reading table/toc for each session.
* morituri/test/test_image_table.py:
Fix up MusicBrainz disc id for my Ladyhawke disc.
Add AccurateRip URL verification, compared against EAC's.
* morituri/test/test_image_toc.py:
Use two separate session read-toc output files to verify
the case of Das Capital.
Verify musicbrainz URL.
2009-05-25 Thomas Vander Stichele <thomas at apestaart dot org>
* morituri/common/task.py:

View File

@@ -25,6 +25,8 @@ Wrap Table of Contents.
"""
import copy
import urllib
import urlparse
from morituri.common import task, common, log
@@ -65,11 +67,12 @@ class Track:
indexes = None
isrc = None
cdtext = None
session = None
def __repr__(self):
return '<Track %02d>' % self.number
def __init__(self, number, audio=True):
def __init__(self, number, audio=True, session=None):
self.number = number
self.audio = audio
self.indexes = {}
@@ -123,7 +126,6 @@ class Table(object, log.Loggable):
@type tracks: list of L{Track}
@ivar catalog: catalog number
@type catalog: str
@ivar version: version number of the object and its API.
"""
tracks = None # list of Track
@@ -131,7 +133,7 @@ class Table(object, log.Loggable):
catalog = None # catalog number; FIXME: is this UPC ?
cdtext = None
version = 1
classVersion = 1
def __init__(self, tracks=None):
if not tracks:
@@ -139,6 +141,10 @@ class Table(object, log.Loggable):
self.tracks = tracks
self.cdtext = {}
self.logName = "Table 0x%08X" % id(self)
# done this way because just having a class-defined instance var
# gets overridden when unpickling
self.instanceVersion = self.classVersion
def getTrackStart(self, number):
"""
@@ -159,9 +165,20 @@ class Table(object, log.Loggable):
@returns: the end of the given track number (ie index 1 of next track)
@rtype: int
"""
# default to end of disc
end = self.leadout - 1
# if not last track, calculate it from the next track
if number < len(self.tracks):
end = self.tracks[number].getIndex(1).absolute - 1
# if on a session border, subtract the session leadin
this = self.tracks[number - 1]
next = self.tracks[number]
if next.session > this.session:
gap = self._getSessionGap(next.session)
end -= gap
return end
def getTrackLength(self, number):
@@ -245,6 +262,8 @@ class Table(object, log.Loggable):
@rtype: str
@returns: the 28-character base64-encoded disc ID
"""
values = self._getMusicBrainzValues()
# MusicBrainz disc id does not take into account data tracks
import sha
import base64
@@ -252,26 +271,17 @@ class Table(object, log.Loggable):
sha1 = sha.new()
# number of first track
sha1.update("%02X" % 1)
sha1.update("%02X" % values[0])
# number of last track
sha1.update("%02X" % self.getAudioTracks())
sha1.update("%02X" % values[1])
leadout = self.leadout
# if the disc is multi-session, last track is the data track,
# and we should subtract 11250 + 150 from the last track's offset
# for the leadout
if self.hasDataTracks():
assert not self.tracks[-1].audio
leadout = self.tracks[-1].getIndex(1).absolute - 11250 - 150
# treat leadout offset as track 0 offset
sha1.update("%08X" % (150 + leadout))
sha1.update("%08X" % values[2])
# offsets of tracks
for i in range(1, 100):
try:
offset = self.tracks[i - 1].getIndex(1).absolute + 150
offset = values[2 + i]
except IndexError:
#print 'track', i - 1, '0 offset'
offset = 0
@@ -295,6 +305,71 @@ class Table(object, log.Loggable):
return result
def getMusicBrainzSubmitURL(self):
host = 'mm.musicbrainz.org'
discid = self.getMusicBrainzDiscId()
values = self._getMusicBrainzValues()
query = urllib.urlencode({
'id': discid,
'toc': ' '.join([str(v) for v in values]),
'tracks': self.getAudioTracks()
})
return urlparse.urlunparse((
'http', host, '/bare/cdlookup.html', '', query, ''))
def _getMusicBrainzValues(self):
"""
Get all MusicBrainz values needed to calculate disc id and submit URL.
This includes:
- track number of first track
- number of audio tracks
- leadout of disc
- offset of index 1 of each track
@rtype: list of int
"""
# MusicBrainz disc id does not take into account data tracks
result = []
# number of first track
result.append(1)
# number of last audio track
result.append(self.getAudioTracks())
leadout = self.leadout
# if the disc is multi-session, last track is the data track,
# and we should subtract 11250 + 150 from the last track's offset
# for the leadout
if self.hasDataTracks():
assert not self.tracks[-1].audio
leadout = self.tracks[-1].getIndex(1).absolute - 11250 - 150
# treat leadout offset as track 0 offset
result.append(150 + leadout)
# offsets of tracks
for i in range(1, 100):
try:
track = self.tracks[i - 1]
if not track.audio:
continue
offset = track.getIndex(1).absolute + 150
result.append(offset)
except IndexError:
pass
self.debug('Musicbrainz values: %r', result)
return result
def getAccurateRipIds(self):
"""
Calculate the two AccurateRip ID's.
@@ -338,7 +413,7 @@ class Table(object, log.Loggable):
return "http://www.accuraterip.com/accuraterip/" \
"%s/%s/%s/dBAR-%.3d-%s-%s-%s.bin" % (
discId1[-1], discId1[-2], discId1[-3],
len(self.tracks), discId1, discId2, self.getCDDBDiscId())
self.getAudioTracks(), discId1, discId2, self.getCDDBDiscId())
def cue(self, program='Morituri'):
"""
@@ -370,6 +445,10 @@ class Table(object, log.Loggable):
lines.append('FILE "%s" WAVE' % path)
for i, track in enumerate(self.tracks):
# FIXME: skip data tracks for now
if not track.audio:
continue
# if there is no index 0, but there is a new file, advance
# FILE line here
if not track.indexes.has_key(0):
@@ -503,18 +582,7 @@ class Table(object, log.Loggable):
@type other: L{Table}
"""
# From cdrecord multi-session info:
# For the first additional session this is 11250 sectors
# lead-out/lead-in overhead + 150 sectors for the pre-gap of the first
# track after the lead-in = 11400 sectos.
# For all further session this is 6750 sectors lead-out/lead-in
# overhead + 150 sectors for the pre-gap of the first track after the
# lead-in = 6900 sectors.
gap = 11400
if session > 2:
gap = 6900
gap = self._getSessionGap(session)
trackCount = len(self.tracks)
sourceCounter = self.tracks[-1].getLastIndex().counter
@@ -522,6 +590,7 @@ class Table(object, log.Loggable):
for track in other.tracks:
t = copy.deepcopy(track)
t.number = track.number + trackCount
t.session = session
for i in t.indexes.values():
if i.absolute is not None:
i.absolute += self.leadout + gap
@@ -534,6 +603,22 @@ class Table(object, log.Loggable):
self.tracks.append(t)
self.leadout += other.leadout + gap # FIXME
self.debug('Fixing leadout, now %d', self.leadout)
def _getSessionGap(self, session):
# From cdrecord multi-session info:
# For the first additional session this is 11250 sectors
# lead-out/lead-in overhead + 150 sectors for the pre-gap of the first
# track after the lead-in = 11400 sectos.
# For all further session this is 6750 sectors lead-out/lead-in
# overhead + 150 sectors for the pre-gap of the first track after the
# lead-in = 6900 sectors.
gap = 11400
if session > 2:
gap = 6900
return gap
### lookups
def getNextTrackIndex(self, track, index):
@@ -584,3 +669,20 @@ class Table(object, log.Loggable):
return False
return True
def canCue(self):
"""
Check if this table can be used to generate a .cue file
"""
if not self.hasTOC():
self.debug('No TOC, cannot cue')
return False
for t in self.tracks:
for i in t.indexes.values():
if i.relative is None:
self.debug('Track %02d, Index %02d does not have relative',
t.number, i.number)
return False
return True

View File

@@ -67,9 +67,41 @@ _POSITION_RE = re.compile(r"""
(?P<ss>\d\d) # SS
""", re.VERBOSE)
class LineParser(object, log.Loggable):
"""
Parse incoming bytes into lines
Calls 'parse' on owner for each parsed line.
"""
def __init__(self, owner):
self._buffer = "" # accumulate characters
self._lines = [] # accumulate lines
self._owner = owner
def read(self, bytes):
self.log('received %d bytes', len(bytes))
self._buffer += bytes
# parse buffer into lines if possible, and parse them
if "\n" in self._buffer:
self.log('buffer has newline, splitting')
lines = self._buffer.split('\n')
if lines[-1] != "\n":
# last line didn't end yet
self.log('last line still in progress')
self._buffer = lines[-1]
del lines[-1]
else:
self.log('last line finished, resetting buffer')
self._buffer = ""
for line in lines:
self.log('Parsing %s', line)
self._owner.parse(line)
self._lines.extend(lines)
class OutputParser(object, log.Loggable):
def __init__(self, taskk):
def __init__(self, taskk, session=None):
self._buffer = "" # accumulate characters
self._lines = [] # accumulate lines
self._errors = [] # accumulate error lines
@@ -77,6 +109,8 @@ class OutputParser(object, log.Loggable):
self._frames = None # number of frames
self._track = None # which track are we analyzing?
self._task = taskk
self._tracks = 0 # count of tracks, relative to session
self._session = session
self.table = table.Table() # the index table for the TOC
@@ -154,20 +188,21 @@ class OutputParser(object, log.Loggable):
m = _TRACK_RE.search(line)
if m:
self._tracks = int(m.group('track'))
track = table.Track(self._tracks)
t = int(m.group('track'))
self._tracks += 1
track = table.Track(self._tracks, session=self._session)
track.index(1, absolute=int(m.group('start')))
self.table.tracks.append(track)
self.debug('Found track %d', self._tracks)
self.debug('Found absolute track %d, session-relative %d', t,
self._tracks)
m = _LEADOUT_RE.search(line)
if m:
self.debug('Found leadout line, moving to LEADOUT state')
self._state = 'LEADOUT'
self._frames = int(m.group('start'))
self.debug('Found leadout at offset %r', self._frames)
self.table.leadout = self._frames
self.info('%d tracks found', self._tracks)
self.debug('Found absolute leadout at offset %r', self._frames)
self.info('%d tracks found for this session', self._tracks)
return
def _parse_LEADOUT(self, line):
@@ -208,11 +243,17 @@ class CDRDAOTask(task.Task):
self.runner.schedule(1.0, self._read, runner)
def _read(self, runner):
ret = self._popen.recv()
if ret:
self.log("read from stdout: %s", ret)
self.readbytesout(ret)
ret = self._popen.recv_err()
if ret:
self.log("read from stderr: %s", ret)
self.readbytes(ret)
self.readbyteserr(ret)
if self._popen.poll() is None:
# not finished yet
@@ -239,11 +280,17 @@ class CDRDAOTask(task.Task):
os.kill(self._popen.pid, signal.SIGTERM)
self.stop()
def readbytes(self, bytes):
def readbytesout(self, bytes):
"""
Called when bytes have been read from stdout.
"""
pass
def readbyteserr(self, bytes):
"""
Called when bytes have been read from stderr.
"""
raise NotImplementedError
pass
def done(self):
"""
@@ -251,39 +298,86 @@ class CDRDAOTask(task.Task):
"""
raise NotImplementedError
class ReadTableTask(CDRDAOTask):
class DiscInfoTask(CDRDAOTask):
"""
I am a task that reads all indexes of a CD.
I am a task that reads information about a disc.
@ivar sessions: the number of sessions
@type sessions: int
"""
description = "Scanning disc..."
table = None
def __init__(self, device=None):
"""
@param device: the device to rip from
@type device: str
"""
CDRDAOTask.__init__(self)
self.options = ['disk-info', ]
if device:
self.options.extend(['--device', device, ])
self.parser = LineParser(self)
def readbytesout(self, bytes):
self.parser.read(bytes)
def parse(self, line):
# called by parser
if line.startswith('Sessions'):
self.sessions = int (line[line.find(':') + 1:])
self.debug('Found %d sessions', self.sessions)
def done(self):
pass
# Read stuff for one session
class ReadSessionTask(CDRDAOTask):
"""
I am a task that reads things for one session.
@ivar table: the index table
@type table: L{table.Table}
"""
description = "Scanning indexes..."
description = "Reading session"
table = None
extraOptions = None
def __init__(self, device=None):
def __init__(self, session=None, device=None):
"""
@param device: the device to rip from
@type device: str
@param session: the session to read
@type session: int
@param device: the device to rip from
@type device: str
"""
CDRDAOTask.__init__(self)
self.parser = OutputParser(self)
(fd, self._tocfilepath) = tempfile.mkstemp(suffix='.morituri')
(fd, self._tocfilepath) = tempfile.mkstemp(
suffix='.readtablesession.morituri')
os.close(fd)
os.unlink(self._tocfilepath)
self.options = ['read-toc', ]
if device:
self.options.extend(['--device', device, ])
self.options.extend(['--session', '9', self._tocfilepath, ])
if session:
self.options.extend(['--session', str(session)])
self.description = "%s of session %d..." % (
self.description, session)
if self.extraOptions:
self.options.extend(self.extraOptions)
def readbytes(self, bytes):
self.options.extend([self._tocfilepath, ])
def readbyteserr(self, bytes):
self.parser.read(bytes)
def done(self):
# FIXME: instead of reading only a TOC, output a complete Table
# by merging the TOC info.
self._tocfile = toc.TocFile(self._tocfilepath)
self._tocfile.parse()
@@ -302,12 +396,91 @@ class ReadTableTask(CDRDAOTask):
# 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
# 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):
class ReadTableSessionTask(ReadSessionTask):
"""
I am a task that reads all indexes of a CD for a session.
@ivar table: the index table
@type table: L{table.Table}
"""
description = "Scanning indexes"
class ReadTOCSessionTask(ReadSessionTask):
"""
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.Table}
"""
description = "Reading TOC"
extraOptions = ['--fast-toc', ]
def done(self):
ReadSessionTask.done(self)
assert self.table.hasTOC(), "This Table Index should be a TOC"
# read all sessions
class ReadAllSessionsTask(task.MultiSeparateTask):
"""
I am a base class for tasks that need to read all sessions.
@ivar table: the index table
@type table: L{table.Table}
"""
table = None
_readClass = None
def __init__(self, device=None):
"""
@param device: the device to rip from
@type device: str
"""
task.MultiSeparateTask.__init__(self)
self._device = device
self.tasks = [DiscInfoTask(device=device), ]
def stopped(self, taskk):
# After first task, schedule additional ones
if taskk == self.tasks[0]:
for i in range(taskk.sessions):
self.tasks.append(self._readClass(session=i + 1,
device=self._device))
if self._task == len(self.tasks):
self.table = self.tasks[1].table
if len(self.tasks) > 2:
for i, t in enumerate(self.tasks[2:]):
self.table.merge(t.table, i + 2)
assert self.table.leadout is not None
task.MultiSeparateTask.stopped(self, taskk)
class ReadTableTask(ReadAllSessionsTask):
"""
I am a task that reads all indexes of a CD for all sessions.
@ivar table: the index table
@type table: L{table.Table}
"""
description = "Scanning indexes..."
_readClass = ReadTableSessionTask
class ReadTOCTask(ReadAllSessionsTask):
"""
I am a task that reads the TOC of a CD, without pregaps.
@@ -316,32 +489,4 @@ class ReadTOCTask(CDRDAOTask):
"""
description = "Reading TOC..."
table = None
def __init__(self, device=None):
"""
@param device: the device to rip from
@type device: str
"""
CDRDAOTask.__init__(self)
self.parser = OutputParser(self)
(fd, self._toc) = tempfile.mkstemp(suffix='.morituri')
os.close(fd)
os.unlink(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', ]
if device:
self.options.extend(['--device', device, ])
self.options.extend(['--session', '9', self._toc, ])
def readbytes(self, bytes):
self.parser.read(bytes)
def done(self):
os.unlink(self._toc)
self.table = self.parser.table
assert self.table.hasTOC(), "This Table Index should be a TOC"
_readClass = ReadTOCSessionTask

View File

@@ -150,8 +150,12 @@ def getPath(outdir, template, metadata, i):
v['A'] = filterForPath(metadata.artist)
v['d'] = filterForPath(metadata.title)
if i >= 0:
v['a'] = filterForPath(metadata.tracks[i].artist)
v['n'] = filterForPath(metadata.tracks[i].title)
try:
v['a'] = filterForPath(metadata.tracks[i - 1].artist)
v['n'] = filterForPath(metadata.tracks[i - 1].title)
except IndexError, e:
print 'ERROR: no track %d found, %r' % (i, e)
raise
else:
# htoa defaults to disc's artist
v['a'] = filterForPath(metadata.artist)
@@ -213,13 +217,12 @@ class Rip(logcommand.LogCommand):
# already show us some info based on this
print "CDDB disc id", ittoc.getCDDBDiscId()
print "MusicBrainz disc id", ittoc.getMusicBrainzDiscId()
metadata = musicbrainz(ittoc.getMusicBrainzDiscId())
url = ittoc.getAccurateRipURL()
print "AccurateRip URL", url
cache = accurip.AccuCache()
responses = cache.retrieve(url)
if not metadata:
print 'Submit this disc to MusicBrainz at:'
print ittoc.getMusicBrainzSubmitURL()
# now, read the complete index table, which is slower
path = os.path.join(os.path.expanduser('~'), '.morituri', 'cache',
@@ -237,7 +240,12 @@ class Rip(logcommand.LogCommand):
assert itable.getCDDBDiscId() == ittoc.getCDDBDiscId(), \
"full table's id %s differs from toc id %s" % (
itable.getCDDBDiscId(), ittoc.getCDDBDiscId())
assert itable.getMusicBrainzDiscId() == ittoc.getMusicBrainzDiscId()
assert itable.getMusicBrainzDiscId() == ittoc.getMusicBrainzDiscId(), \
"full table's mb id %s differs from toc id mb %s" % (
itable.getMusicBrainzDiscId(), ittoc.getMusicBrainzDiscId())
assert itable.getAccurateRipURL() == ittoc.getAccurateRipURL(), \
"full table's AR URL %s differs from toc AR URL %s" % (
itable.getAccurateRipURL(), ittoc.getAccurateRipURL())
outdir = self.options.output_directory or os.getcwd()
@@ -257,6 +265,10 @@ class Rip(logcommand.LogCommand):
# rip it
htoapath = getPath(outdir, self.options.track_template, metadata, -1) + '.wav'
dirname = os.path.dirname(htoapath)
if not os.path.exists(dirname):
os.makedirs(dirname)
htoalength = stop - start
if not os.path.exists(htoapath):
print 'Ripping track %d: %s' % (0, os.path.basename(htoapath))
@@ -265,7 +277,7 @@ class Rip(logcommand.LogCommand):
offset=int(self.options.offset),
device=self.parentCommand.options.device)
function(runner, t)
if t.checksum:
if t.checksum is not None:
print 'Checksums match for track %d' % 0
else:
print 'ERROR: checksums did not match for track %d' % 0
@@ -274,6 +286,12 @@ class Rip(logcommand.LogCommand):
for i, track in enumerate(itable.tracks):
# FIXME: rip data tracks differently
if not track.audio:
# FIXME: make it work for now
track.indexes[1].relative = 0
continue
path = getPath(outdir, self.options.track_template, metadata, i) + '.wav'
dirname = os.path.dirname(path)
if not os.path.exists(dirname):
@@ -305,6 +323,7 @@ class Rip(logcommand.LogCommand):
os.makedirs(dirname)
# write .cue file
assert itable.canCue()
cuePath = '%s.cue' % discName
handle = open(cuePath, 'w')
handle.write(itable.cue())
@@ -329,13 +348,12 @@ class Rip(logcommand.LogCommand):
handle.close()
# verify using accuraterip
print "CDDB disc id", itable.getCDDBDiscId()
print "MusicBrainz disc id", itable.getMusicBrainzDiscId()
url = itable.getAccurateRipURL()
url = ittoc.getAccurateRipURL()
print "AccurateRip URL", url
cache = accurip.AccuCache()
responses = cache.retrieve(url)
if not responses:
print 'Album not found in AccurateRip database'
@@ -364,7 +382,7 @@ class Rip(logcommand.LogCommand):
confidence = None
# match against each response's checksum
for j, r in enumerate(responses):
for j, r in enumerate(responses or []):
if "%08x" % csum == r.checksums[i]:
if not response:
response = r

View File

@@ -1,9 +1,11 @@
# -*- Mode: Python; test-case-name: morituri.test.test_image_table -*-
# vi:si:et:sw=4:sts=4:ts=4
from morituri.image import table
import unittest
from morituri.image import table
from morituri.test import common
def h(i):
return "0x%08x" % i
@@ -39,13 +41,18 @@ class LadyhawkeTestCase(unittest.TestCase):
self.assertEquals(self.table.getCDDBDiscId(), "c60af50d")
def testMusicBrainz(self):
# track
# output from mb-submit-disc:
# http://mm.musicbrainz.org/bare/cdlookup.html?toc=1+12+195856+150+15687+31841+51016+66616+81352+99559+116070+133243+149997+161710+177832&tracks=12&id=KnpGsLhvH.lPrNc1PBL21lb9Bg4-
# however, not (yet) in musicbrainz database
self.assertEquals(self.table.getMusicBrainzDiscId(),
"qrJJkrLvXz5Nkvym3oZM4KI9U4A-")
"KnpGsLhvH.lPrNc1PBL21lb9Bg4-")
def testAccurateRip(self):
self.assertEquals(self.table.getAccurateRipIds(), (
"0013bd5a", "00b8d489"))
self.assertEquals(self.table.getAccurateRipURL(),
"http://www.accuraterip.com/accuraterip/a/5/d/dBAR-012-0013bd5a-00b8d489-c60af50d.bin")
class MusicBrainzTestCase(unittest.TestCase):
# example taken from http://musicbrainz.org/doc/DiscIDCalculation

View File

@@ -179,24 +179,6 @@ class LadyhawkeTestCase(unittest.TestCase):
# c60af50d 13 150 15687 31841 51016 66616 81352 99559 116070 133243
# 149997 161710 177832 207256 2807
# Das Capital has a htoa and a data track
# the fast.toc was generated with cdrdao read-toc --fast-toc --session 9
class CapitalTestCase(unittest.TestCase):
def setUp(self):
self.toc = toc.TocFile(os.path.join(os.path.dirname(__file__),
'capital.fast.toc'))
self.toc.parse()
self.assertEquals(len(self.toc.table.tracks), 12)
#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(), 'b910140c')
# output from cd-discid:
# b910140c 12 24320 44855 64090 77885 88095 104020 118245 129255 141765 164487 181780 209250 4440
testCDDBId.skip = 'not implemented yet'
class CapitalMergeTestCase(unittest.TestCase):
def setUp(self):
self.toc1 = toc.TocFile(os.path.join(os.path.dirname(__file__),
@@ -220,5 +202,9 @@ class CapitalMergeTestCase(unittest.TestCase):
self.assertEquals(self.table.getCDDBDiscId(), 'b910140c')
# output from cd-discid:
# b910140c 12 24320 44855 64090 77885 88095 104020 118245 129255 141765 164487 181780 209250 4440
testCDDBId.skip = 'not implemented yet'
def testMusicBrainz(self):
# URL to submit: http://mm.musicbrainz.org/bare/cdlookup.html?toc=1+11+197850+24320+44855+64090+77885+88095+104020+118245+129255+141765+164487+181780&tracks=11&id=MAj3xXf6QMy7G.BIFOyHyq4MySE-
self.assertEquals(self.table.getMusicBrainzDiscId(),
"MAj3xXf6QMy7G.BIFOyHyq4MySE-")