Example #1
0
    def delete(self):
        """Removes the chunk from the file"""

        delete_bytes(self._fileobj, self.size, self.offset)
        if self.parent_chunk is not None:
            self.parent_chunk._remove_subchunk(self)
        self._fileobj.flush()
Example #2
0
def delete(filething, delete_v1=True, delete_v2=True):
    """Remove tags from a file.

    Args:
        delete_v1 (bool): delete any ID3v1 tag
        delete_v2 (bool): delete any ID3v2 tag

    Raises:
        mutagen.MutagenError: In case deleting failed
    """

    f = filething.fileobj

    if delete_v1:
        tag, offset = find_id3v1(f)
        if tag is not None:
            f.seek(offset, 2)
            f.truncate()

    # technically an insize=0 tag is invalid, but we delete it anyway
    # (primarily because we used to write it)
    if delete_v2:
        f.seek(0, 0)
        idata = f.read(10)
        try:
            id3, vmaj, vrev, flags, insize = struct.unpack('>3sBBB4s', idata)
        except struct.error:
            pass
        else:
            insize = BitPaddedInt(insize)
            if id3 == b'ID3' and insize >= 0:
                delete_bytes(f, insize + 10, 0)
Example #3
0
    def delete(self):
        """Removes the chunk from the file"""

        delete_bytes(self.__fileobj, self.size, self.offset)
        if self.parent_chunk is not None:
            self.parent_chunk._update_size(self.parent_chunk.data_size -
                                           self.size)
Example #4
0
    def delete(self):
        """Removes the chunk from the file"""

        delete_bytes(self.__fileobj, self.size, self.offset)
        if self.parent_chunk is not None:
            self.parent_chunk._update_size(
                self.parent_chunk.data_size - self.size)
Example #5
0
def delete(filething, delete_v1=True, delete_v2=True):
    """Remove tags from a file.

    Args:
        delete_v1 (bool): delete any ID3v1 tag
        delete_v2 (bool): delete any ID3v2 tag

    Raises:
        mutagen.MutagenError: In case deleting failed
    """

    f = filething.fileobj

    if delete_v1:
        tag, offset = find_id3v1(f)
        if tag is not None:
            f.seek(offset, 2)
            f.truncate()

    # technically an insize=0 tag is invalid, but we delete it anyway
    # (primarily because we used to write it)
    if delete_v2:
        f.seek(0, 0)
        idata = f.read(10)
        try:
            id3, vmaj, vrev, flags, insize = struct.unpack('>3sBBB4s', idata)
        except struct.error:
            pass
        else:
            insize = BitPaddedInt(insize)
            if id3 == b'ID3' and insize >= 0:
                delete_bytes(f, insize + 10, 0)
Example #6
0
def delete(filename, delete_v1=True, delete_v2=True):
    """Remove tags from a file.

    Keyword arguments:

    * delete_v1 -- delete any ID3v1 tag
    * delete_v2 -- delete any ID3v2 tag
    """

    with open(filename, 'rb+') as f:

        if delete_v1:
            tag, offset = _find_id3v1(f)
            if tag is not None:
                f.seek(offset, 2)
                f.truncate()

        # technically an insize=0 tag is invalid, but we delete it anyway
        # (primarily because we used to write it)
        if delete_v2:
            f.seek(0, 0)
            idata = f.read(10)
            try:
                id3, vmaj, vrev, flags, insize = unpack('>3sBBB4s', idata)
            except struct.error:
                id3, insize = b'', -1
            insize = BitPaddedInt(insize)
            if id3 == b'ID3' and insize >= 0:
                delete_bytes(f, insize + 10, 0)
Example #7
0
File: id3.py Project: stedy/mutagen
def delete(filename, delete_v1=True, delete_v2=True):
    """Remove tags from a file.

    Keyword arguments:

    * delete_v1 -- delete any ID3v1 tag
    * delete_v2 -- delete any ID3v2 tag
    """

    f = open(filename, 'rb+')

    if delete_v1:
        try:
            f.seek(-128, 2)
        except IOError:
            pass
        else:
            if f.read(3) == b'TAG':
                f.seek(-128, 2)
                f.truncate()

    # technically an insize=0 tag is invalid, but we delete it anyway
    # (primarily because we used to write it)
    if delete_v2:
        f.seek(0, 0)
        idata = f.read(10)
        try:
            id3, vmaj, vrev, flags, insize = unpack('>3sBBB4s', idata)
        except struct.error:
            id3, insize = b'', -1
        insize = BitPaddedInt(insize)
        if id3 == b'ID3' and insize >= 0:
            delete_bytes(f, insize + 10, 0)
Example #8
0
File: id3.py Project: stedy/mutagen
def delete(filename, delete_v1=True, delete_v2=True):
    """Remove tags from a file.

    Keyword arguments:

    * delete_v1 -- delete any ID3v1 tag
    * delete_v2 -- delete any ID3v2 tag
    """

    f = open(filename, "rb+")

    if delete_v1:
        try:
            f.seek(-128, 2)
        except IOError:
            pass
        else:
            if f.read(3) == b"TAG":
                f.seek(-128, 2)
                f.truncate()

    # technically an insize=0 tag is invalid, but we delete it anyway
    # (primarily because we used to write it)
    if delete_v2:
        f.seek(0, 0)
        idata = f.read(10)
        try:
            id3, vmaj, vrev, flags, insize = unpack(">3sBBB4s", idata)
        except struct.error:
            id3, insize = b"", -1
        insize = BitPaddedInt(insize)
        if id3 == b"ID3" and insize >= 0:
            delete_bytes(f, insize + 10, 0)
