Ejemplo n.º 1
0
    def load(self, filething, known_frames=None, translate=True, v2_version=4):
        """load(filething, known_frames=None, translate=True, v2_version=4)

        Load tags from a filename.

        Args:
            filename (filething): filename or file object to load tag data from
            known_frames (Dict[`mutagen.text`, `Frame`]): dict mapping frame
                IDs to Frame objects
            translate (bool): Update all tags to ID3v2.3/4 internally. If you
                intend to save, this must be true or you have to
                call update_to_v23() / update_to_v24() manually.
            v2_version (int): if update_to_v23 or update_to_v24 get called
                (3 or 4)

        Example of loading a custom frame::

            my_frames = dict(mutagen.id3.Frames)
            class XMYF(Frame): ...
            my_frames["XMYF"] = XMYF
            mutagen.id3.ID3(filename, known_frames=my_frames)
        """

        fileobj = filething.fileobj

        if v2_version not in (3, 4):
            raise ValueError("Only 3 and 4 possible for v2_version")

        self.unknown_frames = []
        self._header = None
        self._padding = 0

        self._pre_load_header(fileobj)

        try:
            self._header = ID3Header(fileobj)
        except (ID3NoHeaderError, ID3UnsupportedVersionError):
            frames, offset = find_id3v1(fileobj)
            if frames is None:
                raise

            self.version = ID3Header._V11
            for v in frames.values():
                self.add(v)
        else:
            # XXX: attach to the header object so we have it in spec parsing..
            if known_frames is not None:
                self._header._known_frames = known_frames

            data = read_full(fileobj, self.size - 10)
            remaining_data = self._read(self._header, data)
            self._padding = len(remaining_data)

        if translate:
            if v2_version == 3:
                self.update_to_v23()
            else:
                self.update_to_v24()
Ejemplo n.º 2
0
    def load(self, filething, known_frames=None, translate=True, v2_version=4):
        """load(filething, known_frames=None, translate=True, v2_version=4)

        Load tags from a filename.

        Args:
            filename (filething): filename or file object to load tag data from
            known_frames (Dict[`mutagen.text`, `Frame`]): dict mapping frame
                IDs to Frame objects
            translate (bool): Update all tags to ID3v2.3/4 internally. If you
                intend to save, this must be true or you have to
                call update_to_v23() / update_to_v24() manually.
            v2_version (int): if update_to_v23 or update_to_v24 get called
                (3 or 4)

        Example of loading a custom frame::

            my_frames = dict(mutagen.id3.Frames)
            class XMYF(Frame): ...
            my_frames["XMYF"] = XMYF
            mutagen.id3.ID3(filename, known_frames=my_frames)
        """

        fileobj = filething.fileobj

        if v2_version not in (3, 4):
            raise ValueError("Only 3 and 4 possible for v2_version")

        self.unknown_frames = []
        self._header = None
        self._padding = 0

        self._pre_load_header(fileobj)

        try:
            self._header = ID3Header(fileobj)
        except (ID3NoHeaderError, ID3UnsupportedVersionError):
            frames, offset = find_id3v1(fileobj)
            if frames is None:
                raise

            self.version = ID3Header._V11
            for v in frames.values():
                self.add(v)
        else:
            # XXX: attach to the header object so we have it in spec parsing..
            if known_frames is not None:
                self._header._known_frames = known_frames

            data = read_full(fileobj, self.size - 10)
            remaining_data = self._read(self._header, data)
            self._padding = len(remaining_data)

        if translate:
            if v2_version == 3:
                self.update_to_v23()
            else:
                self.update_to_v24()
