Example #1
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 #2
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 #3
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 #4
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 #5
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, 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 #6
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, 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 #7
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 #8
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 #9
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 #10
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()