def __init__(self, comment_strings, vendor_string): """comment_strings is a list of unicode strings vendor_string is a unicode string""" MetaData.__setattr__(self, "comment_strings", comment_strings) MetaData.__setattr__(self, "vendor_string", vendor_string)
def __setitem__(self, key, values): from audiotools import PY3 assert(isinstance(key, str if PY3 else unicode)) for v in values: assert(isinstance(v, str if PY3 else unicode)) new_values = values[:] new_comment_strings = [] matching_keys = self.ALIASES.get(key.upper(), frozenset([key.upper()])) for comment in self.comment_strings: if u"=" in comment: (c_key, c_value) = comment.split(u"=", 1) if c_key.upper() in matching_keys: try: # replace current value with newly set value new_comment_strings.append( u"{}={}".format(c_key, new_values.pop(0))) except IndexError: # no more newly set values, so remove current value continue else: # passthrough unmatching values new_comment_strings.append(comment) else: # passthrough values with no "=" sign new_comment_strings.append(comment) # append any leftover values for new_value in new_values: new_comment_strings.append(u"{}={}".format(key.upper(), new_value)) MetaData.__setattr__(self, "comment_strings", new_comment_strings)
def __setitem__(self, key, values): new_values = values[:] new_comment_strings = [] matching_keys = self.ALIASES.get(key.upper(), frozenset([key.upper()])) for comment in self.comment_strings: if (u"=" in comment): (c_key, c_value) = comment.split(u"=", 1) if (c_key.upper() in matching_keys): try: # replace current value with newly set value new_comment_strings.append( u"%s=%s" % (c_key, new_values.pop(0))) except IndexError: # no more newly set values, so remove current value continue else: # passthrough unmatching values new_comment_strings.append(comment) else: # passthrough values with no "=" sign new_comment_strings.append(comment) # append any leftover values for new_value in new_values: new_comment_strings.append(u"%s=%s" % (key.upper(), new_value)) MetaData.__setattr__(self, "comment_strings", new_comment_strings)
def __setitem__(self, key, values): from audiotools import PY3 assert (isinstance(key, str if PY3 else unicode)) for v in values: assert (isinstance(v, str if PY3 else unicode)) new_values = values[:] new_comment_strings = [] matching_keys = self.ALIASES.get(key.upper(), frozenset([key.upper()])) for comment in self.comment_strings: if u"=" in comment: (c_key, c_value) = comment.split(u"=", 1) if c_key.upper() in matching_keys: try: # replace current value with newly set value new_comment_strings.append(u"{}={}".format( c_key, new_values.pop(0))) except IndexError: # no more newly set values, so remove current value continue else: # passthrough unmatching values new_comment_strings.append(comment) else: # passthrough values with no "=" sign new_comment_strings.append(comment) # append any leftover values for new_value in new_values: new_comment_strings.append(u"{}={}".format(key.upper(), new_value)) MetaData.__setattr__(self, "comment_strings", new_comment_strings)
def __setattr__(self, attr, value): if (attr in self.ATTRIBUTE_MAP): if (value is not None): import re if (attr == 'track_number'): try: self[b'Track'].data = (re.sub( r'\d+', u"%d" % (value), self[b'Track'].__unicode__(), 1).encode("utf-8")) except KeyError: self[b'Track'] = self.ITEM.string( b'Track', __number_pair__(value, self.track_total)) elif (attr == 'track_total'): try: if (re.search(r'/\D*\d+', self[b'Track'].__unicode__()) is not None): self[b'Track'].data = (re.sub( r'(/\D*)(\d+)', u"\\g<1>" + u"%d" % (value), self[b'Track'].__unicode__(), 1).encode("utf-8")) else: self[b'Track'].data = ( u"%s/%d" % (self[b'Track'].__unicode__(), value)).encode("utf-8") except KeyError: self[b'Track'] = self.ITEM.string( b'Track', __number_pair__(self.track_number, value)) elif (attr == 'album_number'): try: self[b'Media'].data = (re.sub( r'\d+', u"%d" % (value), self[b'Media'].__unicode__(), 1).encode("utf-8")) except KeyError: self[b'Media'] = self.ITEM.string( b'Media', __number_pair__(value, self.album_total)) elif (attr == 'album_total'): try: if (re.search(r'/\D*\d+', self[b'Media'].__unicode__()) is not None): self[b'Media'].data = (re.sub( r'(/\D*)(\d+)', u"\\g<1>" + u"%d" % (value), self[b'Media'].__unicode__(), 1).encode("utf-8")) else: self[b'Media'].data = ( u"%s/%d" % (self[b'Media'].__unicode__(), value)).encode("utf-8") except KeyError: self[b'Media'] = self.ITEM.string( b'Media', __number_pair__(self.album_number, value)) else: self[self.ATTRIBUTE_MAP[attr]] = self.ITEM.string( self.ATTRIBUTE_MAP[attr], value) else: delattr(self, attr) else: MetaData.__setattr__(self, attr, value)
def __delattr__(self, attr): if attr == "track_number": MetaData.__setattr__(self, "__track_number__", 0) elif attr in self.FIELD_LENGTHS: MetaData.__setattr__(self, self.ID3v1_FIELDS[attr], u"") elif attr in self.FIELDS: # field not supported by ID3v1Comment, so ignore it pass else: MetaData.__delattr__(self, attr)
def __delattr__(self, attr): if (attr == "track_number"): MetaData.__setattr__(self, "__track_number__", chr(0)) elif (attr in self.FIELD_LENGTHS): MetaData.__setattr__(self, self.ID3v1_FIELDS[attr], chr(0) * self.FIELD_LENGTHS[attr]) elif (attr in self.FIELDS): # field not supported by ID3v1Comment, so ignore it pass else: MetaData.__delattr__(self, attr)
def __setattr__(self, attr, value): if (attr in self.ATTRIBUTE_MAP): if (value is not None): import re if (attr == 'track_number'): try: self['Track'].data = re.sub(r'\d+', str(int(value)), self['Track'].data, 1) except KeyError: self['Track'] = self.ITEM.string( 'Track', __number_pair__(value, self.track_total)) elif (attr == 'track_total'): try: if (re.search(r'/\D*\d+', self['Track'].data) is not None): self['Track'].data = re.sub( r'(/\D*)(\d+)', "\\g<1>" + str(int(value)), self['Track'].data, 1) else: self['Track'].data = "%s/%d" % (self['Track'].data, value) except KeyError: self['Track'] = self.ITEM.string( 'Track', __number_pair__(self.track_number, value)) elif (attr == 'album_number'): try: self['Media'].data = re.sub(r'\d+', str(int(value)), self['Media'].data, 1) except KeyError: self['Media'] = self.ITEM.string( 'Media', __number_pair__(value, self.album_total)) elif (attr == 'album_total'): try: if (re.search(r'/\D*\d+', self['Media'].data) is not None): self['Media'].data = re.sub( r'(/\D*)(\d+)', "\\g<1>" + str(int(value)), self['Media'].data, 1) else: self['Media'].data = "%s/%d" % (self['Media'].data, value) except KeyError: self['Media'] = self.ITEM.string( 'Media', __number_pair__(self.album_number, value)) else: self[self.ATTRIBUTE_MAP[attr]] = self.ITEM.string( self.ATTRIBUTE_MAP[attr], value) else: delattr(self, attr) else: MetaData.__setattr__(self, attr, value)
def __init__(self, comment_strings, vendor_string): """comment_strings is a list of unicode strings vendor_string is a unicode string""" from audiotools import PY3 # some debug type checking for s in comment_strings: assert (isinstance(s, str if PY3 else unicode)) assert (isinstance(vendor_string, str if PY3 else unicode)) MetaData.__setattr__(self, "comment_strings", comment_strings) MetaData.__setattr__(self, "vendor_string", vendor_string)
def __init__(self, comment_strings, vendor_string): """comment_strings is a list of unicode strings vendor_string is a unicode string""" from audiotools import PY3 # some debug type checking for s in comment_strings: assert(isinstance(s, str if PY3 else unicode)) assert(isinstance(vendor_string, str if PY3 else unicode)) MetaData.__setattr__(self, "comment_strings", comment_strings) MetaData.__setattr__(self, "vendor_string", vendor_string)
def __delitem__(self, key): new_comment_strings = [] matching_keys = self.ALIASES.get(key.upper(), frozenset([key.upper()])) for comment in self.comment_strings: if (u"=" in comment): (c_key, c_value) = comment.split(u"=", 1) if (c_key.upper() not in matching_keys): # passthrough unmatching values new_comment_strings.append(comment) else: # passthrough values with no "=" sign new_comment_strings.append(comment) MetaData.__setattr__(self, "comment_strings", new_comment_strings)
def __delitem__(self, key): from audiotools import PY3 assert(isinstance(key, str if PY3 else unicode)) new_comment_strings = [] matching_keys = self.ALIASES.get(key.upper(), frozenset([key.upper()])) for comment in self.comment_strings: if u"=" in comment: (c_key, c_value) = comment.split(u"=", 1) if c_key.upper() not in matching_keys: # passthrough unmatching values new_comment_strings.append(comment) else: # passthrough values with no "=" sign new_comment_strings.append(comment) MetaData.__setattr__(self, "comment_strings", new_comment_strings)
def __delitem__(self, key): from audiotools import PY3 assert (isinstance(key, str if PY3 else unicode)) new_comment_strings = [] matching_keys = self.ALIASES.get(key.upper(), frozenset([key.upper()])) for comment in self.comment_strings: if u"=" in comment: (c_key, c_value) = comment.split(u"=", 1) if c_key.upper() not in matching_keys: # passthrough unmatching values new_comment_strings.append(comment) else: # passthrough values with no "=" sign new_comment_strings.append(comment) MetaData.__setattr__(self, "comment_strings", new_comment_strings)
def __setattr__(self, attr, value): def swap_number(unicode_value, new_number): import re return re.sub(r'\d+', u"%d" % (new_number), unicode_value, 1) def swap_slashed_number(unicode_value, new_number): if u"/" in unicode_value: (first, second) = unicode_value.split(u"/", 1) return u"/".join([first, swap_number(second, new_number)]) else: return u"/".join([unicode_value, u"%d" % (new_number)]) if attr in self.ATTRIBUTE_MAP: key = self.ATTRIBUTE_MAP[attr] if value is not None: if attr in {'track_number', 'album_number'}: try: current_value = self[key].__unicode__() self[key] = self.ITEM.string( key, swap_number(current_value, value)) except KeyError: self[key] = self.ITEM.string( key, __number_pair__(value, None)) elif attr in {'track_total', 'album_total'}: try: current_value = self[key].__unicode__() self[key] = self.ITEM.string( key, swap_slashed_number(current_value, value)) except KeyError: self[key] = self.ITEM.string( key, __number_pair__(None, value)) elif attr == 'compilation': self[key] = self.ITEM.string(key, u"%d" % (1 if value else 0)) else: self[key] = self.ITEM.string(key, value) else: delattr(self, attr) else: MetaData.__setattr__(self, attr, value)
def __setattr__(self, attr, value): def swap_number(unicode_value, new_number): import re return re.sub(r'\d+', u"{:d}".format(new_number), unicode_value, 1) def swap_slashed_number(unicode_value, new_number): if u"/" in unicode_value: (first, second) = unicode_value.split(u"/", 1) return u"/".join([first, swap_number(second, new_number)]) else: return u"/".join([unicode_value, u"{:d}".format(new_number)]) if attr in self.ATTRIBUTE_MAP: key = self.ATTRIBUTE_MAP[attr] if value is not None: if attr in {'track_number', 'album_number'}: try: current_value = self[key].__unicode__() self[key] = self.ITEM.string( key, swap_number(current_value, value)) except KeyError: self[key] = self.ITEM.string( key, __number_pair__(value, None)) elif attr in {'track_total', 'album_total'}: try: current_value = self[key].__unicode__() self[key] = self.ITEM.string( key, swap_slashed_number(current_value, value)) except KeyError: self[key] = self.ITEM.string( key, __number_pair__(None, value)) elif attr == 'compilation': self[key] = self.ITEM.string( key, u"{:d}".format(1 if value else 0)) else: self[key] = self.ITEM.string(key, value) else: delattr(self, attr) else: MetaData.__setattr__(self, attr, value)
def __init__(self, tags, contains_header=True, contains_footer=True): """constructs an ApeTag from a list of ApeTagItem objects""" for tag in tags: assert(isinstance(tag, ApeTagItem)) MetaData.__setattr__(self, "tags", list(tags)) MetaData.__setattr__(self, "contains_header", contains_header) MetaData.__setattr__(self, "contains_footer", contains_footer)
def __init__(self, tags, contains_header=True, contains_footer=True): """constructs an ApeTag from a list of ApeTagItem objects""" for tag in tags: if (not isinstance(tag, ApeTagItem)): raise ValueError("%s is not ApeTag" % (repr(tag))) MetaData.__setattr__(self, "tags", list(tags)) MetaData.__setattr__(self, "contains_header", contains_header) MetaData.__setattr__(self, "contains_footer", contains_footer)
def __setattr__(self, attr, value): if attr == "track_number": MetaData.__setattr__( self, "__track_number__", min(0 if (value is None) else int(value), 0xFF)) elif attr in self.FIELD_LENGTHS: if value is None: delattr(self, attr) else: # all are text fields MetaData.__setattr__(self, self.ID3v1_FIELDS[attr], value[0:self.FIELD_LENGTHS[attr]]) elif attr in self.FIELDS: # field not supported by ID3v1Comment, so ignore it pass else: MetaData.__setattr__(self, attr, value)
def __setattr__(self, attr, value): if attr == "track_number": MetaData.__setattr__( self, "__track_number__", min(0 if (value is None) else int(value), 0xFF)) elif attr in self.FIELD_LENGTHS: if value is None: delattr(self, attr) else: # all are text fields MetaData.__setattr__( self, self.ID3v1_FIELDS[attr], value[0:self.FIELD_LENGTHS[attr]]) elif attr in self.FIELDS: # field not supported by ID3v1Comment, so ignore it pass else: MetaData.__setattr__(self, attr, value)
def __setattr__(self, attr, value): if (attr == "track_number"): if (value is None): MetaData.__setattr__(self, "__track_number__", chr(0)) else: MetaData.__setattr__(self, "__track_number__", chr(min(int(value), 0xFF))) elif (attr in self.FIELD_LENGTHS): if (value is None): delattr(self, attr) else: # all are text fields encoded = value.encode('ascii', 'replace') if (len(encoded) < self.FIELD_LENGTHS[attr]): MetaData.__setattr__( self, self.ID3v1_FIELDS[attr], encoded + chr(0) * (self.FIELD_LENGTHS[attr] - len(encoded))) elif (len(encoded) > self.FIELD_LENGTHS[attr]): MetaData.__setattr__( self, self.ID3v1_FIELDS[attr], encoded[0:self.FIELD_LENGTHS[attr]]) else: MetaData.__setattr__( self, self.ID3v1_FIELDS[attr], encoded) elif (attr in self.FIELDS): # field not supported by ID3v1Comment, so ignore it pass else: MetaData.__setattr__(self, attr, value)
def __setattr__(self, attr, value): if (attr == "track_number"): if (value is None): MetaData.__setattr__(self, "__track_number__", chr(0)) else: MetaData.__setattr__(self, "__track_number__", chr(min(int(value), 0xFF))) elif (attr in self.FIELD_LENGTHS): if (value is None): delattr(self, attr) else: # all are text fields encoded = value.encode('ascii', 'replace') if (len(encoded) < self.FIELD_LENGTHS[attr]): MetaData.__setattr__( self, self.ID3v1_FIELDS[attr], encoded + chr(0) * (self.FIELD_LENGTHS[attr] - len(encoded))) elif (len(encoded) > self.FIELD_LENGTHS[attr]): MetaData.__setattr__(self, self.ID3v1_FIELDS[attr], encoded[0:self.FIELD_LENGTHS[attr]]) else: MetaData.__setattr__(self, self.ID3v1_FIELDS[attr], encoded) elif (attr in self.FIELDS): # field not supported by ID3v1Comment, so ignore it pass else: MetaData.__setattr__(self, attr, value)
def __setattr__(self, attr, value): # updates the first matching field for the given attribute # in our list of comment strings def has_number(unicode_string): import re return re.search(r'\d+', unicode_string) is not None def swap_number(unicode_value, new_number): import re return re.sub(r'\d+', u"{:d}".format(new_number), unicode_value, 1) if (attr in self.FIELDS) and (value is None): # setting any value to None is equivilent to deleting it # in this high-level implementation delattr(self, attr) elif attr in self.ATTRIBUTE_MAP: key = self.ATTRIBUTE_MAP[attr] if attr in {'track_number', 'album_number'}: try: current_values = self[key] for i in range(len(current_values)): current_value = current_values[i] if u"/" not in current_value: if has_number(current_value): current_values[i] = swap_number(current_value, value) self[key] = current_values break else: (first, second) = current_value.split(u"/", 1) if has_number(first): current_values[i] = u"/".join( [swap_number(first, value), second]) self[key] = current_values break else: # no integer field matching key, so add new one self[key] = current_values + [u"{:d}".format(value)] except KeyError: # no current field with key, so add new one self[key] = [u"{:d}".format(value)] elif attr in {'track_total', 'album_total'}: # look for standalone TRACKTOTAL/DISCTOTAL field try: current_values = self[key] for i in range(len(current_values)): current_value = current_values[i] if has_number(current_value): current_values[i] = swap_number(current_value, value) self[key] = current_values return except KeyError: current_values = [] # no TRACKTOTAL/DISCTOTAL field # or none of them contain an integer, # so look for slashed TRACKNUMBER/DISCNUMBER values try: new_key = {"track_total": u"TRACKNUMBER", "album_total": u"DISCNUMBER"}[attr] slashed_values = self[new_key] for i in range(len(slashed_values)): current_value = slashed_values[i] if u"/" in current_value: (first, second) = current_value.split(u"/", 1) if has_number(second): slashed_values[i] = u"/".join( [first, swap_number(second, value)]) self[new_key] = slashed_values return except KeyError: # no TRACKNUMBER/DISCNUMBER field found pass # no slashed TRACKNUMBER/DISCNUMBER values either # so append a TRACKTOTAL/DISCTOTAL field self[key] = current_values + [u"{:d}".format(value)] elif attr == "compilation": self[key] = [u"1" if value else u"0"] else: # leave subsequent fields with the same key as-is try: current_values = self[key] self[key] = [value] + current_values[1:] except KeyError: # no current field with key, so add new one self[key] = [value] elif attr in self.FIELDS: # attribute is supported by MetaData # but not supported by VorbisComment # so ignore it pass else: # attribute is not MetaData-specific, so set as-is MetaData.__setattr__(self, attr, value)
def __init__(self, track_name=chr(0) * 30, artist_name=chr(0) * 30, album_name=chr(0) * 30, year=chr(0) * 4, comment=chr(0) * 28, track_number=chr(0), genre=chr(0)): """fields are as follows: | field | length | |--------------+--------| | track_name | 30 | | artist_name | 30 | | album_name | 30 | | year | 4 | | comment | 28 | | track_number | 1 | | genre | 1 | |--------------+--------| all are binary strings of the given length and must not be any shorter or longer """ if (len(track_name) != 30): raise ValueError("track_name must be exactly 30 bytes") if (len(artist_name) != 30): raise ValueError("artist_name must be exactly 30 bytes") if (len(album_name) != 30): raise ValueError("album_name must be exactly 30 bytes") if (len(year) != 4): raise ValueError("year must be exactly 4 bytes") if (len(comment) != 28): raise ValueError("comment must be exactly 28 bytes") if (len(track_number) != 1): raise ValueError("track_number must be exactly 1 byte") if (len(genre) != 1): raise ValueError("genre must be exactly 1 byte") MetaData.__setattr__(self, "__track_name__", track_name) MetaData.__setattr__(self, "__artist_name__", artist_name) MetaData.__setattr__(self, "__album_name__", album_name) MetaData.__setattr__(self, "__year__", year) MetaData.__setattr__(self, "__comment__", comment) MetaData.__setattr__(self, "__track_number__", track_number) MetaData.__setattr__(self, "__genre__", genre)
def __setattr__(self, attr, value): # updates the first matching field for the given attribute # in our list of comment strings if ((value is None) and (attr in self.FIELDS)): # setting any value to None is equivilent to deleting it # in this high-level implementation delattr(self, attr) elif ((attr == "track_number") or (attr == "album_number")): key = self.ATTRIBUTE_MAP[attr] try: new_values = self[key] for i in range(len(new_values)): # look for the first TRACKNUMBER/DISCNUMBER field # which contains an integer if (re.search(r'\d+', new_values[i]) is not None): # and replace the integer part of the field new_values[i] = re.sub(r'\d+', u"%d" % (value), new_values[i], 1) # then set the field to the new set of values # (which may contain subsequent fields to leave as-is) self[key] = new_values break else: # no integer field with matching key # so append new integer field self[key] = self[key] + [u"%d" % (value)] except KeyError: # no TRACKNUMBER/DISCNUMBER field # so add a new one self[key] = [u"%d" % (value)] elif ((attr == "track_total") or (attr == "album_total")): key = self.ATTRIBUTE_MAP[attr] try: new_values = self[key] for i in range(len(new_values)): # look for the first TRACKTOTAL/DISCTOTAL field # which contains an integer if (re.search(r'\d+', new_values[i]) is not None): # and replace the integer part of the field new_values[i] = re.sub(r'\d+', u"%d" % (value), new_values[i], 1) self[key] = new_values return except KeyError: new_values = [] # no TRACKTOTAL/DISCTOTAL field # or none of them contain an integer, # so look for slashed TRACKNUMBER/DISCNUMBER values try: slashed_key = { "track_total": u"TRACKNUMBER", "album_total": u"DISCNUMBER" }[attr] new_slashed_values = self[slashed_key] for i in range(len(new_slashed_values)): # look for the first TRACKNUMBER/DISCNUMBER field # which contains a slashed value if (re.search(r'/\D*\d+', new_slashed_values[i]) is not None): # and replace the slashed part of the field new_slashed_values[i] = re.sub( r'(/\D*)(\d+)', u'\\g<1>' + (u"%d" % (value)), new_slashed_values[i], 1) self[slashed_key] = new_slashed_values return except KeyError: # no TRACKNUMBER/DISCNUMBER field found pass # no TRACKTOTAL/DISCTOTAL fields # or no integer values in those fields, # and no slashed TRACKNUMBER/DISCNUMBER values # or no integer values in those fields, # so append a TRACKTOTAL/DISCTOTAL field self[key] = new_values + [u"%d" % (value)] elif (attr in self.ATTRIBUTE_MAP.keys()): key = self.ATTRIBUTE_MAP[attr] try: current_values = self[key] # try to leave subsequent fields with the same key as-is self[key] = [u"%s" % (value)] + current_values[1:] except KeyError: # no current field with the same key, so add a new one self[key] = [u"%s" % (value)] elif (attr in self.FIELDS): # attribute is supported by MetaData # but not supported by VorbisComment # so ignore it pass else: # attribute is not MetaData-specific, so set as-is MetaData.__setattr__(self, attr, value)
def __setattr__(self, attr, value): if (attr in self.ATTRIBUTE_MAP): if (value is not None): import re if (attr == 'track_number'): try: self[b'Track'].data = (re.sub( r'\d+', u"%d" % (value), self[b'Track'].__unicode__(), 1).encode("utf-8")) except KeyError: self[b'Track'] = self.ITEM.string( b'Track', __number_pair__(value, self.track_total)) elif (attr == 'track_total'): try: if (re.search( r'/\D*\d+', self[b'Track'].__unicode__()) is not None): self[b'Track'].data = (re.sub( r'(/\D*)(\d+)', u"\\g<1>" + u"%d" % (value), self[b'Track'].__unicode__(), 1).encode("utf-8")) else: self[b'Track'].data = (u"%s/%d" % ( self[b'Track'].__unicode__(), value)).encode("utf-8") except KeyError: self[b'Track'] = self.ITEM.string( b'Track', __number_pair__(self.track_number, value)) elif (attr == 'album_number'): try: self[b'Media'].data = (re.sub( r'\d+', u"%d" % (value), self[b'Media'].__unicode__(), 1).encode("utf-8")) except KeyError: self[b'Media'] = self.ITEM.string( b'Media', __number_pair__(value, self.album_total)) elif (attr == 'album_total'): try: if (re.search( r'/\D*\d+', self[b'Media'].__unicode__()) is not None): self[b'Media'].data = (re.sub( r'(/\D*)(\d+)', u"\\g<1>" + u"%d" % (value), self[b'Media'].__unicode__(), 1).encode("utf-8")) else: self[b'Media'].data = (u"%s/%d" % ( self[b'Media'].__unicode__(), value)).encode("utf-8") except KeyError: self[b'Media'] = self.ITEM.string( b'Media', __number_pair__(self.album_number, value)) else: self[self.ATTRIBUTE_MAP[attr]] = self.ITEM.string( self.ATTRIBUTE_MAP[attr], value) else: delattr(self, attr) else: MetaData.__setattr__(self, attr, value)
def __setattr__(self, attr, value): if (attr in self.ATTRIBUTE_MAP): if (value is not None): import re if (attr == 'track_number'): try: self['Track'].data = re.sub(r'\d+', str(int(value)), self['Track'].data, 1) except KeyError: self['Track'] = self.ITEM.string( 'Track', __number_pair__(value, self.track_total)) elif (attr == 'track_total'): try: if (re.search(r'/\D*\d+', self['Track'].data) is not None): self['Track'].data = re.sub( r'(/\D*)(\d+)', "\\g<1>" + str(int(value)), self['Track'].data, 1) else: self['Track'].data = "%s/%d" % ( self['Track'].data, value) except KeyError: self['Track'] = self.ITEM.string( 'Track', __number_pair__(self.track_number, value)) elif (attr == 'album_number'): try: self['Media'].data = re.sub(r'\d+', str(int(value)), self['Media'].data, 1) except KeyError: self['Media'] = self.ITEM.string( 'Media', __number_pair__(value, self.album_total)) elif (attr == 'album_total'): try: if (re.search(r'/\D*\d+', self['Media'].data) is not None): self['Media'].data = re.sub( r'(/\D*)(\d+)', "\\g<1>" + str(int(value)), self['Media'].data, 1) else: self['Media'].data = "%s/%d" % ( self['Media'].data, value) except KeyError: self['Media'] = self.ITEM.string( 'Media', __number_pair__(self.album_number, value)) else: self[self.ATTRIBUTE_MAP[attr]] = self.ITEM.string( self.ATTRIBUTE_MAP[attr], value) else: delattr(self, attr) else: MetaData.__setattr__(self, attr, value)
def __setattr__(self, attr, value): # updates the first matching field for the given attribute # in our list of comment strings def has_number(unicode_string): import re return re.search(r'\d+', unicode_string) is not None def swap_number(unicode_value, new_number): import re return re.sub(r'\d+', u"{:d}".format(new_number), unicode_value, 1) if (attr in self.FIELDS) and (value is None): # setting any value to None is equivilent to deleting it # in this high-level implementation delattr(self, attr) elif attr in self.ATTRIBUTE_MAP: key = self.ATTRIBUTE_MAP[attr] if attr in {'track_number', 'album_number'}: try: current_values = self[key] for i in range(len(current_values)): current_value = current_values[i] if u"/" not in current_value: if has_number(current_value): current_values[i] = swap_number( current_value, value) self[key] = current_values break else: (first, second) = current_value.split(u"/", 1) if has_number(first): current_values[i] = u"/".join( [swap_number(first, value), second]) self[key] = current_values break else: # no integer field matching key, so add new one self[key] = current_values + [u"{:d}".format(value)] except KeyError: # no current field with key, so add new one self[key] = [u"{:d}".format(value)] elif attr in {'track_total', 'album_total'}: # look for standalone TRACKTOTAL/DISCTOTAL field try: current_values = self[key] for i in range(len(current_values)): current_value = current_values[i] if has_number(current_value): current_values[i] = swap_number( current_value, value) self[key] = current_values return except KeyError: current_values = [] # no TRACKTOTAL/DISCTOTAL field # or none of them contain an integer, # so look for slashed TRACKNUMBER/DISCNUMBER values try: new_key = { "track_total": u"TRACKNUMBER", "album_total": u"DISCNUMBER" }[attr] slashed_values = self[new_key] for i in range(len(slashed_values)): current_value = slashed_values[i] if u"/" in current_value: (first, second) = current_value.split(u"/", 1) if has_number(second): slashed_values[i] = u"/".join( [first, swap_number(second, value)]) self[new_key] = slashed_values return except KeyError: # no TRACKNUMBER/DISCNUMBER field found pass # no slashed TRACKNUMBER/DISCNUMBER values either # so append a TRACKTOTAL/DISCTOTAL field self[key] = current_values + [u"{:d}".format(value)] elif attr == "compilation": self[key] = [u"1" if value else u"0"] else: # leave subsequent fields with the same key as-is try: current_values = self[key] self[key] = [value] + current_values[1:] except KeyError: # no current field with key, so add new one self[key] = [value] elif attr in self.FIELDS: # attribute is supported by MetaData # but not supported by VorbisComment # so ignore it pass else: # attribute is not MetaData-specific, so set as-is MetaData.__setattr__(self, attr, value)
def __init__(self, track_name=u"", artist_name=u"", album_name=u"", year=u"", comment=u"", track_number=0, genre=0): """fields are as follows: | field | length | |--------------+--------| | track_name | 30 | | artist_name | 30 | | album_name | 30 | | year | 4 | | comment | 28 | | track_number | 1 | | genre | 1 | |--------------+--------| track_name, artist_name, album_name, year and comment are unicode strings track_number and genre are integers """ if len(track_name) > 30: raise ValueError("track_name cannot be longer than 30 characters") if len(artist_name) > 30: raise ValueError("artist_name cannot be longer than 30 characters") if len(album_name) > 30: raise ValueError("album_name cannot be longer than 30 characters") if len(year) > 4: raise ValueError("year cannot be longer than 4 characters") if len(comment) > 28: raise ValueError("comment cannot be longer than 28 characters") MetaData.__setattr__(self, "__track_name__", track_name) MetaData.__setattr__(self, "__artist_name__", artist_name) MetaData.__setattr__(self, "__album_name__", album_name) MetaData.__setattr__(self, "__year__", year) MetaData.__setattr__(self, "__comment__", comment) MetaData.__setattr__(self, "__track_number__", track_number) MetaData.__setattr__(self, "__genre__", genre)