diff --git a/ChangeLog b/ChangeLog index f3ef80b..6ef7f81 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2009-05-05 Thomas Vander Stichele + + * morituri/image/table.py: + * morituri/test/test_image_table.py: + Implement MusicBrainz disc id. Works for audio-only discs, + have to figure out why it fails for an Enhanced CD like the + Ladyhawke one. + 2009-05-05 Thomas Vander Stichele * morituri/image/table.py: diff --git a/morituri/image/table.py b/morituri/image/table.py index 924fb08..d798751 100644 --- a/morituri/image/table.py +++ b/morituri/image/table.py @@ -349,6 +349,55 @@ class IndexTable(object, log.Loggable): return "%08x" % value + def getMusicBrainzDiscId(self): + """ + Calculate the MusicBrainz disc ID. + + @rtype: str + @returns: the 28-character base64-encoded disc ID + """ + # MusicBrainz disc id does not take into account data tracks + import sha + import base64 + + sha1 = sha.new() + + # number of first track + sha1.update("%02X" % 1) + + # number of last track + sha1.update("%02X" % self.getAudioTracks()) + + # treat leadout offset as track 0 offset + sha1.update("%08X" % (150 + self.leadout)) + + # offsets of tracks + for i in range(1, 100): + try: + offset = self.tracks[i - 1].getIndex(1).absolute + 150 + except IndexError: + #print 'track', i - 1, '0 offset' + offset = 0 + sha1.update("%08X" % offset) + + digest = sha1.digest() + assert len(digest) == 20, \ + "digest should be 20 chars, not %d" % len(digest) + + # The RFC822 spec uses +, /, and = characters, all of which are special + # HTTP/URL characters. To avoid the problems with dealing with that, I + # (Rob) used ., _, and - + + # base64 altchars specify replacements for + and / + result = base64.b64encode(digest, '._') + + # now replace = + result = "-".join(result.split("=")) + assert len(result) == 28, \ + "Result should be 28 characters, not %d" % len(result) + + return result + def getAccurateRipIds(self): """ Calculate the two AccurateRip ID's. diff --git a/morituri/test/test_image_table.py b/morituri/test/test_image_table.py index ea13199..42014db 100644 --- a/morituri/test/test_image_table.py +++ b/morituri/test/test_image_table.py @@ -39,6 +39,37 @@ class LadyhawkeTestCase(unittest.TestCase): def testCDDB(self): self.assertEquals(self.table.getCDDBDiscId(), "c60af50d") + def testMusicBrainz(self): + # FIXME: doesn't seem to be the correct id, so try the example on this + # track + #print self.table.getMusicBrainzDiscId() + pass + def testAccurateRip(self): self.assertEquals(self.table.getAccurateRipIds(), ( "0013bd5a", "00b8d489")) + +class MusicBrainzTestCase(unittest.TestCase): + # example taken from http://musicbrainz.org/doc/DiscIDCalculation + # disc is Ettella Diamant + + def setUp(self): + self.table = table.IndexTable() + + for i in range(6): + self.table.tracks.append(table.ITTrack(i + 1, audio=True)) + + offsets = [0, 15213, 32164, 46442, 63264, 80339] + t = self.table.tracks + for i, offset in enumerate(offsets): + t[i].index(1, absolute=offset) + + self.failIf(self.table.hasTOC()) + + self.table.leadout = 95312 + + self.failUnless(self.table.hasTOC()) + + def testMusicBrainz(self): + self.assertEquals(self.table.getMusicBrainzDiscId(), + '49HHV7Eb8UKF3aQiNmu1GR8vKTY-')