Example #9
0
    def save(self, filething=None):
        """Save changes to a file.

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

        Tags are always written at the end of the file, and include
        a header and a footer.
        """

        fileobj = filething.fileobj

        data = _APEv2Data(fileobj)

        if data.is_at_start:
            delete_bytes(fileobj, data.end - data.start, data.start)
        elif data.start is not None:
            fileobj.seek(data.start)
            # Delete an ID3v1 tag if present, too.
            fileobj.truncate()
        fileobj.seek(0, 2)

        tags = []
        for key, value in self.items():
            # Packed format for an item:
            # 4B: Value length
            # 4B: Value type
            # Key name
            # 1B: Null
            # Key value
            value_data = value._write()
            if not isinstance(key, bytes):
                key = key.encode("utf-8")
            tag_data = bytearray()
            tag_data += struct.pack("<2I", len(value_data), value.kind << 1)
            tag_data += key + b"\0" + value_data
            tags.append(bytes(tag_data))

        # "APE tags items should be sorted ascending by size... This is
        # not a MUST, but STRONGLY recommended. Actually the items should
        # be sorted by importance/byte, but this is not feasible."
        tags.sort(key=lambda tag: (len(tag), tag))
        num_tags = len(tags)
        tags = b"".join(tags)

        header = bytearray(b"APETAGEX")
        # version, tag size, item count, flags
        header += struct.pack("<4I", 2000,
                              len(tags) + 32, num_tags, HAS_HEADER | IS_HEADER)
        header += b"\0" * 8
        fileobj.write(header)

        fileobj.write(tags)

        footer = bytearray(b"APETAGEX")
        footer += struct.pack("<4I", 2000,
                              len(tags) + 32, num_tags, HAS_HEADER)
        footer += b"\0" * 8

        fileobj.write(footer)
Example #10
0
    def save(self):
        # Move attributes to the right objects
        self.to_extended_content_description = {}
        self.to_metadata = {}
        self.to_metadata_library = []
        for name, value in self.tags:
            if name in _standard_attribute_names:
                continue
            large_value = value.data_size() > 0xFFFF
            if (value.language is None and value.stream is None
                    and name not in self.to_extended_content_description
                    and not large_value):
                self.to_extended_content_description[name] = value
            elif (value.language is None and value.stream is not None
                  and name not in self.to_metadata and not large_value):
                self.to_metadata[name] = value
            else:
                self.to_metadata_library.append((name, value))

        # Add missing objects
        if not self.content_description_obj:
            self.content_description_obj = \
                ContentDescriptionObject()
            self.objects.append(self.content_description_obj)
        if not self.extended_content_description_obj:
            self.extended_content_description_obj = \
                ExtendedContentDescriptionObject()
            self.objects.append(self.extended_content_description_obj)
        if not self.header_extension_obj:
            self.header_extension_obj = \
                HeaderExtensionObject()
            self.objects.append(self.header_extension_obj)
        if not self.metadata_obj:
            self.metadata_obj = \
                MetadataObject()
            self.header_extension_obj.objects.append(self.metadata_obj)
        if not self.metadata_library_obj:
            self.metadata_library_obj = \
                MetadataLibraryObject()
            self.header_extension_obj.objects.append(self.metadata_library_obj)

        # Render the header
        data = "".join([obj.render(self) for obj in self.objects])
        data = (HeaderObject.GUID +
                struct.pack("<QL",
                            len(data) + 30, len(self.objects)) + "\x01\x02" +
                data)

        fileobj = file(self.filename, "rb+")
        try:
            size = len(data)
            if size > self.size:
                insert_bytes(fileobj, size - self.size, self.size)
            if size < self.size:
                delete_bytes(fileobj, self.size - size, 0)
            fileobj.seek(0)
            fileobj.write(data)
        finally:
            fileobj.close()
Example #11
0
    def save(self, filething):
        """Save changes to a file.

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

        Tags are always written at the end of the file, and include
        a header and a footer.
        """

        fileobj = filething.fileobj

        data = _APEv2Data(fileobj)

        if data.is_at_start:
            delete_bytes(fileobj, data.end - data.start, data.start)
        elif data.start is not None:
            fileobj.seek(data.start)
            # Delete an ID3v1 tag if present, too.
            fileobj.truncate()
        fileobj.seek(0, 2)

        tags = []
        for key, value in self.items():
            # Packed format for an item:
            # 4B: Value length
            # 4B: Value type
            # Key name
            # 1B: Null
            # Key value
            value_data = value._write()
            if not isinstance(key, bytes):
                key = key.encode("utf-8")
            tag_data = bytearray()
            tag_data += struct.pack("<2I", len(value_data), value.kind << 1)
            tag_data += key + b"\0" + value_data
            tags.append(bytes(tag_data))

        # "APE tags items should be sorted ascending by size... This is
        # not a MUST, but STRONGLY recommended. Actually the items should
        # be sorted by importance/byte, but this is not feasible."
        tags.sort(key=len)
        num_tags = len(tags)
        tags = b"".join(tags)

        header = bytearray(b"APETAGEX")
        # version, tag size, item count, flags
        header += struct.pack("<4I", 2000, len(tags) + 32, num_tags,
                              HAS_HEADER | IS_HEADER)
        header += b"\0" * 8
        fileobj.write(header)

        fileobj.write(tags)

        footer = bytearray(b"APETAGEX")
        footer += struct.pack("<4I", 2000, len(tags) + 32, num_tags,
                              HAS_HEADER)
        footer += b"\0" * 8

        fileobj.write(footer)
