Skip to content

Commit

Permalink
Add video and video info support
Browse files Browse the repository at this point in the history
  • Loading branch information
Kutu committed Apr 22, 2024
1 parent b36316d commit 0d1169c
Show file tree
Hide file tree
Showing 15 changed files with 699 additions and 48 deletions.
17 changes: 10 additions & 7 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@
- [ ] Implement missing endpoints.
- [ ] indexes mock data should use mock artist data.
- [ ] Add the `subsonic.system.check_subsonic_extension()` method.
- [ ] Add the `subsonic.system.check_compatibility()` method.
- [ ] Make all modules private (leading underscore).
- [ ] Improve error handling:
- [ ] Check and rewrite all docstrings taking care about raising exceptions.
- [ ] Decide if `raw_request()` and `json_request` should be public, if not `self.api` should be `self._api`,
- [ ] Remove unnecessary packages installed in GitHub Actions `tests` job.
- [ ] Spin up a `MkDocs` documentation.
- [ ] Add the URL in the GitHub page.
Expand Down Expand Up @@ -43,8 +46,8 @@ The final objetive of Knuckles to be a fully compatible implementation wrapper a
- [x] `getArtist`
- [x] `getAlbum`
- [x] `getSong`
- [ ] `getVideos` **[VIDEO]**
- [ ] `getVideoInfo` **[VIDEO]**
- [x] `getVideos`
- [x] `getVideoInfo`
- [ ] `getArtistInfo` **[No ID3]**
- [x] `getArtistInfo2`
- [ ] `getAlbumInfo` **[No ID3]**
Expand All @@ -55,12 +58,12 @@ The final objetive of Knuckles to be a fully compatible implementation wrapper a

#### Album/song lists
- [ ] `getAlbumList` **[No ID3]**
- [ ] `getAlbumList2` **[MISSING]**
- [ ] `getRandomSongs` **[MISSING]**
- [ ] `getSongsByGenre` **[MISSING]**
- [ ] `getNowPlaying` **[MISSING]**
- [x] `getAlbumList2`
- [x] `getRandomSongs`
- [x] `getSongsByGenre`
- [x] `getNowPlaying`
- [ ] `getStarred` **[No ID3]**
- [ ] `getStarred2` **[MISSING]**
- [x] `getStarred2`

#### Searching
- [x] `search` _(Will never be implemented, deprecated)_
Expand Down
20 changes: 20 additions & 0 deletions src/knuckles/browsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from .models.music_directory import MusicDirectory
from .models.music_folder import MusicFolder
from .models.song import Song
from .models.video import Video, VideoInfo

if TYPE_CHECKING:
from .subsonic import Subsonic
Expand Down Expand Up @@ -176,6 +177,25 @@ def get_song(self, id_: str) -> Song:

return Song(self.subsonic, **response)

def get_videos(self) -> list[Video]:
response = self.api.json_request("getVideos")["videos"]["video"]

return [Video(self.subsonic, **video) for video in response]

def get_video(self, video_id: str) -> Video | None:
videos = self.get_videos()

for video in videos:
if video.id == video_id:
return video

return None

def get_video_info(self, video_id: str) -> VideoInfo:
response = self.api.json_request("getVideoInfo", {"id": video_id})["videoInfo"]

return VideoInfo(self.subsonic, **response)

