Exemple #1
0
 def test_long(self):
     if PY2:
         data = BitPaddedInt.to_str(sys.maxint + 1, width=16)
         val = BitPaddedInt(data)
         self.assertEqual(val, sys.maxint + 1)
         self.assertTrue(isinstance(val, BitPaddedLong))
     else:
         self.assertTrue(BitPaddedInt is BitPaddedLong)
    def _digest_from_file(self):
        with io.open(self.name, 'rb') as f:
            start = 0
            end = self.size

            try:
                # technically an insize=0 tag is invalid, but we skip it anyway
                f.seek(start)
                idata = f.read(10)
                try:
                    id3, insize = struct.unpack('>3sxxx4s', idata)
                    insize = BitPaddedInt(insize)
                    if id3 == 'ID3' and insize >= 0 and insize + 10 <= end:
                        start = insize + 10
                except struct.error:
                    pass

                f.seek(-128, 2)
                idata = f.read(3)
                if idata == "TAG":
                    end -= 128

            except IOError:
                pass

            f.seek(start)
            buf = f.read(end - start)
            return hashlib.sha256(buf).hexdigest()
Exemple #3
0
    def __init__(self, fileobj, offset=None):
        """Parse MPEG stream information from a file-like object.

        If an offset argument is given, it is used to start looking
        for stream information and Xing headers; otherwise, ID3v2 tags
        will be skipped automatically. A correct offset can make
        loading files significantly faster.
        """

        try: size = os.path.getsize(fileobj.name)
        except (IOError, OSError, AttributeError):
            fileobj.seek(0, 2)
            size = fileobj.tell()

        # If we don't get an offset, try to skip an ID3v2 tag.
        if offset is None:
            fileobj.seek(0, 0)
            idata = fileobj.read(10)
            try: id3, insize = struct.unpack('>3sxxx4s', idata)
            except struct.error: id3, insize = '', 0
            insize = BitPaddedInt(insize)
            if id3 == 'ID3' and insize > 0:
                offset = insize + 10
            else: offset = 0

        # Try to find two valid headers (meaning, very likely MPEG data)
        # at the given offset, 30% through the file, 60% through the file,
        # and 90% through the file.
        for i in [offset, 0.3 * size, 0.6 * size, 0.9 * size]:
            try: self.__try(fileobj, int(i), size - offset)
            except error, e: pass
            else: break
Exemple #4
0
 def save_frame(frame):
     #Copied from mutagen.id3.ID3
     flags = 0
     framedata = frame._writeData()
     datasize = BitPaddedInt.to_str(len(framedata), width=4)
     header = pack('>4s4sH', type(frame).__name__, datasize, flags)
     return header + framedata
Exemple #5
0
 def save_frame(frame):
     #Copied from mutagen.id3.ID3
     flags = 0
     framedata = frame._writeData()
     datasize = BitPaddedInt.to_str(len(framedata), width=4)
     header = pack('>4s4sH', type(frame).__name__, datasize, flags)
     return header + framedata
Exemple #6
0
 def test_long(self):
     if PY2:
         data = BitPaddedInt.to_str(sys.maxint + 1, width=16)
         val = BitPaddedInt(data)
         self.assertEqual(val, sys.maxint + 1)
         self.assertTrue(isinstance(val, BitPaddedLong))
     else:
         self.assertTrue(BitPaddedInt is BitPaddedLong)
Exemple #7
0
 def __save_frame(self, frame, v2):
     flags = 0
     if self.PEDANTIC and isinstance(frame, TextFrame):
         if len(str(frame)) == 0: return ''
     framedata = frame._writeData()
     if v2 == 3: bits=8
     else: bits=7
     datasize = BitPaddedInt.to_str(len(framedata), width=4, bits=bits)
     header = pack('>4s4sH', type(frame).__name__, datasize, flags)
     return header + framedata
