def load(self, fh, limit_frames=None): """ Load a file and extract ID3v2 data """ _match_frame = self._match_frame self.fh = fh fh.seek(0) if fh.read(3) != 'ID3': return verinfo = fh.read(2) self.version = ( 2, ord(verinfo[0]), ord(verinfo[1]), ) if (self.version[1] < 3): raise Error, "Cannot process tags with version less than 2.3.0 (This tag's version is 2.%s.%s)" % ( self.version[1], self.version[2], ) if (self.version[1] > 4) or (self.version[2] > 0): raise Error, "Cannot process tags with version greater than 2.4.0 (This tag's version is 2.%s.%s)" % ( self.version[1], self.version[2], ) tag_flags = binfuncs.byte2bin(fh.read(1), 8) self._unsync = tag_flags[0] self.extended_header = tag_flags[1] self.experimental = tag_flags[2] self.footer = tag_flags[3] assert tag_flags[4] == 0 assert tag_flags[5] == 0 assert tag_flags[6] == 0 assert tag_flags[7] == 0 if self.extended_header: raise Error("Don't know what to do with an extended header") self.tag_size = binfuncs.synchsafe2dec(fh.read(4)) if DEBUG_LEVEL >= 1: print "tag version: %d.%d.%d" % self.version print "tag size:", self.tag_size print "unsync:", self.unsync if self.version[1] == 3 and self.unsync: # print self.tag_size tag = fh.read(self.tag_size) tag = binfuncs.deunsynchstr(tag) self.tag_size = len(tag) fh = StringIO.StringIO(tag) sizeleft = self.tag_size # 11 == frame header + 1 byte frame, smallest legal frame while sizeleft >= 11: frameid = fh.read(4) if _match_frame(frameid) or frameid in ID3v2Frames.frameTypes[ self.version[1]]: rawframesize = fh.read(4) if self.version[1] >= 4 and frameid != 'COM ': framesize = binfuncs.synchsafe2dec(rawframesize) (framesize23, ) = struct.unpack('!I', rawframesize) # print "2.4: %d vs 2.3: %d" % (framesize, framesize23,) else: (framesize, ) = struct.unpack('!I', rawframesize) if framesize > sizeleft + 2: if self.broken_frames == 'drop': # warnings.warn("Broken frame size in %r. Dropping rest of tag." % self.filename) self.padding_size = sizeleft sizeleft = 0 break else: raise BrokenFrameError( "Invalid frame size %r (raw: %r). Frame type was %r. Corrupt tag." % ( framesize, rawframesize, frameid, )) data = fh.read(framesize + 2) if DEBUG_LEVEL >= 2: print "Raw frame: %r" % (data, ) elif frameid == '\x00\x00\x00\x00' or frameid == 'MP3e': # MP3ext http://www.mutschler.de/mp3ext/ puts "MP3ext " over and over in the padding sizeleft -= 4 break else: try: lastframeid = self.frames[-1].id except IndexError: lastframeid = None raise Error( "Found garbage where I expected a Frame Id %r. Last frame was %r" % ( frameid, lastframeid, )) try: if limit_frames and frameid in limit_frames: self.new_frame(frameid, data) except BrokenFrameError, err: if self.broken_frames == 'drop': # warnings.warn("Broken frame in %r. Dropping frame." % self.filename) pass else: raise sizeleft -= (framesize + 2 + 4 + 4)
def _parse_frame(self, data): flags = binfuncs.byte2bin(data[:2], 8) self.data = data[2:] if self.version[1] == 3: # %abc00000 %ijk00000 self.tag_alter_preservation = flags[0] self.file_alter_preservation = flags[1] self.read_only = flags[2] assert flags[3] == 0 assert flags[4] == 0 assert flags[5] == 0 assert flags[6] == 0 assert flags[7] == 0 self._compression = flags[8] self.encryption = flags[9] self.grouping_id = flags[10] assert flags[11] == 0 assert flags[12] == 0 assert flags[13] == 0 assert flags[14] == 0 assert flags[15] == 0 elif self.version[1] == 4: # %0abc0000 %0h00kmnp assert flags[0] == 0 self.tag_alter_preservation = flags[1] self.file_alter_preservation = flags[2] self.read_only = flags[3] assert flags[4] == 0 assert flags[5] == 0 assert flags[6] == 0 assert flags[7] == 0 assert flags[8] == 0 self.grouping_id = flags[9] assert flags[10] == 0 assert flags[11] == 0 self._compression = flags[12] self.encryption = flags[13] self._unsynchronisation = flags[14] self.data_length_indicator = flags[15] if self._compression and not self.data_length_indicator: raise dnuos.id3.BrokenFrameError, "The compression flag was set but not the data_length_indicator" else: raise dnuos.id3.Error("Unsupported tag (how did we not catch this before?)") # these add bytes to the header if self.grouping_id: self.group_id = self.data[0] self.data = self.data[1:] if self.encryption: self.encryption_method = self.data[0] self.data = self.data[1:] if self.version[1] == 3 and self.compression: (self.true_size,) = struct.unpack('!I', self.data[:4]) self.data = self.data[4:] if self.version[1] == 4 and self.data_length_indicator: self.true_size = binfuncs.synchsafe2dec(self.data[:4]) self.data = self.data[4:] # now we post process if self.unsynchronisation: self.data = binfuncs.unsynchstr(self.data) # if self.encryption: # warnings.warn("Encrypted frame (method %r, frameid %r" % (self.encryption_method, self.frameid,)) if self.compression: self.data = zlib.decompress(self.data) if self.true_size: assert len(self.data) == self.true_size, "len(data) == %d should be == %d, and should %r" % (len(self.data), self.true_size, self.__dict__) # now we run the subclass's data parser self.parse_data()
def _parse_frame(self, data): flags = binfuncs.byte2bin(data[:2], 8) self.data = data[2:] if self.version[1] == 3: # %abc00000 %ijk00000 self.tag_alter_preservation = flags[0] self.file_alter_preservation = flags[1] self.read_only = flags[2] assert flags[3] == 0 assert flags[4] == 0 assert flags[5] == 0 assert flags[6] == 0 assert flags[7] == 0 self._compression = flags[8] self.encryption = flags[9] self.grouping_id = flags[10] assert flags[11] == 0 assert flags[12] == 0 assert flags[13] == 0 assert flags[14] == 0 assert flags[15] == 0 elif self.version[1] == 4: # %0abc0000 %0h00kmnp assert flags[0] == 0 self.tag_alter_preservation = flags[1] self.file_alter_preservation = flags[2] self.read_only = flags[3] assert flags[4] == 0 assert flags[5] == 0 assert flags[6] == 0 assert flags[7] == 0 assert flags[8] == 0 self.grouping_id = flags[9] assert flags[10] == 0 assert flags[11] == 0 self._compression = flags[12] self.encryption = flags[13] self._unsynchronisation = flags[14] self.data_length_indicator = flags[15] if self._compression and not self.data_length_indicator: raise dnuos.id3.BrokenFrameError, "The compression flag was set but not the data_length_indicator" else: raise dnuos.id3.Error( "Unsupported tag (how did we not catch this before?)") # these add bytes to the header if self.grouping_id: self.group_id = self.data[0] self.data = self.data[1:] if self.encryption: self.encryption_method = self.data[0] self.data = self.data[1:] if self.version[1] == 3 and self.compression: (self.true_size, ) = struct.unpack('!I', self.data[:4]) self.data = self.data[4:] if self.version[1] == 4 and self.data_length_indicator: self.true_size = binfuncs.synchsafe2dec(self.data[:4]) self.data = self.data[4:] # now we post process if self.unsynchronisation: self.data = binfuncs.unsynchstr(self.data) # if self.encryption: # warnings.warn("Encrypted frame (method %r, frameid %r" % (self.encryption_method, self.frameid,)) if self.compression: self.data = zlib.decompress(self.data) if self.true_size: assert len( self.data ) == self.true_size, "len(data) == %d should be == %d, and should %r" % ( len(self.data), self.true_size, self.__dict__) # now we run the subclass's data parser self.parse_data()
def load(self, fh, limit_frames=None): """ Load a file and extract ID3v2 data """ _match_frame = self._match_frame self.fh = fh fh.seek(0) if fh.read(3) != 'ID3': return verinfo = fh.read(2) self.version = (2,ord(verinfo[0]),ord(verinfo[1]),) if(self.version[1] < 3): raise Error, "Cannot process tags with version less than 2.3.0 (This tag's version is 2.%s.%s)" % (self.version[1],self.version[2],) if(self.version[1] > 4) or (self.version[2] > 0): raise Error, "Cannot process tags with version greater than 2.4.0 (This tag's version is 2.%s.%s)" % (self.version[1], self.version[2],) tag_flags = binfuncs.byte2bin(fh.read(1), 8) self._unsync = tag_flags[0] self.extended_header = tag_flags[1] self.experimental = tag_flags[2] self.footer = tag_flags[3] assert tag_flags[4] == 0 assert tag_flags[5] == 0 assert tag_flags[6] == 0 assert tag_flags[7] == 0 if self.extended_header: raise Error("Don't know what to do with an extended header") self.tag_size = binfuncs.synchsafe2dec(fh.read(4)) if DEBUG_LEVEL >= 1: print "tag version: %d.%d.%d" % self.version print "tag size:", self.tag_size print "unsync:", self.unsync if self.version[1] == 3 and self.unsync: # print self.tag_size tag = fh.read(self.tag_size) tag = binfuncs.deunsynchstr(tag) self.tag_size = len(tag) fh = StringIO.StringIO(tag) sizeleft = self.tag_size # 11 == frame header + 1 byte frame, smallest legal frame while sizeleft >= 11: frameid = fh.read(4) if _match_frame(frameid) or frameid in ID3v2Frames.frameTypes[self.version[1]]: rawframesize = fh.read(4) if self.version[1] >= 4 and frameid != 'COM ': framesize = binfuncs.synchsafe2dec(rawframesize) (framesize23,) = struct.unpack('!I', rawframesize) # print "2.4: %d vs 2.3: %d" % (framesize, framesize23,) else: (framesize,) = struct.unpack('!I', rawframesize) if framesize > sizeleft + 2: if self.broken_frames == 'drop': # warnings.warn("Broken frame size in %r. Dropping rest of tag." % self.filename) self.padding_size = sizeleft sizeleft = 0 break else: raise BrokenFrameError("Invalid frame size %r (raw: %r). Frame type was %r. Corrupt tag." % (framesize,rawframesize,frameid,)) data = fh.read(framesize + 2) if DEBUG_LEVEL >= 2: print "Raw frame: %r" % (data,) elif frameid == '\x00\x00\x00\x00' or frameid == 'MP3e': # MP3ext http://www.mutschler.de/mp3ext/ puts "MP3ext " over and over in the padding sizeleft -= 4 break else: try: lastframeid = self.frames[-1].id except IndexError: lastframeid = None raise Error("Found garbage where I expected a Frame Id %r. Last frame was %r" % (frameid, lastframeid,)) try: if limit_frames and frameid in limit_frames: self.new_frame(frameid, data) except BrokenFrameError, err: if self.broken_frames == 'drop': # warnings.warn("Broken frame in %r. Dropping frame." % self.filename) pass else: raise sizeleft -= (framesize + 2 + 4 + 4)