def get_artist_info(
self,
id_: str,
Expand Down
55 changes: 55 additions & 0 deletions src/knuckles/lists.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

from .api import Api
from .models.album import Album
from .models.now_playing_entry import NowPlayingEntry
from .models.song import Song
from .models.starred_content import StarredContent

if TYPE_CHECKING:
from .subsonic import Subsonic
Expand Down Expand Up @@ -146,3 +149,55 @@ def get_album_list_by_genre(
music_folder_id,
genre=genre_name,
)

def get_random_songs(
self,
num_of_songs: int | None = None,
genre_name: str | None = None,
from_year: int | None = None,
to_year: int | None = None,
music_folder_id: str | None = None,
) -> list[Song]:
response = self.api.json_request(
"getRandomSongs",
{
"size": num_of_songs,
"genre": genre_name,
"fromYear": from_year,
"toYear": to_year,
"musicFolderId": music_folder_id,
},
)["randomSongs"]["song"]

return [Song(subsonic=self.subsonic, **song) for song in response]

def get_songs_by_genre(
self,
genre_name: str,
num_of_songs: int | None = None,
song_list_offset: int | None = None,
music_folder_id: str | None = None,
) -> list[Song]:
response = self.api.json_request(
"getSongsByGenre",
{
"genre": genre_name,
"count": num_of_songs,
"offset": song_list_offset,
"musicFolderId": music_folder_id,
},
)["songsByGenre"]["song"]

return [Song(subsonic=self.subsonic, **song) for song in response]

def get_now_playing(self) -> list[NowPlayingEntry]:
response = self.api.json_request("getNowPlaying")["nowPlaying"]["entry"]

return [NowPlayingEntry(subsonic=self.subsonic, **entry) for entry in response]

def get_starred(self, music_folder_id: str | None = None) -> StarredContent:
response = self.api.json_request(
"getStarred2", {"musicFolderId": music_folder_id}
)["starred2"]

return StarredContent(subsonic=self.subsonic, **response)
23 changes: 23 additions & 0 deletions src/knuckles/models/contributor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from typing import TYPE_CHECKING

# Avoid circular import error
from .artist import Artist
from .model import Model

if TYPE_CHECKING:
from ..subsonic import Subsonic


class Contributor(Model):
def __init__(
self,
subsonic: "Subsonic",
role: str,
artist: Artist,
subRole: str | None = None,
) -> None:
super().__init__(subsonic)

self.role = role
self.subrole = subRole
self.artist = artist
27 changes: 27 additions & 0 deletions src/knuckles/models/now_playing_entry.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from typing import TYPE_CHECKING, Any

from .model import Model
from .song import Song
from .user import User

if TYPE_CHECKING:
from ..subsonic import Subsonic


class NowPlayingEntry(Model):
def __init__(
self,
subsonic: "Subsonic",
username: str,
minutesAgo: int | None = None,
playerId: int | None = None,
playerName: str | None = None,
**song_data: Any,
) -> None:
super().__init__(subsonic)

self.user = User(self._subsonic, username)
self.minutes_ago = minutesAgo
self.player_id = playerId
self.player_name = playerName
self.song = Song(subsonic=self._subsonic, **song_data)
26 changes: 26 additions & 0 deletions src/knuckles/models/replay_gain.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from typing import TYPE_CHECKING

# Avoid circular import error
from .model import Model

if TYPE_CHECKING:
from ..subsonic import Subsonic


class ReplayGain(Model):
def __init__(
self,
subsonic: "Subsonic",
trackGain: str | None = None,
albumGain: str | None = None,
trackPeak: str | None = None,
albumPeak: str | None = None,
baseGain: str | None = None,
) -> None:
super().__init__(subsonic)

self.track_gain = trackGain
self.album_gain = albumGain
self.track_peak = trackPeak
self.album_peak = albumPeak
self.base_gain = baseGain
40 changes: 4 additions & 36 deletions src/knuckles/models/song.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
from knuckles.models.genre import Genre, ItemGenre

from .artist import Artist
from .contributor import Contributor
from .cover_art import CoverArt
from .model import Model
from .replay_gain import ReplayGain

if TYPE_CHECKING:
from ..subsonic import Subsonic
Expand All @@ -16,40 +18,6 @@
from dateutil import parser


class Contributor(Model):
def __init__(
self,
subsonic: "Subsonic",
role: str,
artist: Artist,
subRole: str | None = None,
) -> None:
super().__init__(subsonic)

self.role = role
self.subrole = subRole
self.artist = artist


class ReplayGain(Model):
def __init__(
self,
subsonic: "Subsonic",
trackGain: str | None = None,
albumGain: str | None = None,
trackPeak: str | None = None,
albumPeak: str | None = None,
baseGain: str | None = None,
) -> None:
super().__init__(subsonic)

self.track_gain = trackGain
self.album_gain = albumGain
self.track_peak = trackPeak
self.album_peak = albumPeak
self.base_gain = baseGain


class Song(Model):
"""Representation of all the data related to a song in Subsonic."""

Expand All @@ -58,8 +26,9 @@ def __init__(
subsonic: "Subsonic",
id: str,
title: str | None = None,
isDir: bool = False,
parent: str | None = None,
isDir: bool | None = None,
isVideo: bool | None = None,
album: str | None = None,
artist: str | None = None,
track: int | None = None,
Expand All @@ -74,7 +43,6 @@ def __init__(
duration: int | None = None,
bitRate: int | None = None,
path: str | None = None,
isVideo: bool = False,
userRating: int | None = None,
averageRating: float | None = None,
playCount: int | None = None,
Expand Down
35 changes: 35 additions & 0 deletions src/knuckles/models/starred_content.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from typing import TYPE_CHECKING, Any

from knuckles.models.model import Model

from .album import Album
from .artist import Artist
from .song import Song

if TYPE_CHECKING:
from ..subsonic import Subsonic


class StarredContent(Model):
def __init__(
self,
subsonic: "Subsonic",
song: list[dict[str, Any]] | None = None,
album: list[dict[str, Any]] | None = None,
artist: list[dict[str, Any]] | None = None,
) -> None:
super().__init__(subsonic)

self.songs = (
[Song(subsonic=self._subsonic, **song) for song in song] if song else None
)
self.albums = (
[Album(subsonic=self._subsonic, **album) for album in album]
if album
else None
)
self.artists = (
[Artist(subsonic=self._subsonic, **artist) for artist in artist]
if artist
else None
)
Loading

0 comments on commit 0d1169c

Please sign in to comment.