Skip to content

Commit

Permalink
Refactor and simplify the models architecture
Browse files Browse the repository at this point in the history
  • Loading branch information
Kutu committed Apr 21, 2024
1 parent 227a604 commit 7586f31
Show file tree
Hide file tree
Showing 25 changed files with 311 additions and 252 deletions.
11 changes: 2 additions & 9 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,8 @@
# TODO
- [ ] Add .
- [ ] Make a `model` class and add the following methods to it:
- [ ] `_check_api_access()`
- [ ] `_resource_not_found()`
- [ ] Should `CoverArt` be removed?
- [ ] Determine if video and non-ID3 endpoints will be supported.
- [ ] If not the Video and NonID3 checks in the `Song` model should be removed.
- [ ] Implement missing endpoints.
- [ ] Improve error handling:
- [ ] Add the `subsonic.system.check_subsonic_extension()` method.
- [ ] Check and rewrite all docstrings taking care about raising exceptions.
- [ ] Improve error handling:
- [ ] Check and rewrite all docstrings taking care about raising exceptions.
- [ ] Spin up a `MkDocs` documentation.
- [ ] Add the URL in the GitHub page.

Expand Down
2 changes: 1 addition & 1 deletion src/knuckles/chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,6 @@ def get_chat_messages(self) -> list[ChatMessage]:
"chatMessages"
]["chatMessage"]

messages = [ChatMessage(**message) for message in response]
messages = [ChatMessage(self.subsonic, **message) for message in response]

return messages
19 changes: 9 additions & 10 deletions src/knuckles/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@
from typing import Type


class NoApiAccess(Exception):
class MissingRequiredProperty(Exception):
pass


class InvalidRatingNumber(ValueError):
pass


class VideoArgumentsInSong(ValueError):
pass


class ResourceNotFound(Exception):
pass


class AlbumOrArtistArgumentsInSong(ValueError):
pass
def __init__(
self,
message: str = (
"Unable to generate the model as it does not exist in the server"
),
*args: str
) -> None:
super().__init__(message, *args)


class ShareInvalidSongList(ValueError):
Expand Down
14 changes: 11 additions & 3 deletions src/knuckles/media_library_scanning.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
from typing import TYPE_CHECKING

from .api import Api
from .models.scan_status import ScanStatus

if TYPE_CHECKING:
from .subsonic import Subsonic


class MediaLibraryScanning:
"""Class that contains all the methods needed to interact
with the media library scanning calls in the Subsonic API.
<https://opensubsonic.netlify.app/categories/media-library-scanning/>
"""

def __init__(self, api: Api) -> None:
def __init__(self, api: Api, subsonic: "Subsonic") -> None:
self.api = api

# Only to pass it to the models
self.subsonic = subsonic

def get_scan_status(self) -> ScanStatus:
"""Calls to the "getScanStatus" endpoint of the API.
Expand All @@ -20,7 +28,7 @@ def get_scan_status(self) -> ScanStatus:

response = self.api.json_request("getScanStatus")["scanStatus"]

return ScanStatus(**response)
return ScanStatus(self.subsonic, **response)

def start_scan(self) -> ScanStatus:
"""Calls to the "scanStatus" endpoint of the API.
Expand All @@ -31,4 +39,4 @@ def start_scan(self) -> ScanStatus:

response = self.api.json_request("startScan")["scanStatus"]

return ScanStatus(**response)
return ScanStatus(self.subsonic, **response)
69 changes: 44 additions & 25 deletions src/knuckles/models/album.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,43 +7,49 @@
from .artist import Artist
from .cover_art import CoverArt
from .genre import ItemGenre
from .model import Model

if TYPE_CHECKING:
from ..subsonic import Subsonic


class RecordLabel:
def __init__(self, name: str) -> None:
class RecordLabel(Model):
def __init__(self, subsonic: "Subsonic", name: str) -> None:
super().__init__(subsonic)

self.name = name


class Disc:
def __init__(self, disc: int, title: str) -> None:
class Disc(Model):
def __init__(self, subsonic: "Subsonic", disc: int, title: str) -> None:
super().__init__(subsonic)

self.disc_number = disc
self.title = title


class ReleaseDate:
class ReleaseDate(Model):
def __init__(
self,
subsonic: "Subsonic",
year: int,
month: int,
day: int,
) -> None:
super().__init__(subsonic)

self.year = year
self.month = month
self.day = day


class AlbumInfo:
class AlbumInfo(Model):
"""Representation of all the data related to an album info in Subsonic."""

