<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -313,6 +313,15 @@ class DiscID:
         self.__length__ = length
         self.__lead_in__ = lead_in
 
+    @classmethod
+    def from_cdda(cls, cdda):
+        tracks = list(cdda)
+
+        return cls(tracks=[t.length() for t in tracks],
+                   offsets=[t.offset() for t in tracks],
+                   length=cdda.length(),
+                   lead_in=tracks[0].offset())
+
     def add(self, track):
         self.tracks.append(track)
 </diff>
      <filename>audiotools/__freedb__.py</filename>
    </modified>
    <modified>
      <diff>@@ -1973,7 +1973,10 @@ class RawCDDA:
     def __init__(self, device_name, speed=None):
         import cdio
         self.cdda = cdio.CDDA(device_name)
-        self.total_tracks = self.cdda.total_tracks()
+        self.total_tracks = len([track_type for track_type in
+                                 map(self.cdda.track_type,
+                                     xrange(1,self.cdda.total_tracks() + 1))
+                                 if (track_type == 0)])
         if (speed is not None):
             self.cdda.set_speed(speed)
 
@@ -1991,11 +1994,18 @@ class RawCDDA:
             yield self[i]
 
     def length(self):
-        return self.cdda.length_in_seconds() * 75
+        #lead-in should always be 150
+        return self.last_sector() + 150 + 1
 
     def close(self):
         pass
 
+    def first_sector(self):
+        return self.cdda.first_sector()
+
+    def last_sector(self):
+        return self.cdda.last_sector()
+
 def at_a_time(total,per):
     for i in xrange(total / per):
         yield per</diff>
      <filename>audiotools/__init__.py</filename>
    </modified>
    <modified>
      <diff>@@ -86,6 +86,17 @@ class MBDiscID:
         self.last_track_number = last_track_number
         self.lead_out_track_offset = lead_out_track_offset
 
+    @classmethod
+    def from_cdda(cls, cdda):
+        tracks = list(cdda)
+
+        return cls(
+            tracks=[t.length() for t in tracks],
+            offsets=[t.offset() for t in tracks],
+            length=cdda.length(),
+            lead_in=tracks[0].offset(),
+            lead_out_track_offset=cdda.last_sector() + 150 + 1)
+
     def offsets(self):
         if (self.__offsets__ is None):
             offsets = [self.__lead_in__]
@@ -97,6 +108,16 @@ class MBDiscID:
         else:
             return self.__offsets__
 
+    def __repr__(self):
+        return &quot;MBDiscID(tracks=%s,offsets=%s,length=%s,lead_in=%s,first_track_number=%s,last_track_number=%s,lead_out_track_offset=%s)&quot; % \
+            (repr(self.tracks),
+             repr(self.__offsets__),
+             repr(self.__length__),
+             repr(self.__lead_in__),
+             repr(self.first_track_number),
+             repr(self.last_track_number),
+             repr(self.lead_out_track_offset))
+
     #returns a MusicBrainz DiscID value as a string
     def __str__(self):
         from hashlib import sha1</diff>
      <filename>audiotools/__musicbrainz__.py</filename>
    </modified>
    <modified>
      <diff>@@ -160,7 +160,7 @@ if (__name__ == '__main__'):
         msg.error(unicode(err) + _(u&quot;. Is that an audio cd ?&quot;))
         sys.exit(-1)
 
-    if (len(cdda) == 0xFF):
+    if (len(cdda) == 0):
         msg.error(_(u&quot;No CD in drive&quot;))
         sys.exit(1)
 </diff>
      <filename>cd2track</filename>
    </modified>
    <modified>
      <diff>@@ -99,17 +99,9 @@ if (__name__ == '__main__'):
 
     tracks = list(cdda)
 