Example #12
0
 def test_delete_6106_79_51760(self):
     # This appears to be due to ANSI C limitations in read/write on rb+
     # files. The problematic behavior only showed up in our mmap fallback
     # code for transfers of this or similar sizes.
     data = ''.join(map(str, range(12574)))  # 51760 bytes
     o = self.file(data[:6106 + 79] + data[79:])
     delete_bytes(o, 6106, 79)
     self.failUnless(data == self.read(o))
Example #13
0
    def delete(self, filething):
        """Remove tags from a file."""

        fileobj = filething.fileobj
        data = _APEv2Data(fileobj)
        if data.start is not None and data.size is not None:
            delete_bytes(fileobj, data.end - data.start, data.start)
        self.clear()
Example #14
0
 def test_delete_6106_79_51760(self):
     # This appears to be due to ANSI C limitations in read/write on rb+
     # files.
     data = u''.join(map(str, range(12574)))  # 51760 bytes
     data = data.encode("ascii")
     with self.file(data[:6106 + 79] + data[79:]) as o:
         delete_bytes(o, 6106, 79)
         self.failUnless(data == self.read(o))
Example #15
0
 def test_delete_6106_79_51760(self):
     # This appears to be due to ANSI C limitations in read/write on rb+
     # files. The problematic behavior only showed up in our mmap fallback
     # code for transfers of this or similar sizes.
     data = b''.join(str(x).encode('ascii') for x in range(12574)) # 51760 bytes
     o = self.file(data[:6106+79] + data[79:])
     delete_bytes(o, 6106, 79)
     self.failUnless(data == self.read(o))
Example #16
0
    def delete(self, filething=None):
        """Remove tags from a file."""

        fileobj = filething.fileobj
        data = _APEv2Data(fileobj)
        if data.start is not None and data.size is not None:
            delete_bytes(fileobj, data.end - data.start, data.start)
        self.clear()
Example #17
0
    def save(self):
        # Move attributes to the right objects
        self.to_extended_content_description = {}
        self.to_metadata = {}
        self.to_metadata_library = []
        for name, value in self.tags:
            if name in _standard_attribute_names:
                continue
            large_value = value.data_size() > 0xFFFF
            if (value.language is None and value.stream is None and
                name not in self.to_extended_content_description and
                not large_value):
                self.to_extended_content_description[name] = value
            elif (value.language is None and value.stream is not None and
                  name not in self.to_metadata and not large_value):
                self.to_metadata[name] = value
            else:
                self.to_metadata_library.append((name, value))

        # Add missing objects
        if not self.content_description_obj:
            self.content_description_obj = \
                ContentDescriptionObject()
            self.objects.append(self.content_description_obj)
        if not self.extended_content_description_obj:
            self.extended_content_description_obj = \
                ExtendedContentDescriptionObject()
            self.objects.append(self.extended_content_description_obj)
        if not self.header_extension_obj:
            self.header_extension_obj = \
                HeaderExtensionObject()
            self.objects.append(self.header_extension_obj)
        if not self.metadata_obj:
            self.metadata_obj = \
                MetadataObject()
            self.header_extension_obj.objects.append(self.metadata_obj)
        if not self.metadata_library_obj:
            self.metadata_library_obj = \
                MetadataLibraryObject()
            self.header_extension_obj.objects.append(self.metadata_library_obj)

        # Render the header
        data = "".join([obj.render(self) for obj in self.objects])
        data = (HeaderObject.GUID +
                struct.pack("<QL", len(data) + 30, len(self.objects)) +
                "\x01\x02" + data)

        fileobj = open(self.filename, "rb+")
        try:
            size = len(data)
            if size > self.size:
                insert_bytes(fileobj, size - self.size, self.size)
            if size < self.size:
                delete_bytes(fileobj, self.size - size, 0)
            fileobj.seek(0)
            fileobj.write(data)
        finally:
            fileobj.close()
Example #18
0
 def test_delete_6106_79_51760(self):
     # This appears to be due to ANSI C limitations in read/write on rb+
     # files. The problematic behavior only showed up in our mmap fallback
     # code for transfers of this or similar sizes.
     data = u''.join(map(text_type, xrange(12574)))  # 51760 bytes
     data = data.encode("ascii")
     with self.file(data[:6106 + 79] + data[79:]) as o:
         delete_bytes(o, 6106, 79)
         self.failUnless(data == self.read(o))
Example #19
0
    def delete(self, filename=None):
        """Remove tags from a file."""

        filename = filename or self.filename
        with open(filename, "r+b") as fileobj:
            data = _APEv2Data(fileobj)
            if data.start is not None and data.size is not None:
                delete_bytes(fileobj, data.end - data.start, data.start)

        self.clear()
Example #20
0
    def delete(self, filename=None):
        """Remove tags from a file."""

        filename = filename or self.filename
        with open(filename, "r+b") as fileobj:
            data = _APEv2Data(fileobj)
            if data.start is not None and data.size is not None:
                delete_bytes(fileobj, data.end - data.start, data.start)

        self.clear()
