def packets(self, from_beginning=True): """Yields one fully reassembled Ogg packet per pass. Packets are returned as binary strings.""" if (from_beginning): self.stream.seek(0, 0) segment = cStringIO.StringIO() while (True): try: page = OggStreamReader.OGGS.parse_stream(self.stream) for length in page.segment_lengths: if (length == 255): segment.write(self.stream.read(length)) else: segment.write(self.stream.read(length)) yield segment.getvalue() segment = cStringIO.StringIO() except Con.core.FieldError: break except Con.ConstError: break
def query(self, disc_id): matches = [] self.messenger.info( _(u"Sending Disc ID \"%(disc_id)s\" to server \"%(server)s\"") % \ {"disc_id":str(disc_id).decode('ascii'), "server":self.server.decode('ascii','replace')}) self.write("cddb query " + disc_id.freedb_id()) data = cStringIO.StringIO(self.read()) (code, msg) = self.__parse_line__(data.readline()) if (code == 200): matches.append(msg) elif ((code == 211) or (code == 210)): while (msg != "."): (code, msg) = self.__parse_line__(data.readline()) if (msg != "."): matches.append(msg) if (len(matches) == 1): self.messenger.info(_(u"1 match found")) else: self.messenger.info(_(u"%s matches found") % (len(matches))) return map(lambda m: m.split(" ", 2), matches)
def query(self, disc_id): """Given a DiscID, performs an album query and returns matches. Each match is a (category, id) pair, which the user may need to decide between.""" matches = [] self.messenger.info( _(u"Sending Disc ID \"%(disc_id)s\" to server \"%(server)s\"") % \ {"disc_id": str(disc_id).decode('ascii'), "server": self.server.decode('ascii', 'replace')}) self.write("cddb query " + disc_id.freedb_id()) data = cStringIO.StringIO(self.read()) (code, msg) = self.__parse_line__(data.readline()) if (code == 200): matches.append(msg) elif ((code == 211) or (code == 210)): while (msg != "."): (code, msg) = self.__parse_line__(data.readline()) if (msg != "."): matches.append(msg) if (len(matches) == 1): self.messenger.info(_(u"1 match found")) else: self.messenger.info(_(u"%s matches found") % (len(matches))) return map(lambda m: m.split(" ", 2), matches)
def __populate_metadata__(self): #set up some default values self.__bits_per_sample__ = 16 self.__channels__ = 2 self.__channel_mask__ = 0x3 self.__sample_rate__ = 44100 self.__total_frames__ = 0 self.__blocks__ = [] self.__format__ = None #grab a few pieces of technical metadata from the Shorten file itself #which requires a dry-run through the decoder try: decoder = audiotools.decoders.SHNDecoder(self.filename) try: self.__bits_per_sample__ = decoder.bits_per_sample self.__channels__ = decoder.channels (self.__total_frames__, self.__blocks__) = decoder.metadata() finally: decoder.close() try: self.__channel_mask__ = ChannelMask.from_channels( self.__channels__) except ValueError: self.__channel_mask__ = 0 except (ValueError, IOError): #if we hit an error in SHNDecoder while reading #technical metadata, the default values will have to do return #the remainder requires parsing the file's VERBATIM blocks #which may contain Wave, AIFF or Sun AU info if (self.__blocks__[0] is not None): header = cStringIO.StringIO(self.__blocks__[0]) for format in WaveAudio, AiffAudio: header.seek(0, 0) if (format.is_type(header)): self.__format__ = format break if (self.__format__ is WaveAudio): for (chunk_id, chunk_data) in self.__wave_chunks__(): if (chunk_id == 'fmt '): fmt_chunk = WaveAudio.FMT_CHUNK.parse(chunk_data) self.__sample_rate__ = fmt_chunk.sample_rate if (fmt_chunk.compression == 0xFFFE): self.__channel_mask__ = \ WaveAudio.fmt_chunk_to_channel_mask( fmt_chunk.channel_mask) elif (self.__format__ is AiffAudio): for (chunk_id, chunk_data) in self.__aiff_chunks__(): if (chunk_id == 'COMM'): comm_chunk = AiffAudio.COMM_CHUNK.parse(chunk_data) self.__sample_rate__ = comm_chunk.sample_rate
def read_data(self, category, id, output): self.write("cddb read " + category + " " + id) data = cStringIO.StringIO(self.read()) (code, msg) = self.__parse_line__(data.readline()) if (code == 210): line = data.readline() while (line.strip() != "."): output.write(line) line = data.readline() else: print >> sys.stderr, (code, msg)
def packets(self, from_beginning=True): if (from_beginning): self.stream.seek(0, 0) segment = cStringIO.StringIO() while (True): try: page = OggStreamReader.OGGS.parse_stream(self.stream) for length in page.segment_lengths: if (length == 255): segment.write(self.stream.read(length)) else: segment.write(self.stream.read(length)) yield segment.getvalue() segment = cStringIO.StringIO() except construct.core.FieldError: break except construct.ConstError: break
def __populate_metadata__(self): # Alteration for BootTunes: # Since the vast majority of files will match the defaults, and because this method crashes # Windows 7 and locks up Mac, just go with the defaults. self.__bits_per_sample__ = 16 self.__channels__ = 2 self.__channel_mask__ = 0x3 self.__sample_rate__ = 44100 self.__total_frames__ = 0 self.__blocks__ = [] self.__format__ = None return #grab a few pieces of technical metadata from the Shorten file itself #which requires a dry-run through the decoder decoder = audiotools.decoders.SHNDecoder(self.filename) self.__bits_per_sample__ = decoder.bits_per_sample self.__channels__ = decoder.channels (self.__total_frames__, self.__blocks__) = decoder.metadata() decoder.close() #set up some default values self.__sample_rate__ = 44100 try: self.__channel_mask__ = ChannelMask.from_channels(self.__channels__) except ValueError: self.__channel_mask__ = 0 self.__format__ = None #the remainder requires parsing the file's VERBATIM blocks #which may contain Wave, AIFF or Sun AU info if (self.__blocks__[0] is not None): header = cStringIO.StringIO(self.__blocks__[0]) for format in WaveAudio,AiffAudio: header.seek(0,0) if (format.is_type(header)): self.__format__ = format break if (self.__format__ is WaveAudio): for (chunk_id,chunk_data) in self.__wave_chunks__(): if (chunk_id == 'fmt '): fmt_chunk = WaveAudio.FMT_CHUNK.parse(chunk_data) self.__sample_rate__ = fmt_chunk.sample_rate if (fmt_chunk.compression == 0xFFFE): self.__channel_mask__ = WaveAudio.fmt_chunk_to_channel_mask(fmt_chunk.channel_mask) elif (self.__format__ is AiffAudio): for (chunk_id,chunk_data) in self.__aiff_chunks__(): if (chunk_id == 'COMM'): comm_chunk = AiffAudio.COMM_CHUNK.parse(chunk_data) self.__sample_rate__ = comm_chunk.sample_rate
def set_metadata(self, metadata): """Takes a MetaData object and sets this track's metadata. This metadata includes track name, album name, and so on. Raises IOError if unable to write the file.""" comment = VorbisComment.converted(metadata) if (comment is None): return reader = OggStreamReader(file(self.filename, 'rb')) new_file = cStringIO.StringIO() writer = OggStreamWriter(new_file) pages = reader.pages() #transfer our old header (header_page, header_data) = pages.next() writer.write_page(header_page, header_data) #skip the existing comment packet (page, data) = pages.next() while (page.segment_lengths[-1] == 255): (page, data) = pages.next() #write the pages for our new comment packet comment_pages = OggStreamWriter.build_pages( 0, header_page.bitstream_serial_number, header_page.page_sequence_number + 1, comment.build()) for (page, data) in comment_pages: writer.write_page(page, data) #write the rest of the pages, re-sequenced and re-checksummed sequence_number = comment_pages[-1][0].page_sequence_number + 1 for (i, (page, data)) in enumerate(pages): page.page_sequence_number = i + sequence_number page.checksum = OggStreamReader.calculate_ogg_checksum(page, data) writer.write_page(page, data) reader.close() #re-write the file with our new data in "new_file" f = file(self.filename, "wb") f.write(new_file.getvalue()) f.close() writer.close() self.__read_metadata__()
def read_data(self, category, id, output): """Reads the FreeDB entry matching category and id to output. category and id are raw strings, as returned by query(). output is an open file object. """ self.write("cddb read " + category + " " + id) data = cStringIO.StringIO(self.read()) (code, msg) = self.__parse_line__(data.readline()) if (code == 210): line = data.readline() while (line.strip() != "."): output.write(line) line = data.readline() else: print >> sys.stderr, (code, msg)
def to_string(self): def write_field(f, key, value): chars = list(value) encoded_value = "%s=" % (key) while ((len(chars) > 0) and (len(encoded_value + chars[0].encode('utf-8', 'replace')) < XMCD.LINE_LENGTH)): encoded_value += chars.pop(0).encode('utf-8', 'replace') f.write("%s\r\n" % (encoded_value)) if (len(chars) > 0): write_field(f, key, u"".join(chars)) output = cStringIO.StringIO() for comment in self.comments: output.write(comment.encode('utf-8')) output.write('\r\n') fields = set(self.fields.keys()) for field in ['DISCID', 'DTITLE', 'DYEAR', 'DGENRE']: if (field in fields): write_field(output, field, self.fields[field]) fields.remove(field) for i in xrange(len(self)): field = 'TTITLE%d' % (i) if (field in fields): write_field(output, field, self.fields[field]) fields.remove(field) if ('EXTD' in fields): write_field(output, 'EXTD', self.fields['EXTD']) fields.remove('EXTD') for i in xrange(len(self)): field = 'EXTT%d' % (i) if (field in fields): write_field(output, field, self.fields[field]) fields.remove(field) for field in fields: write_field(output, field, self.fields[field]) return output.getvalue()
def __wave_chunks__(self): total_size = sum([len(block) for block in self.__blocks__ if block is not None]) wave_data = cStringIO.StringIO("".join([block for block in self.__blocks__ if block is not None])) wave_data.read(12) # skip the RIFFxxxxWAVE header data total_size -= 12 #iterate over all the non-data chunks while (total_size > 0): header = WaveAudio.CHUNK_HEADER.parse_stream(wave_data) total_size -= 8 if (header.chunk_id != 'data'): yield (header.chunk_id, wave_data.read(header.chunk_length)) total_size -= header.chunk_length else: continue
def __aiff_chunks__(self): total_size = sum([len(block) for block in self.__blocks__ if block is not None]) aiff_data = cStringIO.StringIO("".join([block for block in self.__blocks__ if block is not None])) aiff_data.read(12) # skip the FORMxxxxAIFF header data total_size -= 12 #iterate over all the non-ssnd chunks while (total_size > 0): header = AiffAudio.CHUNK_HEADER.parse_stream(aiff_data) total_size -= 8 if (header.chunk_id != 'SSND'): yield (header.chunk_id, aiff_data.read(header.chunk_length)) total_size -= header.chunk_length else: #not sure what Shorten does with the first 8 bytes #of the SSND chunk #it'll be a mess if it turns those into audio data continue
def __parse_image__(self, key, type): data = cStringIO.StringIO(str(self[key])) description = construct.CString(None).parse_stream(data).decode( 'utf-8', 'replace') data = data.read() return Image.new(data, description, type)
def set_metadata(self, metadata): metadata = VorbisComment.converted(metadata) if (metadata is None): return reader = OggStreamReader(file(self.filename, 'rb')) new_file = cStringIO.StringIO() writer = OggStreamWriter(new_file) current_sequence_number = 0 pages = reader.pages() #transfer our old header #this must always be the first packet and the first page (header_page, header_data) = pages.next() writer.write_page(header_page, header_data) current_sequence_number += 1 #grab the current "comment" and "setup headers" packets #these may take one or more pages, #but will always end on a page boundary del (pages) packets = reader.packets(from_beginning=False) comment_packet = packets.next() headers_packet = packets.next() #write the pages for our new "comment" packet for (page, data) in OggStreamWriter.build_pages( 0, header_page.bitstream_serial_number, current_sequence_number, VorbisAudio.COMMENT_HEADER.build( construct.Container(packet_type=3, vorbis='vorbis')) + metadata.build()): writer.write_page(page, data) current_sequence_number += 1 #write the pages for the old "setup headers" packet for (page, data) in OggStreamWriter.build_pages( 0, header_page.bitstream_serial_number, current_sequence_number, headers_packet): writer.write_page(page, data) current_sequence_number += 1 #write the rest of the pages, re-sequenced and re-checksummed del (packets) pages = reader.pages(from_beginning=False) for (i, (page, data)) in enumerate(pages): page.page_sequence_number = i + current_sequence_number page.checksum = OggStreamReader.calculate_ogg_checksum(page, data) writer.write_page(page, data) reader.close() #re-write the file with our new data in "new_file" f = file(self.filename, "wb") f.write(new_file.getvalue()) f.close() writer.close() self.__read_metadata__()