Skip to content

Commit

Permalink
Merge pull request #58 from jcassette/bitratemode
Browse files Browse the repository at this point in the history
Add the properties bitrate_mode, encoder_info and encoder_settings
  • Loading branch information
sampsyo authored Nov 22, 2021
2 parents 055c490 + 989a548 commit badcd22
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 5 deletions.
4 changes: 2 additions & 2 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
copyright = '2016, the beets project'
author = 'the beets project'

version = '0.5'
release = '0.5.0'
version = '0.9'
release = '0.9.0'

pygments_style = 'sphinx'
htmlhelp_basename = 'mediafiledoc'
Expand Down
8 changes: 7 additions & 1 deletion docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ The metadata schema is generally based on MusicBrainz' schema with similar namin
* ``lyrics``, ``copyright``, ``url``
* calculated metadata like ``bpm`` (beats per minute) and ``r128_track_gain`` (ReplayGain),
* embedded images (e.g. album art),
* file metadata like ``bitrate`` and ``length``.
* file metadata like ``samplerate``, ``bitdepth``, ``channels``, ``bitrate``, ``bitrate_mode``, ``encoder_info``, ``encoder_settings`` and ``length``.

Compatibility
-------------
Expand Down Expand Up @@ -135,6 +135,12 @@ To copy tags from one MediaFile to another:
Changelog
---------
v0.9.0
''''''
- Add the properties ``bitrate_mode``, ``encoder_info`` and
``encoder_settings``.
v0.8.1
''''''
Expand Down
42 changes: 40 additions & 2 deletions mediafile.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@

import mutagen
import mutagen.id3
import mutagen.mp3
import mutagen.mp4
import mutagen.flac
import mutagen.asf
Expand All @@ -58,7 +59,7 @@
import traceback


__version__ = '0.8.1'
__version__ = '0.9.0'
__all__ = ['UnreadableFileError', 'FileTypeError', 'MediaFile']

log = logging.getLogger(__name__)
Expand Down Expand Up @@ -1721,7 +1722,8 @@ def readable_fields(cls):
for property in cls.fields():
yield property
for property in ('length', 'samplerate', 'bitdepth', 'bitrate',
'channels', 'format'):
'bitrate_mode', 'channels', 'encoder_info',
'encoder_settings', 'format'):
yield property

@classmethod
Expand Down Expand Up @@ -2326,6 +2328,42 @@ def bitrate(self):
return 0
return int(self.filesize * 8 / self.length)

@property
def bitrate_mode(self):
"""The mode of the bitrate used in the audio coding
(a string, eg. "CBR", "VBR" or "ABR").
Only available for the MP3 file format (empty where unavailable).
"""
if hasattr(self.mgfile.info, 'bitrate_mode'):
return {
mutagen.mp3.BitrateMode.CBR: 'CBR',
mutagen.mp3.BitrateMode.VBR: 'VBR',
mutagen.mp3.BitrateMode.ABR: 'ABR',
}.get(self.mgfile.info.bitrate_mode, '')
else:
return ''

@property
def encoder_info(self):
"""The name and/or version of the encoder used
(a string, eg. "LAME 3.97.0").
Only available for some formats (empty where unavailable).
"""
if hasattr(self.mgfile.info, 'encoder_info'):
return self.mgfile.info.encoder_info
else:
return ''

@property
def encoder_settings(self):
"""A guess of the settings used for the encoder (a string, eg. "-V2").
Only available for the MP3 file format (empty where unavailable).
"""
if hasattr(self.mgfile.info, 'encoder_settings'):
return self.mgfile.info.encoder_settings
else:
return ''