Example #21
0
    def replace(klass, fileobj, old_pages, new_pages):
        """Replace old_pages with new_pages within fileobj.

        old_pages must have come from reading fileobj originally.
        new_pages are assumed to have the 'same' data as old_pages,
        and so the serial and sequence numbers will be copied, as will
        the flags for the first and last pages.

        fileobj will be resized and pages renumbered as necessary. As
        such, it must be opened r+b or w+b.
        """

        # Number the new pages starting from the first old page.
        first = old_pages[0].sequence
        for page, seq in zip(new_pages,
                             list(range(first, first + len(new_pages)))):
            page.sequence = seq
            page.serial = old_pages[0].serial

        new_pages[0].first = old_pages[0].first
        new_pages[0].last = old_pages[0].last
        new_pages[0].continued = old_pages[0].continued

        new_pages[-1].first = old_pages[-1].first
        new_pages[-1].last = old_pages[-1].last
        new_pages[-1].complete = old_pages[-1].complete
        if not new_pages[-1].complete and len(new_pages[-1].packets) == 1:
            new_pages[-1].position = -1

        new_data = bytearray().join(map(klass.write, new_pages))

        # Make room in the file for the new data.
        delta = len(new_data)
        fileobj.seek(old_pages[0].offset, 0)
        insert_bytes(fileobj, delta, old_pages[0].offset)
        fileobj.seek(old_pages[0].offset, 0)
        fileobj.write(new_data)
        new_data_end = old_pages[0].offset + delta

        # Go through the old pages and delete them. Since we shifted
        # the data down the file, we need to adjust their offsets. We
        # also need to go backwards, so we don't adjust the deltas of
        # the other pages.
        old_pages.reverse()
        for old_page in old_pages:
            adj_offset = old_page.offset + delta
            delete_bytes(fileobj, old_page.size, adj_offset)

        # Finally, if there's any discrepency in length, we need to
        # renumber the pages for the logical stream.
        if len(old_pages) != len(new_pages):
            fileobj.seek(new_data_end, 0)
            serial = new_pages[-1].serial
            sequence = new_pages[-1].sequence + 1
            klass.renumber(fileobj, serial, sequence)
Example #22
0
    def save(self,
             filething=None,
             v1=1,
             v2_version=4,
             v23_sep='/',
             padding=None):
        """save(filething=None, v1=1, v2_version=4, v23_sep='/', padding=None)

        Save changes to a file.

        Args:
            filething (filething):
                Filename to save the tag to. If no filename is given,
                the one most recently loaded is used.
            v1 (ID3v1SaveOptions):
                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 (int):
                version of ID3v2 tags (3 or 4).
            v23_sep (text):
                the separator used to join multiple text values
                if v2_version == 3. Defaults to '/' but if it's None
                will be the ID3v2v2.4 null separator.
            padding (:obj:`mutagen.PaddingFunction`)

        Raises:
            mutagen.MutagenError

        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.
        """

        f = filething.fileobj

        try:
            header = ID3Header(filething.fileobj)
        except ID3NoHeaderError:
            old_size = 0
        else:
            old_size = header.size

        data = self._prepare_data(f, 0, old_size, v2_version, v23_sep, padding)
        new_size = len(data)

        if (old_size < new_size):
            insert_bytes(f, new_size - old_size, old_size)
        elif (old_size > new_size):
            delete_bytes(f, old_size - new_size, new_size)
        f.seek(0)
        f.write(data)

        self.__save_v1(f, v1)
Example #23
0
File: ogg.py Project: azoon/pymt
    def replace(klass, fileobj, old_pages, new_pages):
        """Replace old_pages with new_pages within fileobj.

        old_pages must have come from reading fileobj originally.
        new_pages are assumed to have the 'same' data as old_pages,
        and so the serial and sequence numbers will be copied, as will
        the flags for the first and last pages.

        fileobj will be resized and pages renumbered as necessary. As
        such, it must be opened r+b or w+b.
        """

        # Number the new pages starting from the first old page.
        first = old_pages[0].sequence
        for page, seq in zip(new_pages, range(first, first + len(new_pages))):
            page.sequence = seq
            page.serial = old_pages[0].serial

        new_pages[0].first = old_pages[0].first
        new_pages[0].last = old_pages[0].last
        new_pages[0].continued = old_pages[0].continued

        new_pages[-1].first = old_pages[-1].first
        new_pages[-1].last = old_pages[-1].last
        new_pages[-1].complete = old_pages[-1].complete
        if not new_pages[-1].complete and len(new_pages[-1].packets) == 1:
            new_pages[-1].position = -1L

        new_data = "".join(map(klass.write, new_pages))

        # Make room in the file for the new data.
        delta = len(new_data)
        fileobj.seek(old_pages[0].offset, 0)
        insert_bytes(fileobj, delta, old_pages[0].offset)
        fileobj.seek(old_pages[0].offset, 0)
        fileobj.write(new_data)
        new_data_end = old_pages[0].offset + delta

        # Go through the old pages and delete them. Since we shifted
        # the data down the file, we need to adjust their offsets. We
        # also need to go backwards, so we don't adjust the deltas of
        # the other pages.
        old_pages.reverse()
        for old_page in old_pages:
            adj_offset = old_page.offset + delta
            delete_bytes(fileobj, old_page.size, adj_offset)

        # Finally, if there's any discrepency in length, we need to
        # renumber the pages for the logical stream.
        if len(old_pages) != len(new_pages):
            fileobj.seek(new_data_end, 0)
            serial = new_pages[-1].serial
            sequence = new_pages[-1].sequence + 1
            klass.renumber(fileobj, serial, sequence)