Exemple #8
0
def get_id3(client, path, **defaults):

    logger = client.logger
    logger.info('getting ID3 for %r', path)

    resp = client.get_file(path)
    cl = long(resp.getheader('Content-Length'))

    logger.info('got resp status=%s; len=%d', resp.status, cl)

    # Possibly ID3v2 header
    data = resp.read(10)
    id3, vmaj, vrev, flags, size = unpack('>3sBBB4s', data)

    if id3 == 'ID3':
        extra = 100  # HACK to avoid ocasional EOF
        size = BitPaddedInt(size)

        logger.info(
            'trying ID3v2.x; fetching additional'
            ' %s bytes + %s extra', size, extra)

        body = resp.read(size + extra)

        logger.info('got %s bytes' % len(body))

        data += body
        resp.close()
    else:
        logger.info('trying ID3v1.x')

        resp.close()
        data = client.get_byte_range(path, cl - 128, cl)

        if not data.startswith('TAG'):

            logger.warning('no ID3 tags found')

            return None

    with NamedTemporaryFile('wb') as f:
        f.write(data)
        f.flush()
        try:
            tags = EasyID3(f.name)
        except ID3Error:

            logger.exception('exception from mutagen')

            return None

    logger.debug('extracted ID3 %r', tags)

    return id3_to_dict(tags, defaults)
Exemple #9
0
def _id3_header_length(f):
    try:
        # technically an insize=0 tag is invalid, but we skip it anyway
        end = f.seek(0, io.SEEK_END)
        f.seek(0)
        idata = f.read(10)
        id3, insize = struct.unpack('>3sxxx4s', idata)
        insize = BitPaddedInt(insize)
        if id3 == 'ID3' and 0 <= insize <= end - 10:
            return insize + 10
    except (IOError, struct.error):
        pass
    return 0
Exemple #10
0
 def __check_header(self, fileobj):
     size = 4
     header = fileobj.read(4)
     if header != "fLaC":
         size = None
         if header[:3] == "ID3":
             size = 14 + BitPaddedInt(fileobj.read(6)[2:])
             fileobj.seek(size - 4)
             if fileobj.read(4) != "fLaC": size = None
     if size is None:
         raise FLACNoHeaderError("%r is not a valid FLAC file" %
                                 fileobj.name)
     return size
Exemple #11
0
    def __init__(self, fileobj):
        header = bytearray(fileobj.read(32))
        if len(header) != 32:
            raise MusepackHeaderError("not a Musepack file")
        # Skip ID3v2 tags
        if header[:3] == b"ID3":
            size = 10 + BitPaddedInt(header[6:10])
            fileobj.seek(size)
            header = bytearray(fileobj.read(32))
            if len(header) != 32:
                raise MusepackHeaderError("not a Musepack file")
        # SV7
        if header.startswith(b"MP+"):
            self.version = header[3] & 0xF
            if self.version < 7:
                raise MusepackHeaderError("not a Musepack file")
            frames = cdata.uint_le(header[4:8])
            flags = cdata.uint_le(header[8:12])

            self.title_peak, self.title_gain = struct_unpack(
                "<Hh", header[12:16])
            self.album_peak, self.album_gain = struct_unpack(
                "<Hh", header[16:20])
            self.title_gain /= 100.0
            self.album_gain /= 100.0
            self.title_peak /= 65535.0
            self.album_peak /= 65535.0

            self.sample_rate = RATES[(flags >> 16) & 0x0003]
            self.bitrate = 0
        # SV4-SV6
        else:
            header_dword = cdata.uint_le(header[0:4])
            self.version = (header_dword >> 11) & 0x03FF
            if self.version < 4 or self.version > 6:
                raise MusepackHeaderError("not a Musepack file")
            self.bitrate = (header_dword >> 23) & 0x01FF
            self.sample_rate = 44100
            if self.version >= 5:
                frames = cdata.uint_le(header[4:8])
            else:
                frames = cdata.ushort_le(header[6:8])
            if self.version < 6:
                frames -= 1
        self.channels = 2
        self.length = float(frames * 1152 - 576) / self.sample_rate
        if not self.bitrate and self.length != 0:
            fileobj.seek(0, 2)
            self.bitrate = int(fileobj.tell() * 8 / (self.length * 1000) + 0.5)
Exemple #12
0
    def __init__(self, fileobj):
        # skip id3v2 header
        start_offset = 0
        header = fileobj.read(10)
        from mutagen.id3 import BitPaddedInt
        if header.startswith(b"ID3"):
            size = BitPaddedInt(header[6:])
            start_offset = size + 10

        fileobj.seek(start_offset)
        adif = fileobj.read(4)
        if adif == b"ADIF":
            self._parse_adif(fileobj)
            self._type = "ADIF"
        else:
            self._parse_adts(fileobj, start_offset)
            self._type = "ADTS"