@property
def format(self):
"""A string describing the file format/codec."""
Expand Down
Binary file added test/rsrc/cbr.mp3
Binary file not shown.
51 changes: 51 additions & 0 deletions test/test_mediafile.py
Original file line number Diff line number Diff line change
Expand Up @@ -761,6 +761,9 @@ class MP3Test(ReadWriteTestBase, PartialTestMixin,
audio_properties = {
'length': 1.0,
'bitrate': 80000,
'bitrate_mode': '',
'encoder_info': '',
'encoder_settings': '',
'format': 'MP3',
'samplerate': 44100,
'bitdepth': 0,
Expand All @@ -771,13 +774,28 @@ def test_unknown_apic_type(self):
mediafile = self._mediafile_fixture('image_unknown_type')
self.assertEqual(mediafile.images[0].type, ImageType.other)

def test_bitrate_mode(self):
mediafile = self._mediafile_fixture('cbr')
self.assertEqual(mediafile.bitrate_mode, 'CBR')

def test_encoder_info(self):
mediafile = self._mediafile_fixture('cbr')
self.assertEqual(mediafile.encoder_info, 'LAME 3.100.0+')

def test_encoder_settings(self):
mediafile = self._mediafile_fixture('cbr')
self.assertEqual(mediafile.encoder_settings, '-b 80')


class MP4Test(ReadWriteTestBase, PartialTestMixin,
ImageStructureTestMixin, unittest.TestCase):
extension = 'm4a'
audio_properties = {
'length': 1.0,
'bitrate': 64000,
'bitrate_mode': '',
'encoder_info': '',
'encoder_settings': '',
'format': 'AAC',
'samplerate': 44100,
'bitdepth': 16,
Expand All @@ -799,6 +817,9 @@ class AlacTest(ReadWriteTestBase, unittest.TestCase):
audio_properties = {
'length': 1.0,
'bitrate': 21830,
'bitrate_mode': '',
'encoder_info': '',
'encoder_settings': '',
# 'format': 'ALAC',
'samplerate': 44100,
'bitdepth': 16,
Expand All @@ -811,6 +832,9 @@ class MusepackTest(ReadWriteTestBase, unittest.TestCase):
audio_properties = {
'length': 1.0,
'bitrate': 24023,
'bitrate_mode': '',
'encoder_info': '',
'encoder_settings': '',
'format': u'Musepack',
'samplerate': 44100,
'bitdepth': 0,
Expand All @@ -824,6 +848,9 @@ class WMATest(ReadWriteTestBase, ExtendedImageStructureTestMixin,
audio_properties = {
'length': 1.0,
'bitrate': 128000,
'bitrate_mode': '',
'encoder_info': '',
'encoder_settings': '',
'format': u'Windows Media',
'samplerate': 44100,
'bitdepth': 0,
Expand Down Expand Up @@ -852,6 +879,9 @@ class OggTest(ReadWriteTestBase, ExtendedImageStructureTestMixin,
audio_properties = {
'length': 1.0,
'bitrate': 48000,
'bitrate_mode': '',
'encoder_info': '',
'encoder_settings': '',
'format': u'OGG',
'samplerate': 44100,
'bitdepth': 0,
Expand Down Expand Up @@ -896,6 +926,9 @@ class FlacTest(ReadWriteTestBase, PartialTestMixin,
audio_properties = {
'length': 1.0,
'bitrate': 108688,
'bitrate_mode': '',
'encoder_info': '',
'encoder_settings': '',
'format': u'FLAC',
'samplerate': 44100,
'bitdepth': 16,
Expand All @@ -909,6 +942,9 @@ class ApeTest(ReadWriteTestBase, ExtendedImageStructureTestMixin,
audio_properties = {
'length': 1.0,
'bitrate': 112608,
'bitrate_mode': '',
'encoder_info': '',
'encoder_settings': '',
'format': u'APE',
'samplerate': 44100,
'bitdepth': 16,
Expand All @@ -921,6 +957,9 @@ class WavpackTest(ReadWriteTestBase, unittest.TestCase):
audio_properties = {
'length': 1.0,
'bitrate': 109312,
'bitrate_mode': '',
'encoder_info': '',
'encoder_settings': '',
'format': u'WavPack',
'samplerate': 44100,
'bitdepth': 16 if mutagen.version >= (1, 45, 0) else 0,
Expand All @@ -933,6 +972,9 @@ class OpusTest(ReadWriteTestBase, unittest.TestCase):
audio_properties = {
'length': 1.0,
'bitrate': 66792,
'bitrate_mode': '',
'encoder_info': '',
'encoder_settings': '',
'format': u'Opus',
'samplerate': 48000,
'bitdepth': 0,
Expand All @@ -945,6 +987,9 @@ class AIFFTest(ReadWriteTestBase, unittest.TestCase):
audio_properties = {
'length': 1.0,
'bitrate': 705600,
'bitrate_mode': '',
'encoder_info': '',
'encoder_settings': '',
'format': u'AIFF',
'samplerate': 44100,
'bitdepth': 16,
Expand All @@ -957,6 +1002,9 @@ class WAVETest(ReadWriteTestBase, unittest.TestCase):
audio_properties = {
'length': 1.0,
'bitrate': 88200,
'bitrate_mode': '',
'encoder_info': '',
'encoder_settings': '',
'format': u'WAVE',
'samplerate': 44100,
'bitdepth': 16,
Expand Down Expand Up @@ -1033,6 +1081,9 @@ class DSFTest(ReadWriteTestBase, unittest.TestCase):
audio_properties = {
'length': 0.01,
'bitrate': 11289600,
'bitrate_mode': '',
'encoder_info': '',
'encoder_settings': '',
'format': u'DSD Stream File',
'samplerate': 5644800,
'bitdepth': 1,
Expand Down

0 comments on commit badcd22

Please sign in to comment.