Skip to content

Commit

Permalink
Implement artist info methods and models
Browse files Browse the repository at this point in the history
  • Loading branch information
kutu-dev committed Sep 17, 2023
1 parent a975353 commit d00a3a4
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 10 deletions.
7 changes: 6 additions & 1 deletion src/knuckles/browsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from .api import Api
from .models.album import Album, AlbumInfo
from .models.artist import Artist
from .models.artist import Artist, ArtistInfo
from .models.genre import Genre
from .models.music_folder import MusicFolder
from .models.song import Song
Expand Down Expand Up @@ -138,3 +138,8 @@ def get_song(self, id: str) -> Song:
response = self.api.request("getSong", {"id": id})["song"]

return Song(self.subsonic, **response)

def get_artist_info(self, id: str, count: int | None = None, include_not_present: bool | None = None) -> ArtistInfo:
response = self.api.request("getArtistInfo2", {"id": id, "count": count, "includeNotPresent":include_not_present})["artistInfo2"]

return ArtistInfo(self.subsonic, id, **response)
78 changes: 76 additions & 2 deletions src/knuckles/models/artist.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,66 @@

from dateutil import parser

class ArtistInfo:
"""Representation of all the data related to an artist info in Subsonic."""

def __init__(
self,
# Internal
subsonic: "Subsonic",
artist_id: str,
# Subsonic fields
biography: str,
musicBrainzId: str | None,
lastFmUrl: str | None,
smallImageUrl: str | None,
mediumImageUrl: str | None,
largeImageUrl: str | None,
similarArtist: list[dict[str, Any]] | None = None
) -> None:
"""Representation of all the data related to an album info in Subsonic.
:param subsonic: The subsonic object to make all the internal requests with it.
:type subsonic: Subsonic
:param artist_id: The ID3 of the artist associated with the info.
:type artist_id: str
:param biography: A biography for the album.
:type biography: str
:param musicBrainzId:The ID in music Brainz of the album.
:type musicBrainzId: str
:param smallImageUrl: An URL to the small size cover image of the artist.
:type smallImageUrl: str
:param mediumImageUrl: An URL to the medium size cover image of the artist.
:type mediumImageUrl: str
:param largeImageUrl: An URL to the large size cover image of the artist.
:type largeImageUrl: str
:param similarArtist: A list with all the similar artists.
:type similarArtist: list[str, Any]
"""

self.__subsonic = subsonic
self.artist_id = artist_id
self.biography = biography
self.music_brainz_id = musicBrainzId
self.last_fm_url = lastFmUrl
self.small_image_url = smallImageUrl
self.medium_image_url = mediumImageUrl
self.large_image_url = largeImageUrl
self.similar_artists = [
Artist(self.__subsonic, **artist) for artist in similarArtist
]

def generate(self) -> "ArtistInfo":
"""Return a new artist info with all the data updated from the API,
using the endpoint that return the most information possible.
Useful for making copies with updated data or updating the object itself
with immutability, e.g., foo = foo.generate().
:return: A new album info object with all the data updated.
:rtype: ArtistInfo
"""

return self.__subsonic.browsing.get_artist_info(self.artist_id)

class Artist:
"""Representation of all the data related to an artist in Subsonic."""
Expand Down Expand Up @@ -65,6 +125,7 @@ def __init__(
if album
else None
)
self.info: ArtistInfo | None = None

def generate(self) -> "Artist":
"""Return a new artist with all the data updated from the API,
Expand All @@ -77,6 +138,19 @@ def generate(self) -> "Artist":
:rtype: Artist
"""

get_artist = self.__subsonic.browsing.get_artist(self.id)
new_artist = self.__subsonic.browsing.get_artist(self.id)
new_artist.get_artist_info()

return new_artist

def get_artist_info(self) -> ArtistInfo:
"""Returns the extra info given by the "getAlbumInfo2" endpoint,
also sets it in the info property of the model.
:return: An AlbumInfo object with all the extra info given by the API.
:rtype: AlbumInfo
"""

self.info = self.__subsonic.browsing.get_artist_info(self.id)

return get_artist
return self.info
8 changes: 3 additions & 5 deletions tests/api/test_browsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,15 +179,13 @@ def test_get_album_info(
@responses.activate
def test_get_artist_info(
subsonic: Subsonic,
mock_get_artist_info: Response,
mock_get_artist_info_with_all_optional_params: Response,
artist: dict[str, Any],
artist_info: dict[str, Any],
) -> None:
responses.add(mock_get_artist_info)
responses.add(mock_get_artist_info_with_all_optional_params)

response = subsonic.browsing.get_artist_info(
artist["id"], len(artist_info["similarArtist"]), False
)
response = subsonic.browsing.get_artist_info(artist["id"], len(artist_info["similarArtist"]), False)

assert response.biography == artist_info["biography"]
assert response.music_brainz_id == artist_info["musicBrainzId"]
Expand Down
13 changes: 12 additions & 1 deletion tests/mocks/browsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,10 +174,21 @@ def artist_info(artist: dict[str, Any]) -> dict[str, Any]:
"similarArtist": [artist],
}


@pytest.fixture
def mock_get_artist_info(
mock_generator: MockGenerator, artist: dict[str, Any], artist_info: dict[str, Any]
) -> Response:
return mock_generator(
"getArtistInfo2",
{
"id": artist["id"],
},
{"artistInfo2": artist_info},
)

@pytest.fixture
def mock_get_artist_info_with_all_optional_params(
mock_generator: MockGenerator, artist: dict[str, Any], artist_info: dict[str, Any]
) -> Response:
return mock_generator(
"getArtistInfo2",
Expand Down
4 changes: 3 additions & 1 deletion tests/models/test_artist.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@
def test_generate(
subsonic: Subsonic,
mock_get_artist: Response,
mock_get_artist_info: Response,
artist: dict[str, Any],
artist_info: dict[str, Any],
) -> None:
responses.add(mock_get_artist)
responses.add(mock_get_artist_info)

requested_artist = subsonic.browsing.get_artist(artist["id"])
requested_artist.name = "Foo"
Expand All @@ -37,4 +39,4 @@ def test_get_artist_info(
get_artist_info = requested_artist.get_artist_info()

assert get_artist_info.biography == artist_info["biography"]
assert requested_artist.info.notes == artist_info["notes"]
assert requested_artist.info.biography == artist_info["biography"]
21 changes: 21 additions & 0 deletions tests/models/test_artist_info.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from typing import Any

import responses
from knuckles import Subsonic
from responses import Response


@responses.activate
def test_generate(
subsonic: Subsonic,
mock_get_artist_info: Response,
artist: dict[str, Any],
artist_info: dict[str, Any],
) -> None:
responses.add(mock_get_artist_info)

response = subsonic.browsing.get_artist_info(artist["id"])
response.biography = ""
response = response.generate()

assert response.biography == artist_info["biography"]

0 comments on commit d00a3a4

Please sign in to comment.