Exemple #13
0
    def __check_header(self, fileobj):
        """Returns the offset of the flac block start
        (skipping id3 tags if found). The passed fileobj will be advanced to
        that offset as well.
        """

        size = 4
        header = fileobj.read(4)
        if header != b"fLaC":
            size = None
            if header[:3] == b"ID3":
                size = 14 + BitPaddedInt(fileobj.read(6)[2:])
                fileobj.seek(size - 4)
                if fileobj.read(4) != b"fLaC":
                    size = None
        if size is None:
            raise FLACNoHeaderError("%r is not a valid FLAC file" %
                                    fileobj.name)
        return size
Exemple #14
0
    def __init__(self, fileobj):
        header = fileobj.read(4)
        if len(header) != 4:
            raise MusepackHeaderError("not a Musepack file")

        # Skip ID3v2 tags
        if header[:3] == b"ID3":
            header = fileobj.read(6)
            if len(header) != 6:
                raise MusepackHeaderError("not a Musepack file")
            size = 10 + BitPaddedInt(header[2:6])
            fileobj.seek(size)
            header = fileobj.read(4)
            if len(header) != 4:
                raise MusepackHeaderError("not a Musepack file")

        if header.startswith(b"MPCK"):
            self.__parse_sv8(fileobj)
        else:
            self.__parse_sv467(fileobj)

        if not self.bitrate and self.length != 0:
            fileobj.seek(0, 2)
            self.bitrate = int(round(fileobj.tell() * 8 / self.length))
Exemple #15
0
 def test_s32b(self):
     self.assertEquals(BitPaddedInt(b'\xFF\xFF\xFF\xFF', bits=8).as_str(),
                       b'\xFF\xFF\xFF\xFF')
Exemple #16
0
    def test_has_valid_padding(self):
        self.failUnless(BitPaddedInt.has_valid_padding(b"\xff\xff", bits=8))
        self.failIf(BitPaddedInt.has_valid_padding(b"\xff"))
        self.failIf(BitPaddedInt.has_valid_padding(b"\x00\xff"))
        self.failUnless(BitPaddedInt.has_valid_padding(b"\x7f\x7f"))
        self.failIf(BitPaddedInt.has_valid_padding(b"\x7f", bits=6))
        self.failIf(BitPaddedInt.has_valid_padding(b"\x9f", bits=6))
        self.failUnless(BitPaddedInt.has_valid_padding(b"\x3f", bits=6))

        self.failUnless(BitPaddedInt.has_valid_padding(0xff, bits=8))
        self.failIf(BitPaddedInt.has_valid_padding(0xff))
        self.failIf(BitPaddedInt.has_valid_padding(0xff << 8))
        self.failUnless(BitPaddedInt.has_valid_padding(0x7f << 8))
        self.failIf(BitPaddedInt.has_valid_padding(0x9f << 32, bits=6))
        self.failUnless(BitPaddedInt.has_valid_padding(0x3f << 16, bits=6))
Exemple #17
0
 def test_minwidth(self):
     self.assertEquals(
         len(BitPaddedInt.to_str(100, width=-1, minwidth=6)), 6)
Exemple #18
0
 def test_str_int_init(self):
     from struct import pack
     self.assertEquals(BitPaddedInt(238).as_str(),
                       BitPaddedInt(pack('>L', 238)).as_str())
Exemple #19
0
 def test_w129(self):
     self.assertEquals(BitPaddedInt.to_str(129, width=2), b'\x01\x01')
Exemple #20
0
 def test_s129(self):
     self.assertEquals(BitPaddedInt.to_str(129), b'\x00\x00\x01\x01')