Example #24
0
 def delete(self, filename=None):
     """Remove tags from a file."""
     filename = filename or self.filename
     fileobj = open(filename, "r+b")
     try:
         data = _APEv2Data(fileobj)
         if data.start is not None and data.size is not None:
             delete_bytes(fileobj, data.end - data.start, data.start)
     finally:
         fileobj.close()
     self.clear()
Example #25
0
 def delete(self, filename=None):
     """Remove tags from a file."""
     filename = filename or self.filename
     fileobj = file(filename, "r+b")
     try:
         data = _APEv2Data(fileobj)
         if data.start is not None and data.size is not None:
             delete_bytes(fileobj, data.end - data.start, data.start)
     finally:
         fileobj.close()
     self.clear()
Example #26
0
 def __save_existing(self, fileobj, atoms, path, data, offset):
     # Replace the old ilst atom.
     ilst = path.pop()
     delta = len(data) - ilst.length
     fileobj.seek(ilst.offset + offset)
     if delta > 0:
         insert_bytes(fileobj, delta, ilst.offset + offset)
     elif delta < 0:
         delete_bytes(fileobj, -delta, ilst.offset + offset)
     fileobj.seek(ilst.offset + offset)
     fileobj.write(data)
     self.__update_parents(fileobj, path, delta, offset)
Example #27
0
 def __save_existing(self, fileobj, atoms, path, data, offset):
     # Replace the old ilst atom.
     ilst = path.pop()
     delta = len(data) - ilst.length
     fileobj.seek(ilst.offset + offset)
     if delta > 0:
         insert_bytes(fileobj, delta, ilst.offset + offset)
     elif delta < 0:
         delete_bytes(fileobj, -delta, ilst.offset + offset)
     fileobj.seek(ilst.offset + offset)
     fileobj.write(data)
     self.__update_parents(fileobj, path, delta, offset)
Example #28
0
    def save(self, filething=None, v1=1, v2_version=4, v23_sep='/',
             padding=None):
        """save(filething=None, v1=1, v2_version=4, v23_sep='/', padding=None)

        Save changes to a file.

        Args:
            filething (filething):
                Filename to save the tag to. If no filename is given,
                the one most recently loaded is used.
            v1 (ID3v1SaveOptions):
                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 (int):
                version of ID3v2 tags (3 or 4).
            v23_sep (text):
                the separator used to join multiple text values
                if v2_version == 3. Defaults to '/' but if it's None
                will be the ID3v2v2.4 null separator.
            padding (:obj:`mutagen.PaddingFunction`)

        Raises:
            mutagen.MutagenError

        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.
        """

        f = filething.fileobj

        try:
            header = ID3Header(filething.fileobj)
        except ID3NoHeaderError:
            old_size = 0
        else:
            old_size = header.size

        data = self._prepare_data(
            f, 0, old_size, v2_version, v23_sep, padding)
        new_size = len(data)

        if (old_size < new_size):
            insert_bytes(f, new_size - old_size, old_size)
        elif (old_size > new_size):
            delete_bytes(f, old_size - new_size, new_size)
        f.seek(0)
        f.write(data)

        self.__save_v1(f, v1)
Example #29
0
    def save(self, filename=None):
        """Save changes to a file.

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

        Tags are always written at the end of the file, and include
        a header and a footer.
        """

        filename = filename or self.filename
        try:
            fileobj = open(filename, "r+b")
        except IOError:
            fileobj = open(filename, "w+b")
        data = _APEv2Data(fileobj)

        if data.is_at_start:
            delete_bytes(fileobj, data.end - data.start, data.start)
        elif data.start is not None:
            fileobj.seek(data.start)
            # Delete an ID3v1 tag if present, too.
            fileobj.truncate()
        fileobj.seek(0, 2)

        # "APE tags items should be sorted ascending by size... This is
        # not a MUST, but STRONGLY recommended. Actually the items should
        # be sorted by importance/byte, but this is not feasible."
        tags = [v._internal(k) for k, v in self.items()]
        tags.sort(lambda a, b: cmp(len(a), len(b)))
        num_tags = len(tags)
        tags = "".join(tags)

        header = "APETAGEX%s%s" % (
            # version, tag size, item count, flags
            struct.pack("<4I", 2000, len(tags) + 32, num_tags, HAS_HEADER | IS_HEADER),
            "\0" * 8,
        )
        fileobj.write(header)

        fileobj.write(tags)

        footer = "APETAGEX%s%s" % (
            # version, tag size, item count, flags
            struct.pack("<4I", 2000, len(tags) + 32, num_tags, HAS_HEADER),
            "\0" * 8,
        )
        fileobj.write(footer)
        fileobj.close()