Ejemplo n.º 3
0
    def __init__(self, fileobj=None):
        """Raises ID3NoHeaderError, ID3UnsupportedVersionError or error"""

        if fileobj is None:
            # for testing
            self._flags = 0
            return

        fn = getattr(fileobj, "name", "<unknown>")
        data = fileobj.read(10)
        if len(data) != 10:
            raise ID3NoHeaderError("%s: too small" % fn)

        id3, vmaj, vrev, flags, size = struct.unpack('>3sBBB4s', data)
        self._flags = flags
        self.size = BitPaddedInt(size) + 10
        self.version = (2, vmaj, vrev)

        if id3 != b'ID3':
            raise ID3NoHeaderError("%r doesn't start with an ID3 tag" % fn)

        if vmaj not in [2, 3, 4]:
            raise ID3UnsupportedVersionError("%r ID3v2.%d not supported" %
                                             (fn, vmaj))

        if not BitPaddedInt.has_valid_padding(size):
            raise error("Header size not synchsafe")

        if (self.version >= self._V24) and (flags & 0x0f):
            raise error("%r has invalid flags %#02x" % (fn, flags))
        elif (self._V23 <= self.version < self._V24) and (flags & 0x1f):
            raise error("%r has invalid flags %#02x" % (fn, flags))

        if self.f_extended:
            extsize_data = read_full(fileobj, 4)

            if PY3:
                frame_id = extsize_data.decode("ascii", "replace")
            else:
                frame_id = extsize_data

            if frame_id in Frames:
                # Some tagger sets the extended header flag but
                # doesn't write an extended header; in this case, the
                # ID3 data follows immediately. Since no extended
                # header is going to be long enough to actually match
                # a frame, and if it's *not* a frame we're going to be
                # completely lost anyway, this seems to be the most
                # correct check.
                # https://github.com/quodlibet/quodlibet/issues/126
                self._flags ^= 0x40
                extsize = 0
                fileobj.seek(-4, 1)
            elif self.version >= self._V24:
                # "Where the 'Extended header size' is the size of the whole
                # extended header, stored as a 32 bit synchsafe integer."
                extsize = BitPaddedInt(extsize_data) - 4
                if not BitPaddedInt.has_valid_padding(extsize_data):
                    raise error("Extended header size not synchsafe")
            else:
                # "Where the 'Extended header size', currently 6 or 10 bytes,
                # excludes itself."
                extsize = struct.unpack('>L', extsize_data)[0]

            self._extdata = read_full(fileobj, extsize)
Ejemplo n.º 4
0
    def __init__(self, fileobj=None):
        """Raises ID3NoHeaderError, ID3UnsupportedVersionError or error"""

        if fileobj is None:
            # for testing
            self._flags = 0
            return

        fn = getattr(fileobj, "name", "<unknown>")
        data = fileobj.read(10)
        if len(data) != 10:
            raise ID3NoHeaderError("%s: too small" % fn)

        id3, vmaj, vrev, flags, size = struct.unpack('>3sBBB4s', data)
        self._flags = flags
        self.size = BitPaddedInt(size) + 10
        self.version = (2, vmaj, vrev)

        if id3 != b'ID3':
            raise ID3NoHeaderError("%r doesn't start with an ID3 tag" % fn)

        if vmaj not in [2, 3, 4]:
            raise ID3UnsupportedVersionError("%r ID3v2.%d not supported"
                                             % (fn, vmaj))

        if not BitPaddedInt.has_valid_padding(size):
            raise error("Header size not synchsafe")

        if (self.version >= self._V24) and (flags & 0x0f):
            raise error(
                "%r has invalid flags %#02x" % (fn, flags))
        elif (self._V23 <= self.version < self._V24) and (flags & 0x1f):
            raise error(
                "%r has invalid flags %#02x" % (fn, flags))

        if self.f_extended:
            extsize_data = read_full(fileobj, 4)

            if PY3:
                frame_id = extsize_data.decode("ascii", "replace")
            else:
                frame_id = extsize_data

            if frame_id in Frames:
                # Some tagger sets the extended header flag but
                # doesn't write an extended header; in this case, the
                # ID3 data follows immediately. Since no extended
                # header is going to be long enough to actually match
                # a frame, and if it's *not* a frame we're going to be
                # completely lost anyway, this seems to be the most
                # correct check.
                # https://github.com/quodlibet/quodlibet/issues/126
                self._flags ^= 0x40
                extsize = 0
                fileobj.seek(-4, 1)
            elif self.version >= self._V24:
                # "Where the 'Extended header size' is the size of the whole
                # extended header, stored as a 32 bit synchsafe integer."
                extsize = BitPaddedInt(extsize_data) - 4
                if not BitPaddedInt.has_valid_padding(extsize_data):
                    raise error(
                        "Extended header size not synchsafe")
            else:
                # "Where the 'Extended header size', currently 6 or 10 bytes,
                # excludes itself."
                extsize = struct.unpack('>L', extsize_data)[0]

            self._extdata = read_full(fileobj, extsize)