Exemple #21
0
    def save(self, filename=None, v1=1, v2=4):
        """Save changes to a file.

        If no filename is given, the one most recently loaded is used.

        Keyword arguments:
        v1 -- if 0, ID3v1 tags will be removed
              if 1, ID3v1 tags will be updated but not added
              if 2, ID3v1 tags will be created and/or updated
        v2 -- version of ID3v2 tags (3 or 4). By default Mutagen saves ID3v2.4
              tags. If you want to save ID3v2.3 tags, you must call method
              update_to_v23 before saving the file.

        The lack of a way to update only an ID3v1 tag is intentional.
        """

        # Sort frames by 'importance'
        order = ["TIT2", "TPE1", "TRCK", "TALB", "TPOS", "TDRC", "TCON"]
        order = dict(zip(order, range(len(order))))
        last = len(order)
        frames = self.items()
        frames.sort(lambda a, b: cmp(order.get(a[0][:4], last),
                                     order.get(b[0][:4], last)))

        framedata = [self.__save_frame(frame, v2) for (key, frame) in frames]
        framedata.extend([data for data in self.unknown_frames
                          if len(data) > 10])
        if not framedata:
            try:
                self.delete(filename)
            except EnvironmentError as err:
                from errno import ENOENT
                if err.errno != ENOENT:
                    raise
            return

        framedata = ''.join(framedata)
        framesize = len(framedata)

        if filename is None:
            filename = self.filename
        try:
            f = open(filename, 'rb+')
        except IOError as err:
            from errno import ENOENT
            if err.errno != ENOENT:
                raise
            f = open(filename, 'ab')  # create, then reopen
            f = open(filename, 'rb+')
        try:
            idata = f.read(10)
            try:
                id3, vmaj, vrev, flags, insize = unpack('>3sBBB4s', idata)
            except struct.error:
                id3, insize = '', 0
            insize = BitPaddedInt(insize)
            if id3 != 'ID3':
                insize = -10

            if insize >= framesize:
                outsize = insize
            else:
                outsize = (framesize + 1023) & ~0x3FF
            framedata += '\x00' * (outsize - framesize)

            framesize = BitPaddedInt.to_str(outsize, width=4)
            flags = 0
            header = pack('>3sBBB4s', 'ID3', v2, 0, flags, framesize)
            data = header + framedata

            if (insize < outsize):
                insert_bytes(f, outsize - insize, insize + 10)
            f.seek(0)
            f.write(data)

            try:
                f.seek(-128, 2)
            except IOError as err:
                from errno import EINVAL
                if err.errno != EINVAL:
                    raise
                f.seek(0, 2)  # ensure read won't get "TAG"

            if f.read(3) == "TAG":
                f.seek(-128, 2)
                if v1 > 0:
                    f.write(MakeID3v1(self))
                else:
                    f.truncate()
            elif v1 == 2:
                f.seek(0, 2)
                f.write(MakeID3v1(self))

        finally:
            f.close()
Exemple #22
0
            idata = f.read(10)
            try:
                id3, vmaj, vrev, flags, insize = unpack('>3sBBB4s', idata)
            except struct.error:
                id3, insize = '', 0
            insize = BitPaddedInt(insize)
            if id3 != 'ID3':
                insize = -10

            if insize >= framesize:
                outsize = insize
            else:
                outsize = (framesize + 1023) & ~0x3FF
            framedata += '\x00' * (outsize - framesize)

            framesize = BitPaddedInt.to_str(outsize, width=4)
            flags = 0
            header = pack('>3sBBB4s', 'ID3', v2, 0, flags, framesize)
            data = header + framedata

            if (insize < outsize):
                insert_bytes(f, outsize-insize, insize+10)
            f.seek(0)
            f.write(data)

            try:
                f.seek(-128, 2)
            except IOError, err:
                from errno import EINVAL
                if err.errno != EINVAL:
                    raise
Exemple #23
0
 def test_s1(self):
     self.assertEquals(BitPaddedInt.to_str(1), "\x00\x00\x00\x01")
Exemple #24
0
    def save(self, filename=None, v1=0):
        """Save changes to a file.
        If no filename is given, the one most recently loaded is used.
        Keyword arguments:
        v1 -- if 0, ID3v1 tags will be removed
                    if 1, ID3v1 tags will be updated but not added
                    if 2, ID3v1 tags will be created and/or updated
        The lack of a way to update only an ID3v1 tag is intentional.
        """
        # Sort frames by 'importance'
        order = ["TIT2", "TPE1", "TRCK", "TALB", "TPOS", "TDRC", "TCON"]
        order = dict(zip(order, range(len(order))))
        last = len(order)
        frames = self.items()
        frames.sort(lambda a, b: cmp(order.get(a[0][:4], last), order.get(b[0][:4], last)))

        framedata = [self.__save_frame(frame) for (key, frame) in frames]
        framedata.extend([data for data in self.unknown_frames if len(data) > 10])

        framedata = "".join(framedata)
        framesize = len(framedata)

        if filename is None:
            filename = self.filename
        f = open(filename, "rb+")
        try:
            idata = f.read(10)
            try:
                id3, vmaj, vrev, flags, insize = struct.unpack(">3sBBB4s", idata)
            except struct.error:
                id3, insize = "", 0
            insize = BitPaddedInt(insize)
            if id3 != "ID3":
                insize = -10
            if insize >= framesize:
                outsize = insize
            else:
                outsize = (framesize + 1023) & ~0x3FF
            framedata += "\x00" * (outsize - framesize)

            framesize = BitPaddedInt.to_str(outsize, width=4)
            flags = 0
            header = struct.pack(">3sBBB4s", "ID3", 4, 0, flags, framesize)
            data = header + framedata

            if insize < outsize:
                insert_bytes(f, outsize - insize, insize + 10)
            f.seek(0)

            try:
                f.seek(-128, 2)
            except IOError, err:
                if err.errno != EINVAL:
                    raise
                f.seek(0, 2)  # ensure read won't get "TAG"

            if f.read(3) == "TAG":
                f.seek(-128, 2)
                if v1 > 0:
                    f.write(MakeID3v1(self))
                else:
                    f.truncate()
            elif v1 == 2:
                f.seek(0, 2)
                f.write(MakeID3v1(self))