def __init__(
self,
# Internal
subsonic: "Subsonic",
album_id: str,
# Subsonic fields
notes: str,
musicBrainzId: str | None,
lastFmUrl: str | None,
Expand All @@ -68,7 +74,8 @@ def __init__(
:type largeImageUrl: str
"""

self.__subsonic = subsonic
super().__init__(subsonic)

self.album_id = album_id
self.notes = notes
self.music_brainz_id = musicBrainzId
Expand All @@ -88,17 +95,15 @@ def generate(self) -> "AlbumInfo":
:rtype: AlbumInfo
"""

return self.__subsonic.browsing.get_album_info(self.album_id)
return self._subsonic.browsing.get_album_info(self.album_id)


class Album:
class Album(Model):
"""Representation of all the data related to an album in Subsonic."""

def __init__(
self,
# Internal
subsonic: "Subsonic",
# Subsonic fields
id: str,
parent: str | None = None,
album: str | None = None,
Expand Down Expand Up @@ -176,15 +181,16 @@ def __init__(
:type song: list[dict[str, Any]]
"""

self.__subsonic = subsonic
super().__init__(subsonic)

self.id = id
self.parent = parent
self.album = album
self.name = name
self.is_dir = isDir
self.title = title
self.artist = Artist(self.__subsonic, artistId, artist) if artistId else None
self.cover_art = CoverArt(coverArt) if coverArt else None
self.artist = Artist(self._subsonic, artistId, artist) if artistId else None
self.cover_art = CoverArt(self._subsonic, coverArt) if coverArt else None
self.song_count = songCount
self.duration = duration
self.play_count = playCount
Expand All @@ -195,20 +201,25 @@ def __init__(
self.played = parser.parse(played) if played else None
self.user_rating = userRating
self.songs = (
[song_model_module.Song(self.__subsonic, **song_data) for song_data in song]
[song_model_module.Song(self._subsonic, **song_data) for song_data in song]
if song
else None
)
self.info: AlbumInfo | None = None
self.record_labels = (
[RecordLabel(**record_label) for record_label in recordLabels]
[
RecordLabel(self._subsonic, **record_label)
for record_label in recordLabels
]
if recordLabels
else None
)
self.music_brainz_id = musicBrainzId
self.genres = [ItemGenre(**genre) for genre in genres] if genres else None
self.genres = (
[ItemGenre(self._subsonic, **genre) for genre in genres] if genres else None
)
self.artists = (
[Artist(self.__subsonic, **artist) for artist in artists]
[Artist(self._subsonic, **artist) for artist in artists]
if artists
else None
)
Expand All @@ -217,11 +228,19 @@ def __init__(
self.moods = moods
self.sort_name = sortName
self.original_release_date = (
ReleaseDate(**originalReleaseDate) if originalReleaseDate else None
ReleaseDate(self._subsonic, **originalReleaseDate)
if originalReleaseDate
else None
)
self.release_date = (
ReleaseDate(self._subsonic, **releaseDate) if releaseDate else None
)
self.release_date = ReleaseDate(**releaseDate) if releaseDate else None
self.is_compilation = isCompilation
self.discs = [Disc(**disc) for disc in discTitles] if discTitles else None
self.discs = (
[Disc(self._subsonic, **disc) for disc in discTitles]
if discTitles
else None
)

def generate(self) -> "Album":
"""Return a new album with all the data updated from the API,
Expand All @@ -234,7 +253,7 @@ def generate(self) -> "Album":
:rtype: Album
"""

new_album = self.__subsonic.browsing.get_album(self.id)
new_album = self._subsonic.browsing.get_album(self.id)
new_album.get_album_info()

return new_album
Expand All @@ -247,6 +266,6 @@ def get_album_info(self) -> AlbumInfo:
:rtype: AlbumInfo
"""

self.info = self.__subsonic.browsing.get_album_info(self.id)
self.info = self._subsonic.browsing.get_album_info(self.id)

return self.info
27 changes: 13 additions & 14 deletions src/knuckles/models/artist.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,21 @@
import knuckles.models.album as album_model_module

from .cover_art import CoverArt
from .model import Model

if TYPE_CHECKING:
from ..subsonic import Subsonic

from dateutil import parser


class ArtistInfo:
class ArtistInfo(Model):
"""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,
Expand Down Expand Up @@ -47,7 +46,8 @@ def __init__(
:type similarArtist: list[str, Any]
"""

self.__subsonic = subsonic
super().__init__(subsonic)

self.artist_id = artist_id
self.biography = biography
self.music_brainz_id = musicBrainzId
Expand All @@ -56,7 +56,7 @@ def __init__(
self.medium_image_url = mediumImageUrl
self.large_image_url = largeImageUrl
self.similar_artists = (
[Artist(self.__subsonic, **artist) for artist in similarArtist]
[Artist(self._subsonic, **artist) for artist in similarArtist]
if similarArtist
else None
)
Expand All @@ -72,17 +72,15 @@ def generate(self) -> "ArtistInfo":
:rtype: ArtistInfo
"""

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


class Artist:
class Artist(Model):
"""Representation of all the data related to an artist in Subsonic."""

def __init__(
self,
# Internal
subsonic: "Subsonic",
# Subsonic fields
id: str,
name: str | None = None,
coverArt: str | None = None,
Expand Down Expand Up @@ -120,18 +118,19 @@ def __init__(
:type album: list[dict[str, Any]]
"""

self.__subsonic = subsonic
super().__init__(subsonic)

self.id = id
self.name = name
self.cover_art = CoverArt(coverArt) if coverArt else None
self.cover_art = CoverArt(self._subsonic, coverArt) if coverArt else None
self.artist_image_url = artistImageUrl
self.album_count = albumCount
self.starred = parser.parse(starred) if starred else None
self.user_rating = userRating
self.average_rating = averageRating
self.albums = (
[
album_model_module.Album(self.__subsonic, **album_data)
album_model_module.Album(self._subsonic, **album_data)
for album_data in album
]
if album
Expand All @@ -153,7 +152,7 @@ def generate(self) -> "Artist":
:rtype: Artist
"""

new_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
Expand All @@ -166,6 +165,6 @@ def get_artist_info(self) -> ArtistInfo:
:rtype: AlbumInfo
"""

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

return self.info
Loading

0 comments on commit 7586f31

Please sign in to comment.