Example #30
0
    def save(self, filename=None):
        """Save changes to a file.

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

        Tags are always written at the end of the file, and include
        a header and a footer.
        """

        filename = filename or self.filename
        try:
            fileobj = file(filename, "r+b")
        except IOError:
            fileobj = file(filename, "w+b")
        data = _APEv2Data(fileobj)

        if data.is_at_start:
            delete_bytes(fileobj, data.end - data.start, data.start)
        elif data.start is not None:
            fileobj.seek(data.start)
            # Delete an ID3v1 tag if present, too.
            fileobj.truncate()
        fileobj.seek(0, 2)

        # "APE tags items should be sorted ascending by size... This is
        # not a MUST, but STRONGLY recommended. Actually the items should
        # be sorted by importance/byte, but this is not feasible."
        tags = [v._internal(k) for k, v in self.items()]
        tags.sort(lambda a, b: cmp(len(a), len(b)))
        num_tags = len(tags)
        tags = "".join(tags)

        header = "APETAGEX%s%s" % (
            # version, tag size, item count, flags
            struct.pack("<4I", 2000,
                        len(tags) + 32, num_tags, HAS_HEADER | IS_HEADER),
            "\0" * 8)
        fileobj.write(header)

        fileobj.write(tags)

        footer = "APETAGEX%s%s" % (
            # version, tag size, item count, flags
            struct.pack("<4I", 2000,
                        len(tags) + 32, num_tags, HAS_HEADER),
            "\0" * 8)
        fileobj.write(footer)
        fileobj.close()
Example #31
0
    def test_many_changes(self,
                          num_runs=5,
                          num_changes=300,
                          min_change_size=500,
                          max_change_size=1000,
                          min_buffer_size=1,
                          max_buffer_size=2000):
        self.failUnless(
            min_buffer_size < min_change_size
            and max_buffer_size > max_change_size
            and min_change_size < max_change_size
            and min_buffer_size < max_buffer_size,
            "Given testing parameters make this test useless")
        for j in range(num_runs):
            data = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ" * 1024
            with self.file(data) as fobj:
                filesize = len(data)
                # Generate the list of changes to apply
                changes = []
                for i in range(num_changes):
                    change_size = random.randrange(min_change_size,
                                                   max_change_size)
                    change_offset = random.randrange(0, filesize)
                    filesize += change_size
                    changes.append((change_offset, change_size))

                # Apply the changes, and make sure they all took.
                for offset, size in changes:
                    buffer_size = random.randrange(min_buffer_size,
                                                   max_buffer_size)
                    insert_bytes(fobj, size, offset, BUFFER_SIZE=buffer_size)
                fobj.seek(0)
                self.failIfEqual(fobj.read(len(data)), data)
                fobj.seek(0, 2)
                self.failUnlessEqual(fobj.tell(), filesize)

                # Then, undo them.
                changes.reverse()
                for offset, size in changes:
                    buffer_size = random.randrange(min_buffer_size,
                                                   max_buffer_size)
                    delete_bytes(fobj, size, offset, BUFFER_SIZE=buffer_size)
                fobj.seek(0)
                self.failUnless(fobj.read() == data)
Example #32
0
    def save(self, filename=None):
        """Save changes to a file.

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

        Tags are always written at the end of the file, and include
        a header and a footer.
        """

        filename = filename or self.filename
        try:
            fileobj = open(filename, "r+b")
        except IOError:
            fileobj = open(filename, "w+b")
        data = _APEv2Data(fileobj)

        if data.is_at_start:
            delete_bytes(fileobj, data.end - data.start, data.start)
        elif data.start is not None:
            fileobj.seek(data.start)
            # Delete an ID3v1 tag if present, too.
            fileobj.truncate()
        fileobj.seek(0, 2)

        # "APE tags items should be sorted ascending by size... This is
        # not a MUST, but STRONGLY recommended. Actually the items should
        # be sorted by importance/byte, but this is not feasible."
        tags = sorted((v._internal(k) for k, v in list(self.items())), key=len)
        num_tags = len(tags)
        tags = bytearray().join(tags)

        # tag string, version, tag size, item count, flags
        header = struct_pack("<8s 4I 8x", b"APETAGEX", 2000,
                             len(tags) + 32, num_tags, HAS_HEADER | IS_HEADER)
        fileobj.write(header)

        fileobj.write(tags)

        # tag string, version, tag size, item count, flags
        footer = struct_pack("<8s 4I 8x", b"APETAGEX", 2000,
                             len(tags) + 32, num_tags, HAS_HEADER)
        fileobj.write(footer)
        fileobj.close()
Example #33
0
    def test_many_changes(self, num_runs=5, num_changes=300,
                          min_change_size=500, max_change_size=1000,
                          min_buffer_size=1, max_buffer_size=2000):
        self.failUnless(min_buffer_size < min_change_size and
                        max_buffer_size > max_change_size and
                        min_change_size < max_change_size and
                        min_buffer_size < max_buffer_size,
                        "Given testing parameters make this test useless")
        for j in xrange(num_runs):
            data = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ" * 1024
            fobj = self.file(data)
            filesize = len(data)
            # Generate the list of changes to apply
            changes = []
            for i in xrange(num_changes):
                change_size = random.randrange(
                    min_change_size, max_change_size)
                change_offset = random.randrange(0, filesize)
                filesize += change_size
                changes.append((change_offset, change_size))

            # Apply the changes, and make sure they all took.
            for offset, size in changes:
                buffer_size = random.randrange(
                    min_buffer_size, max_buffer_size)
                insert_bytes(fobj, size, offset, BUFFER_SIZE=buffer_size)
            fobj.seek(0)
            self.failIfEqual(fobj.read(len(data)), data)
            fobj.seek(0, 2)
            self.failUnlessEqual(fobj.tell(), filesize)

            # Then, undo them.
            changes.reverse()
            for offset, size in changes:
                buffer_size = random.randrange(
                    min_buffer_size, max_buffer_size)
                delete_bytes(fobj, size, offset, BUFFER_SIZE=buffer_size)
            fobj.seek(0)
            self.failUnless(fobj.read() == data)
