Skip to content

add support for fetching loudness levels of audio streams #1529

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

bastiaanterhorst
Copy link

@bastiaanterhorst bastiaanterhorst commented Jun 12, 2025

Description

This adds support for fetching loudness levels. This is used in Plexamp and Dash to show a waveform of the currently playing track. See here for a screenshot of what I mean.

Type of change

  • New feature (non-breaking change which adds functionality)
  • This change requires a documentation update

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated the docstring for new or existing methods
  • I have added tests when applicable

I have not added tests (yet). Python is not my first language and I'm having a bit of trouble wrapping my head around how the test suite works.

@bastiaanterhorst
Copy link
Author

bastiaanterhorst commented Jun 12, 2025

Added a basic test that fetches a custom amount of levels and checks the returned list length.

@bastiaanterhorst
Copy link
Author

@JonnyWong16 no interest in this patch?

@@ -433,10 +433,34 @@ def setSelected(self):
"""
return self._parent().setSelectedAudioStream(self)

def levels(self, subSample=128):
""" Load time series loudness levels.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
""" Load time series loudness levels.
""" Returns a list of :class:`~plexapi.media.Level` objects for this AudioStream.
Only available for Tracks which have been analyzed for loudness.

""" Load time series loudness levels.

Attributes:
subSample: (int): the number of loudness segments to load
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
subSample: (int): the number of loudness segments to load
subSample (int): The number of loudness samples to return. Default 128.

Comment on lines +442 to +447

key = f'/library/streams/{self.id}/levels'
params = {'subsample': subSample}

levels = self.fetchItems(ekey=key, cls=Level, params=params)
return levels
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
key = f'/library/streams/{self.id}/levels'
params = {'subsample': subSample}
levels = self.fetchItems(ekey=key, cls=Level, params=params)
return levels
key = f'/library/streams/{self.id}/levels'
params = {'subsample': subSample}
return self.fetchItems(key, params=params)

Comment on lines +453 to +463
@utils.registerPlexObject
class Level(PlexObject):
""" Represents an instance of loudness for an audio stream.

Attributes:
loudness (float): loudness level in db
"""
def _loadData(self, data):
""" Load attribute values from Plex XML response. """
self.loudness = data.attrib.get('v')

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move to the bottom of the file.

Suggested change
@utils.registerPlexObject
class Level(PlexObject):
""" Represents an instance of loudness for an audio stream.
Attributes:
loudness (float): loudness level in db
"""
def _loadData(self, data):
""" Load attribute values from Plex XML response. """
self.loudness = data.attrib.get('v')
@utils.registerPlexObject
class Level(PlexObject):
""" Represents a single loudness Level sample for an AudioStream.
Attributes:
loudness (float): Loudness level value
"""
def _loadData(self, data):
""" Load attribute values from Plex XML response. """
self.loudness = utils.cast(float, data.attrib.get('v'))

@@ -397,6 +397,7 @@ def test_audio_Track_attrs(album):
assert stream.lra is None
assert stream.peak is None
assert stream.startRamp is None
assert len(stream.levels(subSample=32)) == 32
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
assert len(stream.levels(subSample=32)) == 32
if stream.loudness is not None:
assert len(stream.levels(subSample=32)) == 32

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants