Skip to content
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

Feat/stream #30

Merged
merged 8 commits into from
Sep 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: "3.11"
python-version: "3.11.5"
- uses: Gr1N/setup-poetry@v8
- uses: actions/cache@v3
with:
Expand Down
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ share/python-wheels/
.installed.cfg
*.egg
MANIFEST
poetry.lock

# Code Editors
.vscode
Expand Down
804 changes: 804 additions & 0 deletions poetry.lock

Large diffs are not rendered by default.

30 changes: 24 additions & 6 deletions src/knuckles/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,7 @@ def generate_params(self, extra_params: dict[str, Any] = {}) -> dict[str, Any]:

return {**params, "t": token, "s": salt}

def request(
self, endpoint: str, extra_params: dict[str, Any] = {}
) -> dict[str, Any]:
def raw_request(self, endpoint: str, extra_params: dict[str, Any]) -> Response:
"""Make a request to the Subsonic API.

:param endpoint: The endpoint where the request should be made,
Expand All @@ -112,18 +110,38 @@ def request(
:type endpoint: str
:param extra_params: The extra parameters required by the endpoint,
defaults to {}.
:type extra_params: dict[str, Any], optional
:raises code_error: Raises an exception with the format CodeErrorXX or
UnknownCodeError if the request fails.
:return: The JSON data inside the "subsonic-response" object.
:return: The Response object returned by the request.
:rtype: dict[str, Any]
"""