Exemple #25
0
 def test_s0(self):
     self.assertEquals(BitPaddedInt.to_str(0), b'\x00\x00\x00\x00')
Exemple #26
0
 def test_s1(self):
     self.assertEquals(BitPaddedInt.to_bytes(1), b'\x00\x00\x00\x01')
Exemple #27
0
 def test_s1l(self):
     self.assertEquals(
         BitPaddedInt.to_str(1, bigendian=False), b'\x01\x00\x00\x00')
Exemple #28
0
 def test_1l(self):
     self.assertEquals(
         BitPaddedInt(b'\x01\x00\x00\x00', bigendian=False), 1)
Exemple #29
0
 def test_s65(self):
     self.assertEquals(BitPaddedInt.to_str(0x41, 6), b'\x00\x00\x01\x01')
Exemple #30
0
 def test_129b(self):
     self.assertEquals(BitPaddedInt(b'\x00\x00\x01\x81'), 0x81)
Exemple #31
0
 def test_w129l(self):
     self.assertEquals(
         BitPaddedInt.to_str(129, width=2, bigendian=False), b'\x01\x01')
Exemple #32
0
 def test_65(self):
     self.assertEquals(BitPaddedInt(b'\x00\x00\x01\x81', 6), 0x41)
Exemple #33
0
 def test_varwidth(self):
     self.assertEquals(len(BitPaddedInt.to_str(100)), 4)
     self.assertEquals(len(BitPaddedInt.to_str(100, width=-1)), 4)
     self.assertEquals(len(BitPaddedInt.to_str(2 ** 32, width=-1)), 5)
Exemple #34
0
 def test_32b(self):
     self.assertEquals(BitPaddedInt(b'\xFF\xFF\xFF\xFF', bits=8),
                       0xFFFFFFFF)
Exemple #35
0
 def test_promote_long(self):
     l = BitPaddedInt(sys.maxint ** 2)
     self.assertTrue(isinstance(l, long))
     self.assertEqual(BitPaddedInt(l.as_str(width=-1)), l)
Exemple #36
0
 def test_32bi(self):
     self.assertEquals(BitPaddedInt(0xFFFFFFFF, bits=8), 0xFFFFFFFF)
Exemple #37
0
 def test_1(self):
     self.assertEquals(BitPaddedInt(b'\x00\x00\x00\x01'), 1)
Exemple #38
0
    def test_has_valid_padding(self):
        self.failUnless(BitPaddedInt.has_valid_padding("\xff\xff", bits=8))
        self.failIf(BitPaddedInt.has_valid_padding("\xff"))
        self.failIf(BitPaddedInt.has_valid_padding("\x00\xff"))
        self.failUnless(BitPaddedInt.has_valid_padding("\x7f\x7f"))
        self.failIf(BitPaddedInt.has_valid_padding("\x7f", bits=6))
        self.failIf(BitPaddedInt.has_valid_padding("\x9f", bits=6))
        self.failUnless(BitPaddedInt.has_valid_padding("\x3f", bits=6))

        self.failUnless(BitPaddedInt.has_valid_padding(0xFF, bits=8))
        self.failIf(BitPaddedInt.has_valid_padding(0xFF))
        self.failIf(BitPaddedInt.has_valid_padding(0xFF << 8))
        self.failUnless(BitPaddedInt.has_valid_padding(0x7F << 8))
        self.failIf(BitPaddedInt.has_valid_padding(0x9F << 32, bits=6))
        self.failUnless(BitPaddedInt.has_valid_padding(0x3F << 16, bits=6))