<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -357,6 +357,10 @@
 - [X] track2xmcd
 - [X] tracksplit
 - [X] tracktag
+* DONE Check for FLAC metadata chunk overflow
+  Although APEv2 and ID3 tags support very large objects (hundreds of MB),
+  FLAC metadata chunks have a maxmimum of about 16MB per chunk,
+  which may be hit accidentally.
 * TODO Ensure non-image cuesheets are handled properly
   There are 4 different types of cuesheets,
   1 single file type and 3 different multiple file variants.
@@ -484,3 +488,4 @@
 * TODO Fix or replace Python's built-in aifc module
   The current implementation suffers from bugs.
 ** TODO Document AIFF better
+</diff>
      <filename>TODO</filename>
    </modified>
    <modified>
      <diff>@@ -229,25 +229,19 @@ class FlacMetaData(MetaData):
         #STREAMINFO must always be first and is always a fixed size
         built_blocks.append(blocks.next().build_block())
 
-        #VORBISCOMMENT must always be next
-        vorbis = blocks.next()
-        try:
-            built_blocks.append(vorbis.build_block())
-        except FlacMetaDataBlockTooLarge:
-            #if VORBISCOMMENT is too large, substitute a blank one
-            #(this only happens when one pushes over 16MB(!) of text
-            # into a comment, which simply isn't going to happen
-            # accidentcally)
-            built_blocks.append(FlacVorbisComment(
-                    vorbis_data={},
-                    vendor_string=vorbis.vendor_string).build_block())
-
         #then come the rest of the blocks in any order
         for block in blocks:
             try:
                 built_blocks.append(block.build_block())
             except FlacMetaDataBlockTooLarge:
-                pass
+                if (isinstance(block,VorbisComment)):
+                    #if VORBISCOMMENT is too large, substitute a blank one
+                    #(this only happens when one pushes over 16MB(!) of text
+                    # into a comment, which simply isn't going to happen
+                    # accidentcally)
+                    built_blocks.append(FlacVorbisComment(
+                            vorbis_data={},
+                            vendor_string=block.vendor_string).build_block())
 
         #finally, append a fresh PADDING block
         built_blocks.append(
@@ -583,7 +577,15 @@ class FlacAudio(AudioFile):
     @classmethod
     def is_type(cls, file):
         if (file.read(4) == 'fLaC'):
-            return True
+            block_ids = list(cls.__block_ids__(file))
+            if ((len(block_ids) == 0) or (0 not in block_ids)):
+                messenger = Messenger(&quot;audiotools&quot;,None)
+                messenger.error(_(u&quot;STREAMINFO block not found&quot;))
+            elif (block_ids[0] != 0):
+                messenger = Messenger(&quot;audiotools&quot;,None)
+                messenger.error(_(u&quot;STREAMINFO not first metadata block.  Please fix with tracklint(1)&quot;))
+            else:
+                return True
         else:
             #I've seen FLAC files tagged with ID3v2 comments.
             #Though the official flac binaries grudgingly accept these,
@@ -594,7 +596,7 @@ class FlacAudio(AudioFile):
             ID3v2Comment.skip(file)
             if (file.read(4) == 'fLaC'):
                 messenger = Messenger(&quot;audiotools&quot;,None)
-                messenger.error(_(u&quot;ID3v2 tag found at start of FLAC file.  Please remove.&quot;))
+                messenger.error(_(u&quot;ID3v2 tag found at start of FLAC file.  Please remove with tracklint(1)&quot;))
             return False
 
     def lossless(self):
@@ -740,6 +742,17 @@ class FlacAudio(AudioFile):
         p = FlacAudio.METADATA_BLOCK_HEADER.parse(flacfile.read(4))
         return (p.last_block, p.block_type, p.block_length)
 
+    @classmethod
+    def __block_ids__(cls, flacfile):
+        p = Con.Container(last_block=False,
+                          block_type=None,
+                          block_length=0)
+
+        while (not p.last_block):
+            p = FlacAudio.METADATA_BLOCK_HEADER.parse_stream(flacfile)
+            yield p.block_type
+            flacfile.seek(p.block_length,1)
+
     def set_cuesheet(self,cuesheet):
         if (cuesheet is None):
             return</diff>
      <filename>audiotools/__flac__.py</filename>
    </modified>
    <modified>
      <diff>@@ -97,7 +97,7 @@ MUSICBRAINZ_PORT = config.getint_default(&quot;MusicBrainz&quot;,&quot;port&quot;,80)
 THUMBNAIL_FORMAT = config.get_default(&quot;Thumbnail&quot;,&quot;format&quot;,&quot;jpeg&quot;)
 THUMBNAIL_SIZE = config.getint_default(&quot;Thumbnail&quot;,&quot;size&quot;,150)
 
-VERSION = &quot;2.13alpha3&quot;
+VERSION = &quot;2.13beta1&quot;
 
 FILENAME_FORMAT = config.get_default(
     &quot;Filenames&quot;,&quot;format&quot;,</diff>
      <filename>audiotools/__init__.py</filename>
    </modified>
    <modified>
      <diff>@@ -65,6 +65,8 @@ For example, a track name value of \[lq]\fCSome Title \fR\[rq] becomes
 \[bu] Empty fields are removed.
 .PP
 \[bu] ID3v2 and ID3v1 tags are stripped from FLAC files.
+.PP
+\[bu] FLAC files with misordered STREAMINFO blocks are reordered.
 .SH SEE ALSO
 .BR cd2track (1),
 .BR cd2xmcd (1),</diff>
      <filename>docs/tracklint.1</filename>
    </modified>
    <modified>
      <diff>@@ -91,7 +91,7 @@ extensions = [cdiomodule,
 #     pass #pkg-config not available
 
 setup (name = 'Python Audio Tools',
-       version = '2.13alpha3',
+       version = '2.13beta1',
        description = 'A collection of audio handling utilities',
        author = 'Brian Langenberger',
        author_email = 'tuffy@users.sourceforge.net',</diff>
      <filename>setup.py</filename>
    </modified>
    <modified>
      <diff>@@ -36,7 +36,7 @@ import time
 gettext.install(&quot;audiotools&quot;,unicode=True)
 
 (METADATA,PCM,EXECUTABLE,CUESHEET,IMAGE,CUSTOM) = range(6)
-CASES = set([METADATA,PCM,EXECUTABLE,CUESHEET,IMAGE])
+CASES = set([METADATA])
 
 def nothing(self):
     pass</diff>
      <filename>test/test.py</filename>
    </modified>
    <modified>
      <diff>@@ -47,10 +47,7 @@ def checksum(path):
 YEAR = re.compile(r'^\d{4}$')
 
 TEXT_FIELDS = tuple([field for field in audiotools.MetaData.__FIELDS__
-                     if (field not in (&quot;track_number&quot;,
-                                       &quot;album_number&quot;,
-                                       &quot;track_total&quot;,
-                                       &quot;album_total&quot;))])
+                     if (field not in audiotools.MetaData.__INTEGER_FIELDS__)])
 
 def display_messages(messenger,track,messages):
     for message in messages:
@@ -71,6 +68,10 @@ def fix_track(track,dry_run=False):
         if (not dry_run):
             track = track.fix_id3()
         log.append(_(u&quot;Removed unnecessary ID3 tags&quot;))
+    elif (isinstance(track,DisorderedFlacAudio)):
+        if (not dry_run):
+            track = track.fix_blocks()
+        log.append(_(u&quot;Reordered metadata blocks&quot;))
 
     return (track,log)
 
@@ -406,17 +407,60 @@ class BrokenFlacAudio(audiotools.FlacAudio):
 
         return audiotools.open(self.filename)
 
+class DisorderedFlacAudio(audiotools.FlacAudio):
+    NAME = &quot;disorderedflac&quot;
+
+    def __read_streaminfo__(self):
+        f = file(self.filename,&quot;rb&quot;)
+        f.read(4)
+
+        (stop,header_type,length) = (False,None,0)
+        while (not stop):
+            (stop,header_type,length) = audiotools.FlacAudio.__read_flac_header__(f)
+            if (header_type == 0):
+                p = audiotools.FlacAudio.STREAMINFO.parse(f.read(length))
+
+                md5sum = &quot;&quot;.join([&quot;%.2X&quot; % (x) for x in p.md5]).lower()
+
+                self.__samplerate__ = p.samplerate
+                self.__channels__ = p.channels + 1
+                self.__bitspersample__ = p.bits_per_sample + 1
+                self.__total_frames__ = p.total_samples
+                self.__md5__ = &quot;&quot;.join([chr(c) for c in p.md5])
+                break
+            else:
+                f.seek(length,1)
+        f.close()
+
+    #rewrites the FLAC track with its metadata blocks in the proper order
+    def fix_blocks(self):
+        metadata = self.get_metadata()
+
+        self.set_metadata(metadata)
+
+        return audiotools.open(self.filename)
+
+    @classmethod
+    def is_type(cls,file):
+        header = file.read(4)
+        first_block_type = ord(file.read(1))
+        return (header == 'fLaC') and ((first_block_type &amp; 0x7) != 0)
+
 class PureFlacAudio(audiotools.FlacAudio):
     NAME = &quot;flac&quot;
 
     @classmethod
     def is_type(cls,file):
-        return (file.read(4) == 'fLaC')
+        header = file.read(4)
+        first_block_type = ord(file.read(1))
+        return (header == 'fLaC') and ((first_block_type &amp; 0x7) == 0)
 
 if (audiotools.FlacAudio in audiotools.AVAILABLE_TYPES):
     audiotools.AVAILABLE_TYPES = tuple([t for t in audiotools.AVAILABLE_TYPES
                                         if t != audiotools.FlacAudio] + \
-                                       [BrokenFlacAudio,PureFlacAudio])
+                                       [BrokenFlacAudio,
+                                        DisorderedFlacAudio,
+                                        PureFlacAudio])
 
 audiotools.TYPE_MAP = dict([(track_type.NAME,track_type)
                             for track_type in audiotools.AVAILABLE_TYPES</diff>
      <filename>tracklint</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>d5b478c10ff7f9dcb4459798e4afe9d2fa172016</id>
    </parent>
  </parents>
  <author>
    <name>Brian Langenberger</name>
    <email>bjl@usa.net</email>
  </author>
  <url>http://github.com/tuffy/python-audio-tools/commit/41e55aca14a06d0375194f73b64903bb02472805</url>
  <id>41e55aca14a06d0375194f73b64903bb02472805</id>
  <committed-date>2009-09-30T16:45:46-07:00</committed-date>
  <authored-date>2009-09-30T16:45:46-07:00</authored-date>
  <message>Add a tracklint fix for FLAC files that don't have STREAMINFO as the first block.
Nudged version to 2.13beta1, for real this time.</message>
  <tree>def51d3b70aef989dbc9f0505760469fc7b7f4a8</tree>
  <committer>
    <name>Brian Langenberger</name>
    <email>bjl@usa.net</email>
  </committer>
</commit>
