def _inject(self, fileobj, padding_func): """Write tag data into the Vorbis comment packet/page.""" # Find the old pages in the file; we'll need to remove them, # plus grab any stray setup packet data out of them. fileobj.seek(0) page = OggPage(fileobj) while not page.packets[0].startswith(b"\x03vorbis"): page = OggPage(fileobj) old_pages = [page] while not (old_pages[-1].complete or len(old_pages[-1].packets) > 1): page = OggPage(fileobj) if page.serial == old_pages[0].serial: old_pages.append(page) packets = OggPage.to_packets(old_pages, strict=False) content_size = get_size(fileobj) - len(packets[0]) # approx vcomment_data = b"\x03vorbis" + self.write() padding_left = len(packets[0]) - len(vcomment_data) info = PaddingInfo(padding_left, content_size) new_padding = info._get_padding(padding_func) # Set the new comment packet. packets[0] = vcomment_data + b"\x00" * new_padding new_pages = OggPage._from_packets_try_preserve(packets, old_pages) OggPage.replace(fileobj, old_pages, new_pages)
def render_full(self, asf, fileobj, available, padding_func): # Render everything except padding num_objects = 0 data = bytearray() for obj in self.objects: if obj.GUID == PaddingObject.GUID: continue data += obj.render(asf) num_objects += 1 # calculate how much space we need at least padding_obj = PaddingObject() header_size = len(HeaderObject.GUID) + 14 padding_overhead = len(padding_obj.render(asf)) needed_size = len(data) + header_size + padding_overhead # ask the user for padding adjustments file_size = get_size(fileobj) content_size = file_size - available if content_size < 0: raise ASFHeaderError("truncated content") info = PaddingInfo(available - needed_size, content_size) # add padding padding = info._get_padding(padding_func) padding_obj.parse(asf, b"\x00" * padding) data += padding_obj.render(asf) num_objects += 1 data = (HeaderObject.GUID + struct.pack("<QL", len(data) + 30, num_objects) + b"\x01\x02" + data) return data
def _inject(self, fileobj, padding_func): """Write tag data into the Theora comment packet/page.""" fileobj.seek(0) page = OggPage(fileobj) while not page.packets or \ not page.packets[0].startswith(b"\x81theora"): page = OggPage(fileobj) old_pages = [page] while not (old_pages[-1].complete or len(old_pages[-1].packets) > 1): page = OggPage(fileobj) if page.serial == old_pages[0].serial: old_pages.append(page) packets = OggPage.to_packets(old_pages, strict=False) content_size = get_size(fileobj) - len(packets[0]) # approx vcomment_data = b"\x81theora" + self.write(framing=False) padding_left = len(packets[0]) - len(vcomment_data) info = PaddingInfo(padding_left, content_size) new_padding = info._get_padding(padding_func) packets[0] = vcomment_data + b"\x00" * new_padding new_pages = OggPage._from_packets_try_preserve(packets, old_pages) OggPage.replace(fileobj, old_pages, new_pages)
def _prepare_data(self, fileobj, start, available, v2_version, v23_sep, pad_func): if v2_version not in (3, 4): raise ValueError("Only 3 or 4 allowed for v2_version") config = ID3SaveConfig(v2_version, v23_sep) framedata = self._write(config) needed = len(framedata) + 10 fileobj.seek(0, 2) trailing_size = fileobj.tell() - start info = PaddingInfo(available - needed, trailing_size) new_padding = info._get_padding(pad_func) if new_padding < 0: raise error("invalid padding") new_size = needed + new_padding new_framesize = BitPaddedInt.to_str(new_size - 10, width=4) header = struct.pack('>3sBBB4s', b'ID3', v2_version, 0, 0, new_framesize) data = header + framedata assert new_size >= len(data) data += (new_size - len(data)) * b'\x00' assert new_size == len(data) return data
def _inject(self, fileobj, padding_func): """Write tag data into the Theora comment packet/page.""" fileobj.seek(0) page = OggPage(fileobj) while not page.packets[0].startswith(b"\x81theora"): page = OggPage(fileobj) old_pages = [page] while not (old_pages[-1].complete or len(old_pages[-1].packets) > 1): page = OggPage(fileobj) if page.serial == old_pages[0].serial: old_pages.append(page) packets = OggPage.to_packets(old_pages, strict=False) content_size = get_size(fileobj) - len(packets[0]) # approx vcomment_data = b"\x81theora" + self.write(framing=False) padding_left = len(packets[0]) - len(vcomment_data) info = PaddingInfo(padding_left, content_size) new_padding = info._get_padding(padding_func) packets[0] = vcomment_data + b"\x00" * new_padding new_pages = OggPage._from_packets_try_preserve(packets, old_pages) OggPage.replace(fileobj, old_pages, new_pages)
def _prepare_data(self, fileobj, start, available, v2_version, v23_sep, pad_func): if v2_version not in (3, 4): raise ValueError("Only 3 or 4 allowed for v2_version") config = ID3SaveConfig(v2_version, v23_sep) framedata = self._write(config) needed = len(framedata) + 10 fileobj.seek(0, 2) trailing_size = fileobj.tell() - start info = PaddingInfo(available - needed, trailing_size) new_padding = info._get_padding(pad_func) if new_padding < 0: raise error("invalid padding") new_size = needed + new_padding new_framesize = BitPaddedInt.to_str(new_size - 10, width=4) header = struct.pack( '>3sBBB4s', b'ID3', v2_version, 0, 0, new_framesize) data = header + framedata assert new_size >= len(data) data += (new_size - len(data)) * b'\x00' assert new_size == len(data) return data
def render_full(self, asf, fileobj, available, padding_func): # Render everything except padding num_objects = 0 data = bytearray() for obj in self.objects: if obj.GUID == PaddingObject.GUID: continue data += obj.render(asf) num_objects += 1 # calculate how much space we need at least padding_obj = PaddingObject() header_size = len(HeaderObject.GUID) + 14 padding_overhead = len(padding_obj.render(asf)) needed_size = len(data) + header_size + padding_overhead # ask the user for padding adjustments file_size = get_size(fileobj) content_size = file_size - available assert content_size >= 0 info = PaddingInfo(available - needed_size, content_size) # add padding padding = info._get_padding(padding_func) padding_obj.parse(asf, b"\x00" * padding) data += padding_obj.render(asf) num_objects += 1 data = (HeaderObject.GUID + struct.pack("<QL", len(data) + 30, num_objects) + b"\x01\x02" + data) return data
def _prepare_data(self, fileobj, start, available, v2_version, v23_sep, pad_func): if v2_version == 3: version = ID3Header._V23 elif v2_version == 4: version = ID3Header._V24 else: raise ValueError("Only 3 or 4 allowed for v2_version") # Sort frames by 'importance' order = ["TIT2", "TPE1", "TRCK", "TALB", "TPOS", "TDRC", "TCON"] order = dict((b, a) for a, b in enumerate(order)) last = len(order) frames = sorted(self.items(), key=lambda a: (order.get(a[0][:4], last), a[0])) framedata = [ self.__save_frame(frame, version=version, v23_sep=v23_sep) for (key, frame) in frames ] # only write unknown frames if they were loaded from the version # we are saving with or upgraded to it if self.__unknown_version == version[:2]: framedata.extend(data for data in self.unknown_frames if len(data) > 10) needed = sum(map(len, framedata)) + 10 fileobj.seek(0, 2) trailing_size = fileobj.tell() - start info = PaddingInfo(available - needed, trailing_size) new_padding = info._get_padding(pad_func) if new_padding < 0: raise error("invalid padding") new_size = needed + new_padding new_framesize = BitPaddedInt.to_str(new_size - 10, width=4) header = pack('>3sBBB4s', b'ID3', v2_version, 0, 0, new_framesize) data = bytearray(header) for frame in framedata: data += frame assert new_size >= len(data) data += (new_size - len(data)) * b'\x00' assert new_size == len(data) return data
def _prepare_data(self, fileobj, start, available, v2_version, v23_sep, pad_func): if v2_version == 3: version = ID3Header._V23 elif v2_version == 4: version = ID3Header._V24 else: raise ValueError("Only 3 or 4 allowed for v2_version") # Sort frames by 'importance' order = ["TIT2", "TPE1", "TRCK", "TALB", "TPOS", "TDRC", "TCON"] order = dict((b, a) for a, b in enumerate(order)) last = len(order) frames = sorted(self.items(), key=lambda a: (order.get(a[0][:4], last), a[0])) framedata = [self.__save_frame(frame, version=version, v23_sep=v23_sep) for (key, frame) in frames] # only write unknown frames if they were loaded from the version # we are saving with or upgraded to it if self.__unknown_version == version[:2]: framedata.extend(data for data in self.unknown_frames if len(data) > 10) needed = sum(map(len, framedata)) + 10 fileobj.seek(0, 2) trailing_size = fileobj.tell() - start info = PaddingInfo(available - needed, trailing_size) new_padding = info._get_padding(pad_func) if new_padding < 0: raise error("invalid padding") new_size = needed + new_padding new_framesize = BitPaddedInt.to_str(new_size - 10, width=4) header = pack('>3sBBB4s', b'ID3', v2_version, 0, 0, new_framesize) data = bytearray(header) for frame in framedata: data += frame assert new_size >= len(data) data += (new_size - len(data)) * b'\x00' assert new_size == len(data) return data
def _inject(self, fileobj, padding_func): """Write tag data into the Speex comment packet/page.""" fileobj.seek(0) # Find the first header page, with the stream info. # Use it to get the serial number. page = OggPage(fileobj) while not page.packets[0].startswith(b"Speex "): page = OggPage(fileobj) # Look for the next page with that serial number, it'll start # the comment packet. serial = page.serial page = OggPage(fileobj) while page.serial != serial: page = OggPage(fileobj) # Then find all the pages with the comment packet. old_pages = [page] while not (old_pages[-1].complete or len(old_pages[-1].packets) > 1): page = OggPage(fileobj) if page.serial == old_pages[0].serial: old_pages.append(page) packets = OggPage.to_packets(old_pages, strict=False) content_size = get_size(fileobj) - len(packets[0]) # approx vcomment_data = self.write(framing=False) padding_left = len(packets[0]) - len(vcomment_data) info = PaddingInfo(padding_left, content_size) new_padding = info._get_padding(padding_func) # Set the new comment packet. packets[0] = vcomment_data + b"\x00" * new_padding new_pages = OggPage._from_packets_try_preserve(packets, old_pages) OggPage.replace(fileobj, old_pages, new_pages)
def _writeblocks(cls, blocks, available, cont_size, padding_func): """Render metadata block as a byte string.""" # write everything except padding data = bytearray() for block in blocks: if isinstance(block, Padding): continue data += cls._writeblock(block) blockssize = len(data) # take the padding overhead into account. we always add one # to make things simple. padding_block = Padding() blockssize += len(cls._writeblock(padding_block)) # finally add a padding block info = PaddingInfo(available - blockssize, cont_size) padding_block.length = min(info._get_padding(padding_func), cls._MAX_SIZE) data += cls._writeblock(padding_block, is_last=True) return data
def _inject(self, fileobj, padding_func): fileobj.seek(0) info = OggOpusInfo(fileobj) old_pages = self.__get_comment_pages(fileobj, info) packets = OggPage.to_packets(old_pages) vcomment_data = b"OpusTags" + self.write(framing=False) if self._pad_data: # if we have padding data to preserver we can't add more padding # as long as we don't know the structure of what follows packets[0] = vcomment_data + self._pad_data else: content_size = get_size(fileobj) - len(packets[0]) # approx padding_left = len(packets[0]) - len(vcomment_data) info = PaddingInfo(padding_left, content_size) new_padding = info._get_padding(padding_func) packets[0] = vcomment_data + b"\x00" * new_padding new_pages = OggPage._from_packets_try_preserve(packets, old_pages) OggPage.replace(fileobj, old_pages, new_pages)