diff --git a/libremsonic/__main__.py b/libremsonic/__main__.py index b3d71d9..d8582e6 100644 --- a/libremsonic/__main__.py +++ b/libremsonic/__main__.py @@ -6,8 +6,13 @@ gi.require_version('Gtk', '3.0') from gi.repository import Gtk from .app import LibremsonicApp +from .server import Server def main(): - app = LibremsonicApp() - app.run(sys.argv) + server = Server('ohea', 'https://airsonic.the-evans.family', 'sumner', + 'O}/UieSb[nzZ~l[X1S&zzX1Hi') + + print(server.get_license()) + # app = LibremsonicApp() + # app.run(sys.argv) diff --git a/libremsonic/config.py b/libremsonic/config.py index 6c7f5a2..e3f9907 100644 --- a/libremsonic/config.py +++ b/libremsonic/config.py @@ -39,20 +39,31 @@ class ServerConfiguration: class AppConfiguration: servers: List[ServerConfiguration] current_server: int - cache_location: str - music_storage_location: str + permanent_cache_location: str + temporary_cache_location: str + max_cache_size_mb: int # -1 means unlimited def to_json(self): return { 'servers': [s.__dict__ for s in self.servers], 'current_server': self.current_server, + 'permanent_cache_location': self.permanent_cache_location, + 'temporary_cache_location': self.temporary_cache_location, + 'max_cache_size_mb': self.max_cache_size_mb, } @classmethod def get_default_configuration(cls): + default_cache_location = (os.environ.get('XDG_DATA_HOME') + or os.path.expanduser('~/.local/share')) + default_cache_location = os.path.join(default_cache_location, + 'libremsonic') config = AppConfiguration() config.servers = [] config.current_server = -1 + config.permanent_cache_location = default_cache_location + config.temporary_cache_location = config.permanent_cache_location + config.max_cache_size_mb = -1 return config diff --git a/libremsonic/server/api_objects.py b/libremsonic/server/api_objects.py index 053e694..db0b454 100644 --- a/libremsonic/server/api_objects.py +++ b/libremsonic/server/api_objects.py @@ -41,29 +41,42 @@ class MusicFolder(APIObject): id: int name: str +class MediaType(APIObject, Enum): + -class File(APIObject): - id: int - parent: int - title: str + +class Child(APIObject): + id: str + parent: str isDir: bool + title: str album: str artist: str - track: str - year: str + track: int + year: int genre: str - coverArt: int + coverArt: str size: int contentType: str - isVideo: bool - transcodedSuffix: str - transcodedContentType: str suffix: str + transcodedContentType: str + transcodedSuffix: str duration: int bitRate: int path: str + isVideo: bool + userRating: UserRating + averageRating: AverageRating playCount: int + discNumber: int created: datetime + starred: datetime + albumId: str + artistId: str + type: MediaType + bookmarkPosition: int + originalWidth: int + originalHeight: int class Album(APIObject): @@ -78,20 +91,67 @@ class Album(APIObject): year: str genre: str - song: List[File] + song: List[Child] + + +class AlbumID3(APIObject): + id: str + name: str + artist: str + artistId: str + coverArt: str + songCount: int + duration: int + playCount: int + created: datetime + starred: datetime + year: int + genre: str + + +class AlbumWithSongsID3(APIObject): + id: str + name: str + artist: str + artistId: str + coverArt: str + songCount: int + duration: int + playCount: int + created: datetime + starred: datetime + year: int + genre: str + + song: List[Child] class Artist(APIObject): - id: int + id: str + name: str + artistImageUrl: str + starred: datetime + userRating: UserRating + averageRating: AverageRating + + +class ArtistID3(APIObject): + id: str name: str coverArt: str + artistImageUrl: str albumCount: int - album: List[Album] + starred: datetime -class Shortcut(APIObject): - id: int +class ArtistWithAlbumsID3(APIObject): + id: str name: str + coverArt: str + artistImageUrl: str + albumCount: int + starred: datetime + album: List[AlbumID3] class Index(APIObject): @@ -99,47 +159,34 @@ class Index(APIObject): artist: List[Artist] +class IndexID3(APIObject): + name: str + artist: List[ArtistID3] + + class Indexes(APIObject): lastModified: int ignoredArticles: str index: List[Index] - shortcut: List[Shortcut] - child: List[File] + shortcut: List[Artist] + child: List[Child] class Directory(APIObject): - id: int + id: str parent: str name: str + starred: datetime + userRating: UserRating + averageRating: AverageRating playCount: int - child: List[File] + + child: List[Child] class Genre(APIObject): songCount: int albumCount: int - vvalue: str - - -class MusicFolders(APIObject): - musicFolder: List[MusicFolder] - - -class Genres(APIObject): - genre: List[Genre] - - -class Artists(APIObject): - index: List[Index] - - -class Videos(APIObject): - video: List[File] - - -class VideoInfo(APIObject): - # TODO implement when I have videos - pass class ArtistInfo(APIObject): @@ -161,27 +208,109 @@ class AlbumInfo(APIObject): largeImageUrl: str +class Captions(APIObject): + id: str + name: str + + +class AudioTrack(APIObject): + id: str + name: str + languageCode: str + + +class VideoConversion(APIObject): + id: str + bitRate: int + audioTrackId: int + + +class VideoInfo(APIObject): + id: str + captions: List[Captions] + audioTrack: List[AudioTrack] + conversion: List[VideoConversion] + + +class ArtistsID3(APIObject): + ignoredArticles: str + index: List[IndexID3] + + +class MusicFolders(APIObject): + musicFolder: List[MusicFolder] + + +class Genres(APIObject): + genre: List[Genre] + + +class Artists(APIObject): + index: List[Index] + + +class Videos(APIObject): + video: List[Child] + + class SimilarSongs(APIObject): - song: List[File] + song: List[Child] + + +class TopSongs(APIObject): + song: List[Child] + + +class AlbumList(APIObject): + album: List[Album] class SubsonicResponse(APIObject): + # On every Subsonic Response status: str version: str - license: License - error: SubsonicError - musicFolders: MusicFolders - indexes: Indexes + + # One of these will exist on each SubsonicResponse + album: AlbumWithSongsID3 + albumInfo: AlbumInfo + albumList: AlbumList + albumList2: AlbumList2 + artist: ArtistWithAlbumsID3 + artistInfo: ArtistInfo + artistInfo2: ArtistInfo2 + artists: ArtistsID3 + bookmarks: Bookmarks + chatMessages: ChatMessages directory: Directory + error: Error genres: Genres - artists: Artists - artist: Artist - album: Album - song: File + indexes: Indexes + internetRadioStations: InternetRadioStations + jukeboxPlaylist: JukeboxPlaylist + jukeboxStatus: JukeboxStatus + license: License + lyrics: Lyrics + musicFolders: MusicFolders + newestPodcasts: NewestPodcasts + nowPlaying: NowPlaying + playlist: PlaylistWithSongs + playlists: Playlists + playQueue: PlayQueue + podcasts: Podcasts + randomSongs: Songs + scanStatus: ScanStatus + searchResult: SearchResult + searchResult2: SearchResult2 + searchResult3: SearchResult3 + shares: Shares + similarSongs: SimilarSongs + similarSongs2: SimilarSongs2 + song: Child + songsByGenre: Songs + starred: Starred + starred2: Starred2 + topSongs: TopSongs + user: User + users: Users videos: Videos videoInfo: VideoInfo - artistInfo: ArtistInfo - artistInfo2: ArtistInfo - albumInfo: AlbumInfo - albumInfo2: AlbumInfo - similarSongs: SimilarSongs diff --git a/libremsonic/server/server.py b/libremsonic/server/server.py index 8a49395..6b3c292 100644 --- a/libremsonic/server/server.py +++ b/libremsonic/server/server.py @@ -246,5 +246,78 @@ class Server: :param count: Max number of songs to return. Defaults to 50 according to API Spec. """ - result = self._post(self._make_url('getAlbumInfo2'), id=id) + result = self._post(self._make_url('getSimilarSongs'), + id=id, + count=count) return result.similarSongs.song + + def get_similar_songs2(self, id: int, count: int = None) -> List[File]: + """ + Similar to getSimilarSongs, but organizes music according to ID3 tags. + + :param id: The artist, album or song ID. + :param count: Max number of songs to return. Defaults to 50 according + to API Spec. + """ + result = self._post(self._make_url('getSimilarSongs2'), + id=id, + count=count) + return result.similarSongs2.song + + def get_top_songs(self, artist: str, count: int = None) -> List[File]: + """ + Returns top songs for the given artist, using data from last.fm. + + :param id: The artist name. + :param count: Max number of songs to return. Defaults to 50 according + to API Spec. + """ + result = self._post(self._make_url('getTopSongs'), + artist=artist, + count=count) + return result.topSongs.song + + def get_album_list(self, + type: str, + size: int = None, + offset: int = None, + from_year: int = None, + to_year: int = None, + genre: str = None, + music_folder_id: int = None) -> List[File]: + """ + Returns a list of random, newest, highest rated etc. albums. Similar to + the album lists on the home page of the Subsonic web interface. + + :param type: The list type. Must be one of the following: ``random``, + ``newest``, ``highest``, ``frequent``, ``recent``. Since 1.8.0 you + can also use ``alphabeticalByName`` or ``alphabeticalByArtist`` to + page through all albums alphabetically, and ``starred`` to retrieve + starred albums. Since 1.10.1 you can use ``byYear`` and + ``byGenre`` to list albums in a given year range or genre. + :param size: The number of albums to return. Max 500. Deafult is 10 + according to API Spec. + :param offset: The list offset. Useful if you for example want to page + through the list of newest albums. Default is 0 according to API + Spec. + :param from_year: Required if ``type`` is ``byYear``. The first year in + the range. If ``fromYear > toYear`` a reverse chronological list is + returned. + :param to_year: Required if ``type`` is ``byYear``. The last year in + the range. + :param genre: Required if ``type`` is ``byGenre``. The name of the + genre, e.g., "Rock". + :param music_folder_id: (Since 1.11.0) Only return albums in the music + folder with the given ID. See ``getMusicFolders``. + """ + result = self._post( + self._make_url('getAlbumList'), + type=type, + size=size, + offset=offset, + fromYear=from_year, + toYear=to_year, + genre=genre, + musicFolderId=music_folder_id, + ) + return result.albumList