response: Response = requests.get(
return requests.get(
url=f"{self.url}/rest/{endpoint}",
params=self.generate_params(extra_params),
)

def json_request(
self, endpoint: str, extra_params: dict[str, Any] = {}
) -> dict[str, Any]:
"""Make a request to the Subsonic API and returns a JSON response.
Don't use with binary data endpoints.

:param endpoint: The endpoint where the request should be made,
only specifies the route name, without slashes.
E.g. "ping", "getLicense", etc.
:type endpoint: str
:param extra_params: The extra parameters required by the endpoint,
defaults to {}.
:type extra_params: dict[str, Any], optional
:raises code_error: Raises an exception with the format CodeErrorXX or
UnknownCodeError if the request fails.
:return: The JSON data inside the "subsonic-response" object.
:rtype: dict[str, Any]
"""

response = self.raw_request(endpoint, extra_params)

json_response: dict[str, Any] = response.json()["subsonic-response"]

if json_response["status"] == "failed":
Expand Down
11 changes: 5 additions & 6 deletions src/knuckles/bookmarks.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def get_bookmarks(self) -> list[Bookmark]:
:rtype: list[Bookmark]
"""

response = self.api.request("getBookmarks")["bookmarks"]["bookmark"]
response = self.api.json_request("getBookmarks")["bookmarks"]["bookmark"]

return [Bookmark(self.subsonic, **bookmark) for bookmark in response]

Expand Down Expand Up @@ -62,7 +62,7 @@ def create_bookmark(
:rtype: Bookmark
"""

self.api.request(
self.api.json_request(
"createBookmark", {"id": id, "position": position, "comment": comment}
)

Expand Down Expand Up @@ -97,7 +97,7 @@ def delete_bookmark(self, id: str) -> "Subsonic":
:rtype: Subsonic
"""

self.api.request("deleteBookmark", {"id": id})
self.api.json_request("deleteBookmark", {"id": id})

return self.subsonic

Expand All @@ -108,7 +108,7 @@ def get_play_queue(self) -> PlayQueue:
:rtype: PlayQueue
"""

response = self.api.request("getPlayQueue")["playQueue"]
response = self.api.json_request("getPlayQueue")["playQueue"]

return PlayQueue(self.subsonic, **response)

Expand All @@ -132,12 +132,11 @@ def save_play_queue(
:rtype: PlayQueue
"""

self.api.request(
self.api.json_request(
"savePlayQueue",
{"id": song_ids, "current": current_song_id, "position": position},
)

# TODO This approach is expensive, a better one is preferred
# Fake the song structure given by in the API.
songs = []
for song_id in song_ids:
Expand Down
24 changes: 13 additions & 11 deletions src/knuckles/browsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ def get_music_folders(self) -> list[MusicFolder]:
:rtype: list[MusicFolder]
"""

response = self.api.request("getMusicFolders")["musicFolders"]["musicFolder"]
response = self.api.json_request("getMusicFolders")["musicFolders"][
"musicFolder"
]

return [MusicFolder(self.subsonic, **music_folder) for music_folder in response]

Expand All @@ -39,7 +41,7 @@ def get_genres(self) -> list[Genre]:
:rtype: list[Genre]
"""

response = self.api.request("getGenres")["genres"]["genre"]
response = self.api.json_request("getGenres")["genres"]["genre"]

return [Genre(self.subsonic, **genre) for genre in response]

Expand All @@ -49,7 +51,7 @@ def get_genre(self, name: str) -> Genre | None:
:param name: The name of the genre to get.
:type name: str
:return: A genre object that correspond with the given name
or None if if no genre found.
or None if is no genre found.
:rtype: Genre | None
"""

Expand All @@ -71,9 +73,9 @@ def get_artists(self, music_folder_id: str | None = None) -> list[Artist]:
:rtype: list[Artist]
"""

response = self.api.request("getArtists", {"musicFolderId": music_folder_id})[
"artists"
]["index"]
response = self.api.json_request(
"getArtists", {"musicFolderId": music_folder_id}
)["artists"]["index"]

artists: list[Artist] = []

Expand All @@ -94,7 +96,7 @@ def get_artist(self, id: str) -> Artist:
:rtype: Artist
"""

response = self.api.request("getArtist", {"id": id})["artist"]
response = self.api.json_request("getArtist", {"id": id})["artist"]

return Artist(self.subsonic, **response)

Expand All @@ -108,7 +110,7 @@ def get_album(self, id: str) -> Album:
:rtype: Album
"""

response = self.api.request("getAlbum", {"id": id})["album"]
response = self.api.json_request("getAlbum", {"id": id})["album"]

return Album(self.subsonic, **response)

Expand All @@ -121,7 +123,7 @@ def get_album_info(self, id: str) -> AlbumInfo:
:rtype: AlbumInfo
"""

response = self.api.request("getAlbumInfo2", {"id": id})["albumInfo"]
response = self.api.json_request("getAlbumInfo2", {"id": id})["albumInfo"]

return AlbumInfo(self.subsonic, id, **response)

Expand All @@ -135,14 +137,14 @@ def get_song(self, id: str) -> Song:
:rtype: Song
"""

response = self.api.request("getSong", {"id": id})["song"]
response = self.api.json_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(
response = self.api.json_request(
"getArtistInfo2",
{"id": id, "count": count, "includeNotPresent": include_not_present},
)["artistInfo2"]
Expand Down
4 changes: 2 additions & 2 deletions src/knuckles/chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def add_chat_message(self, message: str) -> "Subsonic":
:rtype: Self
"""

self.api.request("addChatMessage", {"message": message})
self.api.json_request("addChatMessage", {"message": message})

return self.subsonic

Expand All @@ -37,7 +37,7 @@ def get_chat_messages(self) -> list[ChatMessage]:
:rtype: list[ChatMessage]
"""

response: list[dict[str, Any]] = self.api.request("getChatMessages")[
response: list[dict[str, Any]] = self.api.json_request("getChatMessages")[
"chatMessages"
]["chatMessage"]

Expand Down
8 changes: 4 additions & 4 deletions src/knuckles/internet_radio.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def get_internet_radio_stations(
:rtype: list[InternetRadioStation]
"""

response = self.api.request("getInternetRadioStations")[
response = self.api.json_request("getInternetRadioStations")[
"internetRadioStations"
]["internetRadioStation"]

Expand Down Expand Up @@ -67,7 +67,7 @@ def create_internet_radio_station(
:rtype: Subsonic
"""

self.api.request(
self.api.json_request(
"createInternetRadioStation",
{"streamUrl": stream_url, "name": name, "homepageUrl": homepage_url},
)
Expand All @@ -92,7 +92,7 @@ def update_internet_radio_station(
:rtype: Subsonic
"""

self.api.request(
self.api.json_request(
"updateInternetRadioStation",
{
"id": id,
Expand All @@ -113,6 +113,6 @@ def delete_internet_radio_station(self, id: str) -> "Subsonic":
:rtype: Subsonic
"""

self.api.request("deleteInternetRadioStation", {"id": id})
self.api.json_request("deleteInternetRadioStation", {"id": id})

return self.subsonic
22 changes: 11 additions & 11 deletions src/knuckles/jukebox.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def get(self) -> Jukebox:
:rtype: Jukebox
"""

response = self.api.request("jukeboxControl", {"action": "get"})[
response = self.api.json_request("jukeboxControl", {"action": "get"})[
"jukeboxPlaylist"
]

Expand All @@ -40,7 +40,7 @@ def status(self) -> Jukebox:
:rtype: Jukebox
"""

response = self.api.request("jukeboxControl", {"action": "status"})[
response = self.api.json_request("jukeboxControl", {"action": "status"})[
"jukeboxStatus"
]

Expand All @@ -55,7 +55,7 @@ def set(self, id: str) -> Jukebox:
:rtype: Jukebox
"""

response = self.api.request("jukeboxControl", {"action": "set", "id": id})[
response = self.api.json_request("jukeboxControl", {"action": "set", "id": id})[
"jukeboxStatus"
]

Expand All @@ -70,7 +70,7 @@ def start(self) -> Jukebox:
:rtype: Jukebox
"""

response = self.api.request("jukeboxControl", {"action": "start"})[
response = self.api.json_request("jukeboxControl", {"action": "start"})[
"jukeboxStatus"
]

Expand All @@ -84,7 +84,7 @@ def stop(self) -> Jukebox:
:rtype: Jukebox
"""

response = self.api.request("jukeboxControl", {"action": "stop"})[
response = self.api.json_request("jukeboxControl", {"action": "stop"})[
"jukeboxStatus"
]

Expand All @@ -102,7 +102,7 @@ def skip(self, index: int, offset: float = 0) -> Jukebox:
:rtype: Jukebox
"""

response = self.api.request(
response = self.api.json_request(
"jukeboxControl", {"action": "skip", "index": index, "offset": offset}
)["jukeboxStatus"]

Expand All @@ -118,7 +118,7 @@ def add(self, id: str) -> Jukebox:
:rtype: Jukebox
"""

response = self.api.request("jukeboxControl", {"action": "add", "id": id})[
response = self.api.json_request("jukeboxControl", {"action": "add", "id": id})[
"jukeboxStatus"
]

Expand All @@ -132,7 +132,7 @@ def clear(self) -> Jukebox:
:rtype: Jukebox
"""

response = self.api.request("jukeboxControl", {"action": "clear"})[
response = self.api.json_request("jukeboxControl", {"action": "clear"})[
"jukeboxStatus"
]

Expand All @@ -148,7 +148,7 @@ def remove(self, index: int) -> Jukebox:
:rtype: Jukebox
"""

response = self.api.request(
response = self.api.json_request(
"jukeboxControl", {"action": "remove", "index": index}
)["jukeboxStatus"]

Expand All @@ -162,7 +162,7 @@ def shuffle(self) -> Jukebox:
:rtype: Jukebox
"""

response = self.api.request("jukeboxControl", {"action": "shuffle"})[
response = self.api.json_request("jukeboxControl", {"action": "shuffle"})[
"jukeboxStatus"
]

Expand All @@ -182,7 +182,7 @@ def set_gain(self, gain: float) -> Jukebox:
if not 1 > gain > 0:
raise ValueError("The gain should be between 0 and 1 (inclusive)")

response = self.api.request(
response = self.api.json_request(
"jukeboxControl", {"action": "setGain", "gain": gain}
)["jukeboxStatus"]

Expand Down
Loading