diff --git a/beets/mediafile.py b/beets/mediafile.py index a6fcbb64e..724802e0b 100644 --- a/beets/mediafile.py +++ b/beets/mediafile.py @@ -8,7 +8,7 @@ # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: -# +# # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. @@ -106,7 +106,7 @@ def _safe_cast(out_type, val): else: try: # Should work for strings, bools, ints: - return bool(int(val)) + return bool(int(val)) except ValueError: return False @@ -181,7 +181,6 @@ class Packed(object): """Makes a packed list of values subscriptable. To access the packed output after making changes, use packed_thing.items. """ - def __init__(self, items, packstyle, none_val=0, out_type=int): """Create a Packed object for subscripting the packed values in items. The items are packed using packstyle, which is a value @@ -193,11 +192,11 @@ class Packed(object): self.packstyle = packstyle self.none_val = none_val self.out_type = out_type - + def __getitem__(self, index): if not isinstance(index, int): raise TypeError('index must be an integer') - + if self.items is None: return self.none_val @@ -206,7 +205,7 @@ class Packed(object): # Remove time information from dates. Usually delimited by # a "T" or a space. items = re.sub(r'[Tt ].*$', '', unicode(items)) - + # transform from a string packing into a list we can index into if self.packstyle == packing.SLASHED: seq = unicode(items).split('/') @@ -214,17 +213,17 @@ class Packed(object): seq = unicode(items).split('-') elif self.packstyle == packing.TUPLE: seq = items # tuple: items is already indexable - + try: out = seq[index] except: out = None - + if out is None or out == self.none_val or out == '': return _safe_cast(self.out_type, self.none_val) else: return _safe_cast(self.out_type, out) - + def __setitem__(self, index, value): if self.packstyle in (packing.SLASHED, packing.TUPLE): # SLASHED and TUPLE are always two-item packings @@ -232,7 +231,7 @@ class Packed(object): else: # DATE can have up to three fields length = 3 - + # make a list of the items we'll pack new_items = [] for i in range(length): @@ -241,7 +240,7 @@ class Packed(object): else: next_item = self[i] new_items.append(next_item) - + if self.packstyle == packing.DATE: # Truncate the items wherever we reach an invalid (none) # entry. This prevents dates like 2008-00-05. @@ -249,7 +248,7 @@ class Packed(object): if item == self.none_val or item is None: del(new_items[i:]) # truncate break - + if self.packstyle == packing.SLASHED: self.items = '/'.join(map(unicode, new_items)) elif self.packstyle == packing.DATE: @@ -260,7 +259,7 @@ class Packed(object): self.items = '-'.join(elems) elif self.packstyle == packing.TUPLE: self.items = new_items - + # The field itself. @@ -270,7 +269,6 @@ class MediaField(object): can be unicode, int, or bool. id3, mp4, and flac are StorageStyle instances parameterizing the field's storage for each type. """ - def __init__(self, out_type = unicode, **kwargs): """Creates a new MediaField. - out_type: The field's semantic (exterior) type. @@ -283,7 +281,7 @@ class MediaField(object): raise TypeError('MediaField constructor must have keyword ' 'arguments mp3, mp4, and etc') self.styles = kwargs - + def _fetchdata(self, obj, style): """Get the value associated with this descriptor's field stored with the given StorageStyle. Unwraps from a list if necessary. @@ -305,18 +303,18 @@ class MediaField(object): frame = obj.mgfile[style.key] except KeyError: return None - + entry = getattr(frame, style.id3_frame_field) - - else: # Not MP3. + + else: # Not MP3. try: entry = obj.mgfile[style.key] except KeyError: return None - - # possibly index the list + + # Possibly index the list. if style.list_elem: - if entry: # List must have at least one value. + if entry: # List must have at least one value. # Handle Mutagen bugs when reading values (#356). try: return entry[0] @@ -328,21 +326,23 @@ class MediaField(object): return None else: return entry - + def _storedata(self, obj, val, style): """Store val for this descriptor's field in the tag dictionary according to the provided StorageStyle. Store it as a single-item list if necessary. """ - # wrap as a list if necessary - if style.list_elem: out = [val] - else: out = val - + # Wrap as a list if necessary. + if style.list_elem: + out = [val] + else: + out = val + if obj.type == 'mp3': # Try to match on "desc" field. if style.id3_desc is not None: frames = obj.mgfile.tags.getall(style.key) - + # try modifying in place found = False for frame in frames: @@ -350,56 +350,56 @@ class MediaField(object): setattr(frame, style.id3_frame_field, out) found = True break - + # need to make a new frame? if not found: - assert isinstance(style.id3_frame_field, str) # Keyword. + assert isinstance(style.id3_frame_field, str) # Keyword. frame = mutagen.id3.Frames[style.key]( encoding=3, desc=style.id3_desc, **{style.id3_frame_field: val} ) obj.mgfile.tags.add(frame) - + # Try to match on "owner" field. elif style.key.startswith('UFID:'): owner = style.key.split(':', 1)[1] frames = obj.mgfile.tags.getall(style.key) - + for frame in frames: # Replace existing frame data. if frame.owner == owner: setattr(frame, style.id3_frame_field, val) else: # New frame. - assert isinstance(style.id3_frame_field, str) # Keyword. - frame = mutagen.id3.UFID(owner=owner, + assert isinstance(style.id3_frame_field, str) # Keyword. + frame = mutagen.id3.UFID(owner=owner, **{style.id3_frame_field: val}) obj.mgfile.tags.setall('UFID', [frame]) - + # Just replace based on key. else: - assert isinstance(style.id3_frame_field, str) # Keyword. + assert isinstance(style.id3_frame_field, str) # Keyword. frame = mutagen.id3.Frames[style.key](encoding = 3, **{style.id3_frame_field: val}) obj.mgfile.tags.setall(style.key, [frame]) - + else: # Not MP3. obj.mgfile[style.key] = out - + def _styles(self, obj): if obj.type in ('mp3', 'mp4'): styles = self.styles[obj.type] else: styles = self.styles['etc'] # sane styles - + # Make sure we always return a list of styles, even when given # a single style for convenience. if isinstance(styles, StorageStyle): return [styles] else: return styles - + def __get__(self, obj, owner): """Retrieve the value of this metadata field. """ @@ -413,7 +413,7 @@ class MediaField(object): out = self._fetchdata(obj, style) if out: break - + if style.packing: out = Packed(out, style.packing)[style.pack_pos] @@ -421,9 +421,9 @@ class MediaField(object): if obj.type == 'mp4' and style.key.startswith('----:') and \ isinstance(out, str): out = out.decode('utf8') - + return _safe_cast(self.out_type, out) - + def __set__(self, obj, val): """Set the value of this metadata field. """ @@ -433,15 +433,15 @@ class MediaField(object): return for style in styles: - + if style.packing: p = Packed(self._fetchdata(obj, style), style.packing) p[style.pack_pos] = val out = p.items - + else: # unicode, integer, or boolean scalar out = val - + # deal with Nones according to abstract type if present if out is None: if self.out_type == int: @@ -451,7 +451,7 @@ class MediaField(object): elif self.out_type == unicode: out = u'' # We trust that packed values are handled above. - + # Convert to correct storage type (irrelevant for # packed values). if style.as_type == unicode: @@ -472,7 +472,7 @@ class MediaField(object): out = int(out) elif style.as_type in (bool, str): out = style.as_type(out) - + # MPEG-4 "freeform" (----) frames must be encoded as UTF-8 # byte strings. if obj.type == 'mp4' and style.key.startswith('----:') and \ @@ -494,7 +494,7 @@ class CompositeDateField(object): self.year_field = year_field self.month_field = month_field self.day_field = day_field - + def __get__(self, obj, owner): """Return a datetime.date object whose components indicating the smallest valid date whose components are at least as large as @@ -509,9 +509,9 @@ class CompositeDateField(object): max(self.month_field.__get__(obj, owner), 1), max(self.day_field.__get__(obj, owner), 1) ) - except ValueError: # Out of range values. + except ValueError: # Out of range values. return datetime.date.min - + def __set__(self, obj, val): """Set the year, month, and day fields to match the components of the provided datetime.date object. @@ -676,13 +676,12 @@ class MediaFile(object): """Represents a multimedia file on disk and provides access to its metadata. """ - def __init__(self, path): """Constructs a new MediaFile reflecting the file at path. May throw UnreadableFileError. """ self.path = path - + unreadable_exc = ( mutagen.mp3.HeaderNotFoundError, mutagen.flac.FLACNoHeaderError, @@ -723,258 +722,209 @@ class MediaFile(object): else: raise FileTypeError('file type %s unsupported by MediaFile' % type(self.mgfile).__name__) - + # add a set of tags if it's missing if self.mgfile.tags is None: self.mgfile.add_tags() - + def save(self): self.mgfile.save() - - - #### field definitions #### - + + + # Field definitions. + title = MediaField( - mp3 = StorageStyle('TIT2'), - mp4 = StorageStyle("\xa9nam"), - etc = StorageStyle('title'), - ) + mp3 = StorageStyle('TIT2'), + mp4 = StorageStyle("\xa9nam"), + etc = StorageStyle('title'), + ) artist = MediaField( - mp3 = StorageStyle('TPE1'), - mp4 = StorageStyle("\xa9ART"), - etc = StorageStyle('artist'), - ) + mp3 = StorageStyle('TPE1'), + mp4 = StorageStyle("\xa9ART"), + etc = StorageStyle('artist'), + ) album = MediaField( - mp3 = StorageStyle('TALB'), - mp4 = StorageStyle("\xa9alb"), - etc = StorageStyle('album'), - ) + mp3 = StorageStyle('TALB'), + mp4 = StorageStyle("\xa9alb"), + etc = StorageStyle('album'), + ) genre = MediaField( - mp3 = StorageStyle('TCON'), - mp4 = StorageStyle("\xa9gen"), - etc = StorageStyle('genre'), - ) + mp3 = StorageStyle('TCON'), + mp4 = StorageStyle("\xa9gen"), + etc = StorageStyle('genre'), + ) composer = MediaField( - mp3 = StorageStyle('TCOM'), - mp4 = StorageStyle("\xa9wrt"), - etc = StorageStyle('composer'), - ) + mp3 = StorageStyle('TCOM'), + mp4 = StorageStyle("\xa9wrt"), + etc = StorageStyle('composer'), + ) grouping = MediaField( - mp3 = StorageStyle('TIT1'), - mp4 = StorageStyle("\xa9grp"), - etc = StorageStyle('grouping'), - ) + mp3 = StorageStyle('TIT1'), + mp4 = StorageStyle("\xa9grp"), + etc = StorageStyle('grouping'), + ) year = MediaField(out_type=int, - mp3 = StorageStyle('TDRC', - packing = packing.DATE, - pack_pos = 0), - mp4 = StorageStyle("\xa9day", - packing = packing.DATE, - pack_pos = 0), - etc = [StorageStyle('date', - packing = packing.DATE, - pack_pos = 0), - StorageStyle('year')] - ) + mp3 = StorageStyle('TDRC', packing=packing.DATE, pack_pos=0), + mp4 = StorageStyle("\xa9day", packing=packing.DATE, pack_pos=0), + etc = [StorageStyle('date', packing=packing.DATE, pack_pos=0), + StorageStyle('year')] + ) month = MediaField(out_type=int, - mp3 = StorageStyle('TDRC', - packing = packing.DATE, - pack_pos = 1), - mp4 = StorageStyle("\xa9day", - packing = packing.DATE, - pack_pos = 1), - etc = StorageStyle('date', - packing = packing.DATE, - pack_pos = 1) - ) + mp3 = StorageStyle('TDRC', packing=packing.DATE, pack_pos=1), + mp4 = StorageStyle("\xa9day", packing=packing.DATE, pack_pos=1), + etc = StorageStyle('date', packing=packing.DATE, pack_pos=1), + ) day = MediaField(out_type=int, - mp3 = StorageStyle('TDRC', - packing = packing.DATE, - pack_pos = 2), - mp4 = StorageStyle("\xa9day", - packing = packing.DATE, - pack_pos = 2), - etc = StorageStyle('date', - packing = packing.DATE, - pack_pos = 2) - ) + mp3 = StorageStyle('TDRC', packing=packing.DATE, pack_pos=2), + mp4 = StorageStyle("\xa9day", packing=packing.DATE, pack_pos=2), + etc = StorageStyle('date', packing=packing.DATE, pack_pos=2), + ) date = CompositeDateField(year, month, day) - track = MediaField(out_type = int, - mp3 = StorageStyle('TRCK', - packing = packing.SLASHED, - pack_pos = 0), - mp4 = StorageStyle('trkn', - packing = packing.TUPLE, - pack_pos = 0), - etc = [StorageStyle('track'), - StorageStyle('tracknumber')] - ) - tracktotal = MediaField(out_type = int, - mp3 = StorageStyle('TRCK', - packing = packing.SLASHED, - pack_pos = 1), - mp4 = StorageStyle('trkn', - packing = packing.TUPLE, - pack_pos = 1), - etc = [StorageStyle('tracktotal'), - StorageStyle('trackc'), - StorageStyle('totaltracks')] - ) - disc = MediaField(out_type = int, - mp3 = StorageStyle('TPOS', - packing = packing.SLASHED, - pack_pos = 0), - mp4 = StorageStyle('disk', - packing = packing.TUPLE, - pack_pos = 0), - etc = [StorageStyle('disc'), - StorageStyle('discnumber')] - ) - disctotal = MediaField(out_type = int, - mp3 = StorageStyle('TPOS', - packing = packing.SLASHED, - pack_pos = 1), - mp4 = StorageStyle('disk', - packing = packing.TUPLE, - pack_pos = 1), - etc = [StorageStyle('disctotal'), - StorageStyle('discc'), - StorageStyle('totaldiscs')] - ) + track = MediaField(out_type=int, + mp3 = StorageStyle('TRCK', packing=packing.SLASHED, pack_pos=0), + mp4 = StorageStyle('trkn', packing=packing.TUPLE, pack_pos=0), + etc = [StorageStyle('track'), + StorageStyle('tracknumber')] + ) + tracktotal = MediaField(out_type=int, + mp3 = StorageStyle('TRCK', packing=packing.SLASHED, pack_pos=1), + mp4 = StorageStyle('trkn', packing=packing.TUPLE, pack_pos=1), + etc = [StorageStyle('tracktotal'), + StorageStyle('trackc'), + StorageStyle('totaltracks')] + ) + disc = MediaField(out_type=int, + mp3 = StorageStyle('TPOS', packing=packing.SLASHED, pack_pos=0), + mp4 = StorageStyle('disk', packing=packing.TUPLE, pack_pos=0), + etc = [StorageStyle('disc'), + StorageStyle('discnumber')] + ) + disctotal = MediaField(out_type=int, + mp3 = StorageStyle('TPOS', packing=packing.SLASHED, pack_pos=1), + mp4 = StorageStyle('disk', packing=packing.TUPLE, pack_pos=1), + etc = [StorageStyle('disctotal'), + StorageStyle('discc'), + StorageStyle('totaldiscs')] + ) lyrics = MediaField( - mp3 = StorageStyle('USLT', - list_elem = False, - id3_desc = u''), - mp4 = StorageStyle("\xa9lyr"), - etc = StorageStyle('lyrics') - ) + mp3 = StorageStyle('USLT', list_elem=False, id3_desc=u''), + mp4 = StorageStyle("\xa9lyr"), + etc = StorageStyle('lyrics') + ) comments = MediaField( - mp3 = StorageStyle('COMM', id3_desc = u''), - mp4 = StorageStyle("\xa9cmt"), - etc = [StorageStyle('description'), - StorageStyle('comment')] - ) - bpm = MediaField(out_type = int, - mp3 = StorageStyle('TBPM'), - mp4 = StorageStyle('tmpo', as_type = int), - etc = StorageStyle('bpm') - ) - comp = MediaField(out_type = bool, - mp3 = StorageStyle('TCMP'), - mp4 = StorageStyle('cpil', - list_elem = False, - as_type = bool), - etc = StorageStyle('compilation') - ) + mp3 = StorageStyle('COMM', id3_desc=u''), + mp4 = StorageStyle("\xa9cmt"), + etc = [StorageStyle('description'), + StorageStyle('comment')] + ) + bpm = MediaField(out_type=int, + mp3 = StorageStyle('TBPM'), + mp4 = StorageStyle('tmpo', as_type=int), + etc = StorageStyle('bpm'), + ) + comp = MediaField(out_type=bool, + mp3 = StorageStyle('TCMP'), + mp4 = StorageStyle('cpil', list_elem=False, as_type=bool), + etc = StorageStyle('compilation'), + ) albumartist = MediaField( - mp3 = StorageStyle('TPE2'), - mp4 = StorageStyle('aART'), - etc = [StorageStyle('album artist'), - StorageStyle('albumartist')] - ) + mp3 = StorageStyle('TPE2'), + mp4 = StorageStyle('aART'), + etc = [StorageStyle('album artist'), + StorageStyle('albumartist')] + ) albumtype = MediaField( - mp3 = StorageStyle('TXXX', id3_desc=u'MusicBrainz Album Type'), - mp4 = StorageStyle( - '----:com.apple.iTunes:MusicBrainz Album Type'), - etc = StorageStyle('musicbrainz_albumtype') - ) + mp3 = StorageStyle('TXXX', id3_desc=u'MusicBrainz Album Type'), + mp4 = StorageStyle('----:com.apple.iTunes:MusicBrainz Album Type'), + etc = StorageStyle('musicbrainz_albumtype'), + ) label = MediaField( - mp3 = StorageStyle('TPUB'), - mp4 = [StorageStyle('----:com.apple.iTunes:Label'), - StorageStyle('----:com.apple.iTunes:publisher')], - etc = [StorageStyle('label'), - StorageStyle('publisher')] # Traktor - ) + mp3 = StorageStyle('TPUB'), + mp4 = [StorageStyle('----:com.apple.iTunes:Label'), + StorageStyle('----:com.apple.iTunes:publisher')], + etc = [StorageStyle('label'), + StorageStyle('publisher')] # Traktor + ) artist_sort = MediaField( - mp3 = StorageStyle('TSOP'), - mp4 = StorageStyle("soar"), - etc = StorageStyle('ARTISTSORT'), - ) + mp3 = StorageStyle('TSOP'), + mp4 = StorageStyle("soar"), + etc = StorageStyle('ARTISTSORT'), + ) albumartist_sort = MediaField( - mp3 = StorageStyle('TXXX', id3_desc=u'ALBUMARTISTSORT'), - mp4 = StorageStyle("soaa"), - etc = StorageStyle('ALBUMARTISTSORT'), - ) + mp3 = StorageStyle('TXXX', id3_desc=u'ALBUMARTISTSORT'), + mp4 = StorageStyle("soaa"), + etc = StorageStyle('ALBUMARTISTSORT'), + ) # Album art. art = ImageField() # MusicBrainz IDs. mb_trackid = MediaField( - mp3 = StorageStyle('UFID:http://musicbrainz.org', - list_elem = False, - id3_frame_field = 'data'), - mp4 = StorageStyle( - '----:com.apple.iTunes:MusicBrainz Track Id', - as_type=str), - etc = StorageStyle('musicbrainz_trackid') - ) + mp3 = StorageStyle('UFID:http://musicbrainz.org', + list_elem = False, + id3_frame_field = 'data'), + mp4 = StorageStyle('----:com.apple.iTunes:MusicBrainz Track Id', + as_type=str), + etc = StorageStyle('musicbrainz_trackid') + ) mb_albumid = MediaField( - mp3 = StorageStyle('TXXX', id3_desc=u'MusicBrainz Album Id'), - mp4 = StorageStyle( - '----:com.apple.iTunes:MusicBrainz Album Id', - as_type=str), - etc = StorageStyle('musicbrainz_albumid') - ) + mp3 = StorageStyle('TXXX', id3_desc=u'MusicBrainz Album Id'), + mp4 = StorageStyle('----:com.apple.iTunes:MusicBrainz Album Id', + as_type=str), + etc = StorageStyle('musicbrainz_albumid') + ) mb_artistid = MediaField( - mp3 = StorageStyle('TXXX', id3_desc=u'MusicBrainz Artist Id'), - mp4 = StorageStyle( - '----:com.apple.iTunes:MusicBrainz Artist Id', - as_type=str), - etc = StorageStyle('musicbrainz_artistid') - ) + mp3 = StorageStyle('TXXX', id3_desc=u'MusicBrainz Artist Id'), + mp4 = StorageStyle('----:com.apple.iTunes:MusicBrainz Artist Id', + as_type=str), + etc = StorageStyle('musicbrainz_artistid') + ) mb_albumartistid = MediaField( - mp3 = StorageStyle('TXXX', - id3_desc=u'MusicBrainz Album Artist Id'), - mp4 = StorageStyle( - '----:com.apple.iTunes:MusicBrainz Album Artist Id', - as_type=str), - etc = StorageStyle('musicbrainz_albumartistid') - ) + mp3 = StorageStyle('TXXX', + id3_desc=u'MusicBrainz Album Artist Id'), + mp4 = StorageStyle('----:com.apple.iTunes:MusicBrainz Album Artist Id', + as_type=str), + etc = StorageStyle('musicbrainz_albumartistid') + ) # Acoustid fields. acoustid_fingerprint = MediaField( - mp3 = StorageStyle('TXXX', - id3_desc=u'Acoustid Fingerprint'), - mp4 = StorageStyle( - '----:com.apple.iTunes:Acoustid Fingerprint', - as_type=str), - etc = StorageStyle('ACOUSTID_FINGERPRINT') - ) + mp3 = StorageStyle('TXXX', + id3_desc=u'Acoustid Fingerprint'), + mp4 = StorageStyle('----:com.apple.iTunes:Acoustid Fingerprint', + as_type=str), + etc = StorageStyle('ACOUSTID_FINGERPRINT') + ) acoustid_id = MediaField( - mp3 = StorageStyle('TXXX', - id3_desc=u'Acoustid Id'), - mp4 = StorageStyle( - '----:com.apple.iTunes:Acoustid Id', - as_type=str), - etc = StorageStyle('ACOUSTID_ID') - ) + mp3 = StorageStyle('TXXX', + id3_desc=u'Acoustid Id'), + mp4 = StorageStyle('----:com.apple.iTunes:Acoustid Id', + as_type=str), + etc = StorageStyle('ACOUSTID_ID') + ) # ReplayGain fields. rg_track_gain = FloatValueField(2, 'dB', - mp3 = StorageStyle('TXXX', - id3_desc=u'REPLAYGAIN_TRACK_GAIN'), - mp4 = None, - etc = StorageStyle(u'REPLAYGAIN_TRACK_GAIN') - ) + mp3 = StorageStyle('TXXX', id3_desc=u'REPLAYGAIN_TRACK_GAIN'), + mp4 = None, + etc = StorageStyle(u'REPLAYGAIN_TRACK_GAIN') + ) rg_album_gain = FloatValueField(2, 'dB', - mp3 = StorageStyle('TXXX', - id3_desc=u'REPLAYGAIN_ALBUM_GAIN'), - mp4 = None, - etc = StorageStyle(u'REPLAYGAIN_ALBUM_GAIN') - ) + mp3 = StorageStyle('TXXX', id3_desc=u'REPLAYGAIN_ALBUM_GAIN'), + mp4 = None, + etc = StorageStyle(u'REPLAYGAIN_ALBUM_GAIN') + ) rg_track_peak = FloatValueField(6, None, - mp3 = StorageStyle('TXXX', - id3_desc=u'REPLAYGAIN_TRACK_PEAK'), - mp4 = None, - etc = StorageStyle(u'REPLAYGAIN_TRACK_PEAK') - ) + mp3 = StorageStyle('TXXX', id3_desc=u'REPLAYGAIN_TRACK_PEAK'), + mp4 = None, + etc = StorageStyle(u'REPLAYGAIN_TRACK_PEAK') + ) rg_album_peak = FloatValueField(6, None, - mp3 = StorageStyle('TXXX', - id3_desc=u'REPLAYGAIN_ALBUM_PEAK'), - mp4 = None, - etc = StorageStyle(u'REPLAYGAIN_ALBUM_PEAK') - ) + mp3 = StorageStyle('TXXX', id3_desc=u'REPLAYGAIN_ALBUM_PEAK'), + mp4 = None, + etc = StorageStyle(u'REPLAYGAIN_ALBUM_PEAK') + ) @property def length(self): @@ -987,7 +937,7 @@ class MediaFile(object): if hasattr(self.mgfile.info, 'sample_rate'): return self.mgfile.info.sample_rate return 0 - + @property def bitdepth(self): """The number of bits per sample in the audio encoding (an int). @@ -995,7 +945,7 @@ class MediaFile(object): unavailable). """ if hasattr(self.mgfile.info, 'bits_per_sample'): - return self.mgfile.info.bits_per_sample + return self.mgfile.info.bits_per_sample return 0 @property