Example #34
0
    def test_delete_bytes(self):
        from mutagen._util import delete_bytes

        expected_content = 'foo\n'

        self.mount()
        try:
            self._init_mmap()
            try:
                delete_bytes(self.file_obj, 4, 4)
                self.file_obj.seek(0)
                content = self.file_obj.read()
                self.assertEqual(content, expected_content)
                self.file_obj.close()

                self.file_obj = self._open_file()
                content = self.file_obj.read()
                self.assertEqual(content, expected_content)
            finally:
                self._de_init_mmap()
        finally:
            self.umount()
        self.clean_up()
Example #35
0
    def test_delete_bytes(self):
        from mutagen._util import delete_bytes

        expected_content = 'foo\n'

        self.mount()
        try:
            self._init_mmap()
            try:
                delete_bytes(self.file_obj, 4, 4)
                self.file_obj.seek(0)
                content = self.file_obj.read()
                self.assertEqual(content, expected_content)
                self.file_obj.close()

                self.file_obj = self._open_file()
                content = self.file_obj.read()
                self.assertEqual(content, expected_content)
            finally:
                self._de_init_mmap()
        finally:
            self.umount()
        self.clean_up()
Example #36
0
 def test_delete_one(self):
     o = self.file(b'a')
     delete_bytes(o, 1, 0)
     self.assertEquals(b'', self.read(o))
Example #37
0
 def test_delete_second_of_two(self):
     o = self.file(b'ab')
     delete_bytes(o, 1, 1)
     self.assertEquals(b'a', self.read(o))
Example #38
0
 def test_delete_middle(self):
     o = self.file(b'abcdefg')
     delete_bytes(o, 3, 2)
     self.assertEquals(b'abfg', self.read(o))
Example #39
0
 def test_delete_middle(self):
     with self.file(b'abcdefg') as o:
         delete_bytes(o, 3, 2)
         self.assertEqual(b'abfg', self.read(o))
Example #40
0
 def test_delete_middle(self):
     o = self.file(b'abcdefg')
     delete_bytes(o, 3, 2)
     self.assertEquals(b'abfg', self.read(o))
Example #41
0
 def test_delete_one(self):
     o = self.file(b'a')
     delete_bytes(o, 1, 0)
     self.assertEquals(b'', self.read(o))
Example #42
0
 def test_delete_second_of_two(self):
     with self.file(b'ab') as o:
         delete_bytes(o, 1, 1)
         self.assertEqual(b'a', self.read(o))
Example #43
0
 def test_delete_first_of_two(self):
     with self.file(b'ab') as o:
         delete_bytes(o, 1, 0)
         self.assertEqual(b'b', self.read(o))
Example #44
0
 def test_delete_middle(self):
     with self.file(b'abcdefg') as o:
         delete_bytes(o, 3, 2)
         self.assertEqual(b'abfg', self.read(o))
Example #45
0
 def test_delete_first_of_two(self):
     with self.file(b'ab') as o:
         delete_bytes(o, 1, 0)
         self.assertEqual(b'b', self.read(o))
Example #46
0
 def test_delete_zero(self):
     with self.file(b'abcdefg') as o:
         delete_bytes(o, 0, 3)
         self.assertEqual(b'abcdefg', self.read(o))
Example #47
0
    def save(self, filename=None, v1=1, v2_version=4, v23_sep='/',
             padding=None):
        """Save changes to a file.

        Args:
            filename:
                Filename to save the tag to. If no filename is given,
                the one most recently loaded is used.
            v1 (ID3v1SaveOptions):
                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 (int):
                version of ID3v2 tags (3 or 4).
            v23_sep (str):
                the separator used to join multiple text values
                if v2_version == 3. Defaults to '/' but if it's None
                will be the ID3v2v2.4 null separator.
            padding (function):
                A function taking a PaddingInfo which should
                return the amount of padding to use. If None (default)
                will default to something reasonable.

        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.

        Can raise id3.error.
        """

        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:
            try:
                header = ID3Header(f)
            except ID3NoHeaderError:
                old_size = 0
            else:
                old_size = header.size

            data = self._prepare_data(
                f, 0, old_size, v2_version, v23_sep, padding)
            new_size = len(data)

            if (old_size < new_size):
                insert_bytes(f, new_size - old_size, old_size)
            elif (old_size > new_size):
                delete_bytes(f, old_size - new_size, new_size)
            f.seek(0)
            f.write(data)

            self.__save_v1(f, v1)

        finally:
            f.close()
Example #48
0
 def test_delete_second_of_two(self):
     with self.file(b'ab') as o:
         delete_bytes(o, 1, 1)
         self.assertEqual(b'a', self.read(o))
Example #49
0
 def test_delete_first_of_two(self):
     o = self.file(b'ab')
     delete_bytes(o, 1, 0)
     self.assertEquals(b'b', self.read(o))
Example #50
0
 def test_delete_one(self):
     with self.file(b'a') as o:
         delete_bytes(o, 1, 0)
         self.assertEqual(b'', self.read(o))
