From 02edffa62939f45ca4edb4f8853f3ed0440363c9 Mon Sep 17 00:00:00 2001 From: Thomas Vander Stichele Date: Wed, 15 Apr 2009 12:32:10 +0000 Subject: [PATCH] * morituri/test/test_image_toc.py (added): * morituri/image/toc.py (added): Add an abstraction for a TOC. Take data tracks into account correctly for CDDB and AccurateRip disc ID's. --- ChangeLog | 8 ++ morituri/image/toc.py | 133 ++++++++++++++++++++++++++++++++ morituri/test/test_image_toc.py | 42 ++++++++++ 3 files changed, 183 insertions(+) create mode 100644 morituri/image/toc.py create mode 100644 morituri/test/test_image_toc.py diff --git a/ChangeLog b/ChangeLog index 762a57b..24b04ee 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2009-04-15 Thomas Vander Stichele + + * morituri/test/test_image_toc.py (added): + * morituri/image/toc.py (added): + Add an abstraction for a TOC. + Take data tracks into account correctly for CDDB and AccurateRip + disc ID's. + 2009-04-15 Thomas Vander Stichele * morituri/image/image.py: diff --git a/morituri/image/toc.py b/morituri/image/toc.py new file mode 100644 index 0000000..14bb78c --- /dev/null +++ b/morituri/image/toc.py @@ -0,0 +1,133 @@ +# -*- Mode: Python; test-case-name: morituri.test.test_image_toc -*- +# vi:si:et:sw=4:sts=4:ts=4 + +# Morituri - for those about to RIP + +# Copyright (C) 2009 Thomas Vander Stichele + +# This file is part of morituri. +# +# morituri is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# morituri is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with morituri. If not, see . + +""" +Wrap Table of Contents. +""" + +import os +import struct + +import gst + +from morituri.common import task, crc +from morituri.image import cue + +class Track: + number = None # track number, 1-based + start = None # start of track in CD frames, 0-based + end = None # end of track in CD frames, 0-based + audio = True + + def __init__(self, number, start, end, audio=True): + self.number = number + self.start = start + self.end = end + self.audio = audio + +class TOC: + + tracks = None # list of Track + + def __init__(self, tracks=None): + if not tracks: + tracks = [] + + self.tracks = tracks + + def getTrackStart(self, number): + return self.tracks[number - 1].start + + def getTrackEnd(self, number): + return self.tracks[number - 1].end + + def getTrackLength(self, number): + track = self.tracks[number - 1] + return track.end - track.start + 1 + + def getAudioTracks(self): + return len([t for t in self.tracks if t.audio]) + + def _cddbSum(self, i): + ret = 0 + while i > 0: + ret += (i % 10) + i /= 10 + + return ret + + def getCDDBDiscId(self): + # cddb disc id takes into account data tracks + # last byte is the number of tracks on the CD + n = 0 + + for track in self.tracks: + # CD's have a standard lead-in time of 2 seconds + # which gets added for CDDB disc id's + offset = self.getTrackStart(track.number) + \ + 2 * crc.DISC_FRAMES_PER_SECOND + seconds = offset / crc.DISC_FRAMES_PER_SECOND + n += self._cddbSum(seconds) + + last = self.tracks[-1] + leadout = self.getTrackEnd(last.number) + frameLength = leadout - self.getTrackStart(1) + t = frameLength / crc.DISC_FRAMES_PER_SECOND + + value = (n % 0xff) << 24 | t << 8 | len(self.tracks) + + return "%08x" % value + + def getAccurateRipIds(self): + """ + @rtype: two-tuple of (str, str) + """ + discId1 = 0 + discId2 = 0 + + for track in self.tracks: + if not track.audio: + continue + offset = self.getTrackStart(track.number) + discId1 += offset + discId2 += (offset or 1) * track.number + + # also add end values, where leadout offset is one past the end + # of the last track + last = self.tracks[-1] + offset = self.getTrackEnd(last.number) + 1 + discId1 += offset + discId2 += offset * (self.getAudioTracks() + 1) + + discId1 &= 0xffffffff + discId2 &= 0xffffffff + + return ("%08x" % discId1, "%08x" % discId2) + + def getAccurateRipURL(self): + # does not count data tracks + discId1, discId2 = self.getAccurateRipIds() + + 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()) diff --git a/morituri/test/test_image_toc.py b/morituri/test/test_image_toc.py new file mode 100644 index 0000000..ccc0737 --- /dev/null +++ b/morituri/test/test_image_toc.py @@ -0,0 +1,42 @@ +# -*- Mode: Python; test-case-name: morituri.test.test_image_toc -*- +# vi:si:et:sw=4:sts=4:ts=4 + +import os +import unittest + +from morituri.image import toc + +def h(i): + return "0x%08x" % i + +class LadyhawkeTestCase(unittest.TestCase): + # Ladyhawke - Ladyhawke - 0602517818866 + # contains 12 audio tracks and one data track + # CDDB has been verified against freedb: + # http://www.freedb.org/freedb/misc/c60af50d + # http://www.freedb.org/freedb/jazz/c60af50d + # AccurateRip URL has been verified against EAC's, using wireshark + + def setUp(self): + self.toc = toc.TOC([ + toc.Track( 1, 0, 15536), + toc.Track( 2, 15537, 31690), + toc.Track( 3, 31691, 50865), + toc.Track( 4, 50866, 66465), + toc.Track( 5, 66466, 81201), + toc.Track( 6, 81202, 99408), + toc.Track( 7, 99409, 115919), + toc.Track( 8, 115920, 133092), + toc.Track( 9, 133093, 149846), + toc.Track(10, 149847, 161559), + toc.Track(11, 161560, 177681), + toc.Track(12, 177682, 195705), + toc.Track(13, 207106, 210384, audio=False), + ]) + + def testCDDB(self): + self.assertEquals(self.toc.getCDDBDiscId(), "c60af50d") + + def testAccurateRip(self): + self.assertEquals(self.toc.getAccurateRipIds(), ( + "0013bd5a", "00b8d489"))