def __read_extended_header(self, file): size = Syncsafe.decode(fileutil.xread(file, 4)) if size < 6: warn("Unexpected size of ID3v2.4 extended header: {0}".format(size), TagWarning) data = fileutil.xread(file, size - 4) numflags = data[0] if numflags != 1: warn("Unexpected number of ID3v2.4 extended flag bytes: {0}" .format(numflags), TagWarning) flags = data[1] data = data[1 + numflags:] if flags & 0x40: self.flags.add("ext:update") (dummy, data) = self.__read_extended_header_flag_data(data) if flags & 0x20: self.flags.add("ext:crc_present") (self.crc32, data) = self.__read_extended_header_flag_data(data) self.crc32 = Syncsafe.decode(self.crc32) if flags & 0x10: self.flags.add("ext:restrictions") (self.restrictions, data) = self.__read_extended_header_flag_data( data)
def __read_extended_header(self, file): (size, ext_flags, self.padding_size) = \ struct.unpack("!IHI", fileutil.xread(file, 10)) if size != 6 and size != 10: warn("Unexpected size of ID3v2.3 extended header: {0}".format(size), TagWarning) if ext_flags & 128: self.flags.add("ext:crc_present") self.crc32 = struct.unpack("!I", fileutil.xread(file, 4))
def _read_frames(self, file): if "unsynchronised" in self.flags: ufile = UnsyncReader(file) else: ufile = file while file.tell() < self.offset + self.size: header = fileutil.xread(ufile, 6) if not self._is_frame_id(header[0:3]): break frameid = header[0:3].decode("ASCII") size = Int8.decode(header[3:6]) data = fileutil.xread(ufile, size) yield (frameid, None, data)
def __read_extended_header(self, file): (size, ext_flags, self.padding_size) = \ struct.unpack("!IHI", fileutil.xread(file, 10)) if size != 6 and size != 10: warn("Unexpected size of ID3v2.3 extended header: {0}".format(size), TagWarning) if ext_flags & 32768: if size < 10: warn("Extended header is too short for a CRC field: {0} bytes instead of 10".format(size), TagWarning) else: self.flags.add("ext:crc_present") (self.crc32,) = struct.unpack("!I", fileutil.xread(file, 4)) size -= 6 if size > 6: fileutil.xread(file, size - 6)
def _read_frames(self, file): while file.tell() < self.offset + self.size: header = fileutil.xread(file, 10) if not self._is_frame_id(header[0:4]): break frameid = header[0:4].decode("ASCII") if self.ITUNES_WORKAROUND: # Work around iTunes frame size encoding bug. # Older versions of iTunes stored frame sizes as # straight 8bit integers, not syncsafe. # (This is known to be fixed in iTunes 8.2.) size = Int8.decode(header[4:8]) else: size = Syncsafe.decode(header[4:8]) bflags = Int8.decode(header[8:10]) data = fileutil.xread(file, size) yield (frameid, bflags, data)
def _read_frames(self, file, syncsafe_workaround=None): # Older versions of iTunes stored frame sizes as straight 8bit integers, # not syncsafe values as the spec requires. # (The bug is known to be fixed in iTunes 8.2.) # # To work around such an erroneous encoding, we re-read the entire tag # in non-syncsafe mode when we detect a frame with a bad size. # This heuristic does not detect all badly encoded tags; # it fails when the 8-bit frame size happens to be in syncsafe format. # # We could improve detection by parsing the tag both ways and see which # interpretation produces more frames. However, the extra effort doesn't # seem worthwhile to do by default. # # If you have many files with iTunes-encoded tags, you can force stagger # to read them in non-syncsafe mode setting the ITUNES_WORKAROUND # class attribute to True and let stagger reencode your tags. (Stagger # will never produce a 2.4 tag with non-syncsafe frame lengths.) if syncsafe_workaround is None: syncsafe_workaround = self.ITUNES_WORKAROUND origfpos = file.tell() frames = [] while file.tell() < self.offset + self.size: header = fileutil.xread(file, 10) if not self._is_frame_id(header[0:4]): break frameid = header[0:4].decode("ASCII") if syncsafe_workaround: size = Int8.decode(header[4:8]) else: try: size = Syncsafe.decode(header[4:8]) except ValueError: if syncsafe_workaround: raise warn( "Invalid syncsafe frame size; switching to 8-bit mode") file.seek(origfpos) return self._read_frames(file, True) bflags = Int8.decode(header[8:10]) data = fileutil.xread(file, size) frames.append((frameid, bflags, data)) return frames
def _read_header(self, file): self.offset = file.tell() header = fileutil.xread(file, 10) if header[0:5] != b"ID3\x02\00": raise NoTagError("ID3v2.2 header not found") if header[5] & 0x80: self.flags.add("unsynchronised") if header[5] & 0x40: # Compression bit is ill-defined in standard raise TagError("ID3v2.2 tag compression is not supported") if header[5] & 0x3F: warn("Unknown ID3v2.2 flags", TagWarning) self.size = Syncsafe.decode(header[6:10]) + 10
def _read_header(self, file): self.offset = file.tell() header = fileutil.xread(file, 10) if header[0:5] != b"ID3\x03\x00": raise NoTagError("ID3v2.3 header not found") if header[5] & 0x80: self.flags.add("unsynchronised") if header[5] & 0x40: self.flags.add("extended_header") if header[5] & 0x20: self.flags.add("experimental") if header[5] & 0x1F: warn("Unknown ID3v2.3 flags", TagWarning) self.size = Syncsafe.decode(header[6:10]) + 10 if "extended_header" in self.flags: self.__read_extended_header(file)
def _read_header(self, file): self.offset = file.tell() header = fileutil.xread(file, 10) if header[0:5] != b"ID3\x04\x00": raise NoTagError("ID3v2 header not found") if header[5] & _TAG24_UNSYNCHRONISED: self.flags.add("unsynchronised") if header[5] & _TAG24_EXTENDED_HEADER: self.flags.add("extended_header") if header[5] & _TAG24_EXPERIMENTAL: self.flags.add("experimental") if header[5] & _TAG24_FOOTER: self.flags.add("footer") if header[5] & _TAG24_UNKNOWN_MASK: warn("Unknown ID3v2.4 flags", TagWarning) self.size = (Syncsafe.decode(header[6:10]) + 10 + (10 if "footer" in self.flags else 0)) if "extended_header" in self.flags: self.__read_extended_header(file)