-    musicbrainz_discid = audiotools.MBDiscID(
-        [t.length() for t in tracks],
-        [t.offset() for t in tracks],
-        cdda.length(),
-        tracks[0].offset())
-
-    freedb_discid = audiotools.DiscID(
-        [t.length() for t in tracks],
-        [t.offset() for t in tracks],
-        cdda.length(),
-        tracks[0].offset())
+    musicbrainz_discid = audiotools.MBDiscID.from_cdda(cdda)
+
+    freedb_discid = audiotools.DiscID.from_cdda(cdda)
 
     msg.info(_(u&quot;Found CD information&quot;))
 </diff>
      <filename>cd2xmcd</filename>
    </modified>
    <modified>
      <diff>@@ -40,7 +40,7 @@ if (__name__ == '__main__'):
     cdda = audiotools.RawCDDA(options.cdrom)
     tracks = list(cdda)
 
-    if (len(cdda) == 255):
+    if (len(cdda) == 0):
          msg.error(_(u&quot;No CD in drive&quot;))
          sys.exit(1)
 
@@ -59,20 +59,13 @@ if (__name__ == '__main__'):
     msg.new_row()
     msg.output_column(_(u&quot;FreeDB disc ID&quot;),True)
     msg.output_column(_(u&quot; : &quot;))
-    msg.output_column(unicode(str(audiotools.DiscID(
-                    [t.length() for t in tracks],
-                    [t.offset() for t in tracks],
-                    cdda.length(),
-                    tracks[0].offset()))))
+    msg.output_column(unicode(str(audiotools.DiscID.from_cdda(cdda))))
 
     msg.new_row()
     msg.output_column(_(u&quot;MusicBrainz disc ID&quot;),True)
     msg.output_column(_(u&quot; : &quot;))
-    msg.output_column(unicode(str(audiotools.MBDiscID(
-                    [t.length() for t in tracks],
-                    [t.offset() for t in tracks],
-                    cdda.length(),
-                    tracks[0].offset()))))
+
+    msg.output_column(unicode(str(audiotools.MBDiscID.from_cdda(cdda))))
 
     msg.blank_row()
     msg.output_rows()</diff>
      <filename>cdinfo</filename>
    </modified>
    <modified>
      <diff>@@ -38,6 +38,9 @@ static PyObject *CDDA_total_tracks(cdio_CDDAObject* self);
 static PyObject *CDDA_track_offsets(cdio_CDDAObject* self, PyObject *args);
 static PyObject *CDDA_read_sector(cdio_CDDAObject* self);
 static PyObject *CDDA_read_sectors(cdio_CDDAObject* self, PyObject *args);
+static PyObject *CDDA_first_sector(cdio_CDDAObject* self, PyObject *args);
+static PyObject *CDDA_last_sector(cdio_CDDAObject* self, PyObject *args);
+static PyObject *CDDA_track_type(cdio_CDDAObject* self, PyObject *args);
 static PyObject *CDDA_seek(cdio_CDDAObject* self, PyObject *args);
 static PyObject *CDDA_set_speed(cdio_CDDAObject* self, PyObject *args);
 static PyObject *CDDA_length_in_seconds(cdio_CDDAObject* self);
@@ -54,6 +57,12 @@ static PyMethodDef CDDA_methods[] = {
    METH_NOARGS,&quot;Returns a sector at the current position as a string&quot;},
   {&quot;read_sectors&quot;, (PyCFunction)CDDA_read_sectors,
    METH_VARARGS,&quot;Returns a number of sectors starting at the current position&quot;},
