Skip to content

Commit 9e8fcb5

Browse files
Add sonicallySimilar method to Audio class (#1288)
* Add sonicallySimilar method to Audio class closes #1183 * Add type hinting for method - fixes import error * Apply review suggestions Co-Authors @JonnyWong16 * Add optional parameters to sonicallySimilar method - makes it so that params can be None and use the server default * add test for `sonicallySimilar` * Refactor test to check type of elements * Apply suggestions Co-authored-by: JonnyWong16 <[email protected]> * Add authentication to sonicallySimilar test in test_audio.py * fix flake8 --------- Co-authored-by: JonnyWong16 <[email protected]>
1 parent 2f888df commit 9e8fcb5

File tree

2 files changed

+51
-0
lines changed

2 files changed

+51
-0
lines changed

plexapi/audio.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
from pathlib import Path
44
from urllib.parse import quote_plus
55

6+
from typing import Any, Dict, List, Optional, TypeVar
7+
68
from plexapi import media, utils
79
from plexapi.base import Playable, PlexPartialObject, PlexHistory, PlexSession
810
from plexapi.exceptions import BadRequest
@@ -14,6 +16,9 @@
1416
from plexapi.playlist import Playlist
1517

1618

19+
TAudio = TypeVar("TAudio", bound="Audio")
20+
21+
1722
class Audio(PlexPartialObject, PlayedUnplayedMixin):
1823
""" Base class for all audio objects including :class:`~plexapi.audio.Artist`,
1924
:class:`~plexapi.audio.Album`, and :class:`~plexapi.audio.Track`.
@@ -22,6 +27,7 @@ class Audio(PlexPartialObject, PlayedUnplayedMixin):
2227
addedAt (datetime): Datetime the item was added to the library.
2328
art (str): URL to artwork image (/library/metadata/<ratingKey>/art/<artid>).
2429
artBlurHash (str): BlurHash string for artwork image.
30+
distance (float): Sonic Distance of the item from the seed item.
2531
fields (List<:class:`~plexapi.media.Field`>): List of field objects.
2632
guid (str): Plex GUID for the artist, album, or track (plex://artist/5d07bcb0403c64029053ac4c).
2733
index (int): Plex index number (often the track number).
@@ -53,6 +59,7 @@ def _loadData(self, data):
5359
self.addedAt = utils.toDatetime(data.attrib.get('addedAt'))
5460
self.art = data.attrib.get('art')
5561
self.artBlurHash = data.attrib.get('artBlurHash')
62+
self.distance = utils.cast(float, data.attrib.get('distance'))
5663
self.fields = self.findItems(data, media.Field)
5764
self.guid = data.attrib.get('guid')
5865
self.index = utils.cast(int, data.attrib.get('index'))
@@ -125,6 +132,37 @@ def sync(self, bitrate, client=None, clientId=None, limit=None, title=None):
125132

126133
return myplex.sync(sync_item, client=client, clientId=clientId)
127134

135+
def sonicallySimilar(
136+
self: TAudio,
137+
limit: Optional[int] = None,
138+
maxDistance: Optional[float] = None,
139+
**kwargs,
140+
) -> List[TAudio]:
141+
"""Returns a list of sonically similar audio items.
142+
143+
Parameters:
144+
limit (int): Maximum count of items to return. Default 50 (server default)
145+
maxDistance (float): Maximum distance between tracks, 0.0 - 1.0. Default 0.25 (server default).
146+
**kwargs: Additional options passed into :func:`~plexapi.base.PlexObject.fetchItems`.
147+
148+
Returns:
149+
List[:class:`~plexapi.audio.Audio`]: list of sonically similar audio items.
150+
"""
151+
152+
key = f"{self.key}/nearest"
153+
params: Dict[str, Any] = {}
154+
if limit is not None:
155+
params['limit'] = limit
156+
if maxDistance is not None:
157+
params['maxDistance'] = maxDistance
158+
key += utils.joinArgs(params)
159+
160+
return self.fetchItems(
161+
key,
162+
cls=self.__class__,
163+
**kwargs,
164+
)
165+
128166

129167
@utils.registerPlexObject
130168
class Artist(

tests/test_audio.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,19 @@ def test_audio_Audio_section(artist, album, track):
443443
assert track.section().key == album.section().key == artist.section().key
444444

445445

446+
@pytest.mark.authenticated
447+
def test_audio_Audio_sonicallySimilar(artist):
448+
similar_audio = artist.sonicallySimilar()
449+
assert isinstance(similar_audio, list)
450+
assert all(isinstance(i, type(artist)) for i in similar_audio)
451+
452+
similar_audio = artist.sonicallySimilar(limit=1)
453+
assert len(similar_audio) <= 1
454+
455+
similar_audio = artist.sonicallySimilar(maxDistance=0.1)
456+
assert all(i.distance <= 0.1 for i in similar_audio)
457+
458+
446459
def test_audio_Artist_download(monkeydownload, tmpdir, artist):
447460
total = len(artist.tracks())
448461
filepaths = artist.download(savepath=str(tmpdir))

0 commit comments

Comments
 (0)