-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #27 from kutu-dev/feat/searching
Feat/searching
- Loading branch information
Showing
5 changed files
with
208 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
from dataclasses import dataclass | ||
from typing import TYPE_CHECKING | ||
|
||
from .api import Api | ||
from .models.album import Album | ||
from .models.artist import Artist | ||
from .models.song import Song | ||
|
||
if TYPE_CHECKING: | ||
from .subsonic import Subsonic | ||
|
||
|
||
# Use a plain dataclass as it only stores lists | ||
# and doesn't have any sort of method to be generated | ||
@dataclass | ||
class SearchResult: | ||
songs: list[Song] | None = None | ||
albums: list[Album] | None = None | ||
artists: list[Artist] | None = None | ||
|
||
|
||
class Searching: | ||
"""Class that contains all the methods needed to interact | ||
with the searching calls and actions in the Subsonic API. | ||
<https://opensubsonic.netlify.app/categories/searching/> | ||
""" | ||
|
||
def __init__(self, api: Api, subsonic: "Subsonic") -> None: | ||
self.api = api | ||
|
||
# Only to pass it to the models | ||
self.subsonic = subsonic | ||
|
||
def search( | ||
self, | ||
query: str = "", | ||
song_count: int | None = None, | ||
song_offset: int | None = None, | ||
album_count: int | None = None, | ||
album_offset: int | None = None, | ||
artist_count: int | None = None, | ||
artist_offset: int | None = None, | ||
music_folder_id: str | None = None, | ||
) -> SearchResult: | ||
"""Calls the "search3" endpoint of the API. | ||
:param query: The query | ||
:type query: str | ||
:param song_count: Maximum number of songs to return | ||
:type song_count: int | ||
:param song_offset: Offset the results for songs | ||
:type song_offset: int | ||
:param album_count: Maximum number of albums to return | ||
:type album_count: int | ||
:param album_offset: Offset the results for albums | ||
:type album_offset: int | ||
:param artist_count: Maximum number of artists to return | ||
:type artist_count: int | ||
:param artist_offset: Offset the results for artists | ||
:type artist_offset: int | ||
:param music_folder_id: The id of the music folder to search results | ||
:type music_folder_id: str | ||
:return: | ||
:rtype: | ||
""" | ||
response = self.api.request( | ||
"search3", | ||
{ | ||
"query": query, | ||
"songCount": song_count, | ||
"songOffset": song_offset, | ||
"albumCount": album_count, | ||
"albumOffset": album_offset, | ||
"artistCount": artist_count, | ||
"artistOffset": artist_offset, | ||
}, | ||
)["searchResult3"] | ||
|
||
search_result_songs = ( | ||
[Song(self.subsonic, **song) for song in response["song"]] | ||
if "song" in response | ||
else None | ||
) | ||
search_result_albums = ( | ||
[Album(self.subsonic, **album) for album in response["album"]] | ||
if "album" in response | ||
else None | ||
) | ||
search_result_artists = ( | ||
[Artist(self.subsonic, **artist) for artist in response["artist"]] | ||
if "artist" in response | ||
else None | ||
) | ||
|
||
return SearchResult( | ||
songs=search_result_songs, | ||
albums=search_result_albums, | ||
artists=search_result_artists, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
from typing import Any | ||
|
||
import responses | ||
from knuckles import Subsonic | ||
from responses import Response | ||
|
||
|
||
@responses.activate | ||
def test_search_song( | ||
subsonic: Subsonic, | ||
mock_search_song: Response, | ||
song: dict[str, Any], | ||
music_folders: list[dict[str, Any]], | ||
) -> None: | ||
responses.add(mock_search_song) | ||
|
||
response = subsonic.searching.search( | ||
song["title"], 1, 0, 0, 0, 0, 0, music_folders[0]["id"] | ||
) | ||
|
||
assert response.songs is not None | ||
assert response.songs[0].title == song["title"] | ||
assert response.albums is None | ||
assert response.artists is None | ||
|
||
|
||
@responses.activate | ||
def test_search_album( | ||
subsonic: Subsonic, | ||
mock_search_album: Response, | ||
album: dict[str, Any], | ||
music_folders: list[dict[str, Any]], | ||
) -> None: | ||
responses.add(mock_search_album) | ||
|
||
response = subsonic.searching.search( | ||
album["title"], 0, 0, 1, 0, 0, 0, music_folders[0]["id"] | ||
) | ||
|
||
assert response.songs is None | ||
assert response.albums is not None | ||
assert response.albums[0].title == album["title"] | ||
assert response.artists is None | ||
|
||
|
||
@responses.activate | ||
def test_search_artist( | ||
subsonic: Subsonic, | ||
mock_search_artist: Response, | ||
artist: dict[str, Any], | ||
music_folders: list[dict[str, Any]], | ||
) -> None: | ||
responses.add(mock_search_artist) | ||
|
||
response = subsonic.searching.search( | ||
artist["name"], 0, 0, 0, 0, 1, 0, music_folders[0]["id"] | ||
) | ||
|
||
assert response.songs is None | ||
assert response.albums is None | ||
assert response.artists is not None | ||
assert response.artists[0].name == artist["name"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
from typing import Any | ||
|
||
import pytest | ||
from responses import Response | ||
|
||
from tests.conftest import MockGenerator | ||
|
||
|
||
@pytest.fixture | ||
def mock_search_song( | ||
mock_generator: MockGenerator, | ||
music_folders: list[dict[str, Any]], | ||
song: dict[str, Any], | ||
) -> Response: | ||
return mock_generator( | ||
"search3", | ||
{"songCount": 1, "songOffset": 0}, | ||
{"musicFolderId": music_folders[0]["id"], "searchResult3": {"song": [song]}}, | ||
) | ||
|
||
|
||
@pytest.fixture | ||
def mock_search_album( | ||
mock_generator: MockGenerator, music_folders, album: dict[str, Any] | ||
) -> Response: | ||
return mock_generator( | ||
"search3", | ||
{"albumCount": 1, "albumOffset": 0}, | ||
{"musicFolderId": music_folders[0]["id"], "searchResult3": {"album": [album]}}, | ||
) | ||
|
||
|
||
@pytest.fixture | ||
def mock_search_artist( | ||
mock_generator: MockGenerator, music_folders, artist: dict[str, Any] | ||
) -> Response: | ||
return mock_generator( | ||
"search3", | ||
{"artistCount": 1, "artistOffset": 0}, | ||
{ | ||
"musicFolderId": music_folders[0]["id"], | ||
"searchResult3": {"artist": [artist]}, | ||
}, | ||
) |