+  {&quot;first_sector&quot;, (PyCFunction)CDDA_first_sector,
+   METH_NOARGS,&quot;Returns the first sector on the disc&quot;},
+  {&quot;last_sector&quot;, (PyCFunction)CDDA_last_sector,
+   METH_NOARGS,&quot;Returns the last sector on the disc&quot;},
+  {&quot;track_type&quot;, (PyCFunction)CDDA_track_type,
+   METH_VARARGS,&quot;Returns the type of the given track&quot;},
   {&quot;seek&quot;, (PyCFunction)CDDA_seek,
    METH_VARARGS,&quot;Seeks to a specific LSN&quot;},
   {&quot;set_speed&quot;, (PyCFunction)CDDA_set_speed,
@@ -270,6 +279,48 @@ static PyObject *CDDA_read_sectors(cdio_CDDAObject* self, PyObject *args) {
   return result;
 }
 
+static PyObject *CDDA_first_sector(cdio_CDDAObject* self, PyObject *args) {
+  lsn_t sector;
+  static PyObject *result = NULL;
+
+  sector = cdio_cddap_disc_firstsector(self-&gt;cdrom_drive);
+
+  result = Py_BuildValue(&quot;i&quot;,sector);
+  if (result == NULL)
+    return NULL;
+  else
+    return result;
+}
+
+static PyObject *CDDA_last_sector(cdio_CDDAObject* self, PyObject *args) {
+  lsn_t sector;
+  static PyObject *result = NULL;
+
+  sector = cdio_cddap_disc_lastsector(self-&gt;cdrom_drive);
+
+  result = Py_BuildValue(&quot;i&quot;,sector);
+  if (result == NULL)
+    return NULL;
+  else
+    return result;
+}
+
+static PyObject *CDDA_track_type(cdio_CDDAObject* self, PyObject *args) {
+  static PyObject *result = NULL;
+  track_format_t format;
+  track_t tracknum;
+
+  if (!PyArg_ParseTuple(args, &quot;H&quot;, &amp;tracknum))
+    return NULL;
+
+  format = cdio_get_track_format(self-&gt;cdrom_drive-&gt;p_cdio,tracknum);
+  result = Py_BuildValue(&quot;i&quot;,format);
+  if (result == NULL)
+    return NULL;
+  else
+    return result;
+}
+
 static PyObject *CDDA_seek(cdio_CDDAObject* self, PyObject *args) {
   off_t location;
   lsn_t new_location;</diff>
      <filename>src/cdiomodule.c</filename>
    </modified>
    <modified>
      <diff>@@ -1203,7 +1203,7 @@ class TestAiffAudio(TestTextOutput):
 
         if (lossless):
             #test musicbrainz, no --metadata
-            time.sleep(1)
+            time.sleep(2)
             sub = subprocess.Popen([&quot;track2xmcd&quot;,&quot;-V&quot;,&quot;quiet&quot;,&quot;-D&quot;,
                                     &quot;--musicbrainz-server=musicbrainz.org&quot;,
                                     &quot;--no-freedb&quot;] + arguments,
@@ -1212,6 +1212,12 @@ class TestAiffAudio(TestTextOutput):
             self.assertEqual(sub.wait(),0)
             self.assert_(len(xml) &gt; 0)
             mbxml = audiotools.MusicBrainzReleaseXML.read_data(xml).metadata()
+
+            #NOTE: this fails intermittently simply because
+            #the MusicBrainz service is not 100% reliable
+            #an album that returns 1 match may return 0 later on
+            #even with a lengthy delay between checks
+            #this may only be a temporary problem
             self.assertEqual(mbxml,MUSICBRAINZ_METADATA)
 
             #test freedb, no --metadata</diff>
      <filename>test/test.py</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>46a8f5210886e00820dd941a7c49e907a807502d</id>
    </parent>
  </parents>
  <author>
    <name>Brian Langenberger</name>
    <email>bjl@usa.net</email>
  </author>
  <url>http://github.com/tuffy/python-audio-tools/commit/4fbcb9c063f7c6928bc32c51c474d7688bb80a6e</url>
  <id>4fbcb9c063f7c6928bc32c51c474d7688bb80a6e</id>
  <committed-date>2009-10-04T12:03:24-07:00</committed-date>
  <authored-date>2009-10-04T12:03:24-07:00</authored-date>
  <message>Add more libcdio hooks for better disc ID calculation.
This accounts for music CDs with data tracks, and so forth.</message>
  <tree>361c57bd8d8ed659072ed5cd22da1a95fe1c1651</tree>
  <committer>
    <name>Brian Langenberger</name>
    <email>bjl@usa.net</email>
  </committer>
</commit>