Example #51
0
 def test_delete_second_of_two(self):
     o = self.file(b'ab')
     delete_bytes(o, 1, 1)
     self.assertEquals(b'a', self.read(o))
Example #52
0
 def test_delete_one(self):
     with self.file(b'a') as o:
         delete_bytes(o, 1, 0)
         self.assertEqual(b'', self.read(o))
Example #53
0
 def test_delete_first_of_two(self):
     o = self.file(b'ab')
     delete_bytes(o, 1, 0)
     self.assertEquals(b'b', self.read(o))
Example #54
0
    def save(self):
        # Move attributes to the right objects
        self.to_content_description = {}
        self.to_extended_content_description = {}
        self.to_metadata = {}
        self.to_metadata_library = []
        for name, value in self.tags:
            library_only = (value.data_size() > 0xFFFF or value.TYPE == GUID)
            can_cont_desc = value.TYPE == UNICODE

            if library_only or value.language is not None:
                self.to_metadata_library.append((name, value))
            elif value.stream is not None:
                if name not in self.to_metadata:
                    self.to_metadata[name] = value
                else:
                    self.to_metadata_library.append((name, value))
            elif name in ContentDescriptionObject.NAMES:
                if name not in self.to_content_description and can_cont_desc:
                    self.to_content_description[name] = value
                else:
                    self.to_metadata_library.append((name, value))
            else:
                if name not in self.to_extended_content_description:
                    self.to_extended_content_description[name] = value
                else:
                    self.to_metadata_library.append((name, value))

        # Add missing objects
        if not self.content_description_obj:
            self.content_description_obj = \
                ContentDescriptionObject()
            self.objects.append(self.content_description_obj)
        if not self.extended_content_description_obj:
            self.extended_content_description_obj = \
                ExtendedContentDescriptionObject()
            self.objects.append(self.extended_content_description_obj)
        if not self.header_extension_obj:
            self.header_extension_obj = \
                HeaderExtensionObject()
            self.objects.append(self.header_extension_obj)
        if not self.metadata_obj:
            self.metadata_obj = \
                MetadataObject()
            self.header_extension_obj.objects.append(self.metadata_obj)
        if not self.metadata_library_obj:
            self.metadata_library_obj = \
                MetadataLibraryObject()
            self.header_extension_obj.objects.append(self.metadata_library_obj)

        # Render the header
        data = b"".join([obj.render(self) for obj in self.objects])
        data = (HeaderObject.GUID +
                struct.pack("<QL",
                            len(data) + 30, len(self.objects)) + b"\x01\x02" +
                data)

        with open(self.filename, "rb+") as fileobj:
            size = len(data)
            if size > self.size:
                insert_bytes(fileobj, size - self.size, self.size)
            if size < self.size:
                delete_bytes(fileobj, self.size - size, 0)
            fileobj.seek(0)
            fileobj.write(data)

        self.size = size
        self.num_objects = len(self.objects)
Example #55
0
 def test_delete_zero(self):
     with self.file(b'abcdefg') as o:
         delete_bytes(o, 0, 3)
         self.assertEqual(b'abcdefg', self.read(o))
Example #56
0
    def save(self):
        # Move attributes to the right objects
        self.to_content_description = {}
        self.to_extended_content_description = {}
        self.to_metadata = {}
        self.to_metadata_library = []
        for name, value in self.tags:
            library_only = (value.data_size() > 0xFFFF or value.TYPE == GUID)
            can_cont_desc = value.TYPE == UNICODE

            if library_only or value.language is not None:
                self.to_metadata_library.append((name, value))
            elif value.stream is not None:
                if name not in self.to_metadata:
                    self.to_metadata[name] = value
                else:
                    self.to_metadata_library.append((name, value))
            elif name in ContentDescriptionObject.NAMES:
                if name not in self.to_content_description and can_cont_desc:
                    self.to_content_description[name] = value
                else:
                    self.to_metadata_library.append((name, value))
            else:
                if name not in self.to_extended_content_description:
                    self.to_extended_content_description[name] = value
                else:
                    self.to_metadata_library.append((name, value))

        # Add missing objects
        if not self.content_description_obj:
            self.content_description_obj = \
                ContentDescriptionObject()
            self.objects.append(self.content_description_obj)
        if not self.extended_content_description_obj:
            self.extended_content_description_obj = \
                ExtendedContentDescriptionObject()
            self.objects.append(self.extended_content_description_obj)
        if not self.header_extension_obj:
            self.header_extension_obj = \
                HeaderExtensionObject()
            self.objects.append(self.header_extension_obj)
        if not self.metadata_obj:
            self.metadata_obj = \
                MetadataObject()
            self.header_extension_obj.objects.append(self.metadata_obj)
        if not self.metadata_library_obj:
            self.metadata_library_obj = \
                MetadataLibraryObject()
            self.header_extension_obj.objects.append(self.metadata_library_obj)

        # Render the header
        data = b"".join([obj.render(self) for obj in self.objects])
        data = (HeaderObject.GUID +
                struct.pack("<QL", len(data) + 30, len(self.objects)) +
                b"\x01\x02" + data)

        with open(self.filename, "rb+") as fileobj:
            size = len(data)
            if size > self.size:
                insert_bytes(fileobj, size - self.size, self.size)
            if size < self.size:
                delete_bytes(fileobj, self.size - size, 0)
            fileobj.seek(0)
            fileobj.write(data)

        self.size = size
        self.num_objects = len(self.objects)