diff --git a/addon.py b/addon.py index e922d77..db43588 100644 --- a/addon.py +++ b/addon.py @@ -41,6 +41,7 @@ class Plugin(object): def build_url(self, query): """ + Create URL for page. """ parts = list(urlparse.urlparse(self.addon_url)) @@ -50,7 +51,8 @@ class Plugin(object): def route(self): """ - Map a Kodi request to certain action. + Map a Kodi request to certain action. This takes the `mode' query + parameter and executed the function in this instance with that name. """ mode = self.addon_args.get("mode", ["main_page"])[0] @@ -65,16 +67,18 @@ class Plugin(object): url = self.connection.streamUrl( sid=track["id"], maxBitRate=self.bitrate, - tformat=self.trans_format) + tformat=self.transcode_format) # Create list item if show_artist: - li = xbmcgui.ListItem( - "%s - %s" % ( - track.get("artist", ""), - track.get("title", ""))) + title = "%s - %s" % ( + track.get("artist", ""), + track.get("title", "")) else: - li = xbmcgui.ListItem(track.get("title", "")) + title = track.get("title", "") + + # Create item + li = xbmcgui.ListItem(title) # Handle cover art if "coverArt" in track: @@ -86,12 +90,14 @@ class Plugin(object): # Handle metadata li.setProperty("IsPlayable", "true") + li.setMimeType(track.get("contentType")) li.setInfo(type="Music", infoLabels={ - "Artist": track["artist"], - "Title": track["title"], + "Artist": track.get("artist"), + "Title": track.get("title"), "Year": track.get("year"), "Duration": track.get("duration"), - "Genre": track.get("genre")}) + "Genre": track.get("genre"), + "TrackNumber": track.get("track")}) xbmcplugin.addDirectoryItem( handle=self.addon_handle, url=url, listitem=li) @@ -107,12 +113,19 @@ class Plugin(object): # Create list item if show_artist: - li = xbmcgui.ListItem( - "%s - %s" % ( - album.get("artist", ""), - album.get("name", ""))) + title = "%s - %s" % ( + album.get("artist", ""), + album.get("name", "")) else: - li = xbmcgui.ListItem(album.get("name", "")) + title = album.get("name", "") + + # Add year if applicable + if album.get("year"): + title = "%s [%d]" % (title, album.get("year")) + + # Create item + li = xbmcgui.ListItem() + li.setLabel(title) # Handle cover art if "coverArt" in album: @@ -123,7 +136,10 @@ class Plugin(object): li.setProperty("fanart_image", cover_art_url) # Handle metadata - li.setProperty("IsPlayable", "false") + li.setInfo(type="music", infoLabels={ + "Artist": album.get("artist"), + "Album": album.get("name"), + "Year": album.get("year")}) xbmcplugin.addDirectoryItem( handle=self.addon_handle, url=url, listitem=li, isFolder=True) @@ -134,6 +150,7 @@ class Plugin(object): """ menu = [ + {"mode": "starred_list", "foldername": "Starred"}, {"mode": "playlists_list", "foldername": "Playlists"}, {"mode": "artist_list", "foldername": "Artists"}, {"mode": "genre_list", "foldername": "Genres"}, @@ -148,6 +165,18 @@ class Plugin(object): xbmcplugin.endOfDirectory(self.addon_handle) + def starred_list(self): + """ + Display starred songs. + """ + + xbmcplugin.setContent(self.addon_handle, "songs") + + for starred in self.connection.walk_starred(): + self.add_track(starred, show_artist=True) + + xbmcplugin.endOfDirectory(self.addon_handle) + def playlists_list(self): """ Display playlists. @@ -172,10 +201,11 @@ class Plugin(object): playlist_id = self.addon_args["playlist_id"][0] + xbmcplugin.setContent(self.addon_handle, "songs") + for track in self.connection.walk_playlist(playlist_id): self.add_track(track, show_artist=True) - xbmcplugin.setContent(self.addon_handle, "songs") xbmcplugin.endOfDirectory(self.addon_handle) def genre_list(self): @@ -201,10 +231,11 @@ class Plugin(object): genre = self.addon_args["foldername"][0].decode("utf-8") + xbmcplugin.setContent(self.addon_handle, "albums") + for album in self.connection.walk_album_list_genre(genre): self.add_album(album, show_artist=True) - xbmcplugin.setContent(self.addon_handle, "albums") xbmcplugin.endOfDirectory(self.addon_handle) def artist_list(self): @@ -212,6 +243,8 @@ class Plugin(object): Display artist list """ + xbmcplugin.setContent(self.addon_handle, "artists") + for artist in self.connection.walk_artists(): cover_art_url = self.connection.getCoverArtUrl(artist["id"]) url = self.build_url({ @@ -225,7 +258,6 @@ class Plugin(object): xbmcplugin.addDirectoryItem( handle=self.addon_handle, url=url, listitem=li, isFolder=True) - xbmcplugin.setContent(self.addon_handle, "artists") xbmcplugin.endOfDirectory(self.addon_handle) def album_list(self): @@ -235,10 +267,20 @@ class Plugin(object): artist_id = self.addon_args["artist_id"][0] + xbmcplugin.setContent(self.addon_handle, "albums") + for album in self.connection.walk_artist(artist_id): self.add_album(album) - xbmcplugin.setContent(self.addon_handle, "albums") + xbmcplugin.addSortMethod( + self.addon_handle, xbmcplugin.SORT_METHOD_UNSORTED) + xbmcplugin.addSortMethod( + self.addon_handle, xbmcplugin.SORT_METHOD_ALBUM) + xbmcplugin.addSortMethod( + self.addon_handle, xbmcplugin.SORT_METHOD_ARTIST) + xbmcplugin.addSortMethod( + self.addon_handle, xbmcplugin.SORT_METHOD_VIDEO_YEAR) + xbmcplugin.endOfDirectory(self.addon_handle) def track_list(self): @@ -248,10 +290,11 @@ class Plugin(object): album_id = self.addon_args["album_id"][0] + xbmcplugin.setContent(self.addon_handle, "songs") + for track in self.connection.walk_album(album_id): self.add_track(track) - xbmcplugin.setContent(self.addon_handle, "songs") xbmcplugin.endOfDirectory(self.addon_handle) def random_list(self): @@ -295,11 +338,12 @@ class Plugin(object): genre = self.addon_args["foldername"][0].decode("utf-8") + xbmcplugin.setContent(self.addon_handle, "songs") + for track in self.connection.walk_random_songs( size=self.random_count, genre=genre): self.add_track(track, show_artist=True) - xbmcplugin.setContent(self.addon_handle, "songs") xbmcplugin.endOfDirectory(self.addon_handle) def random_by_year_list(self): @@ -312,26 +356,28 @@ class Plugin(object): to_year = xbmcgui.Dialog().input( "To year", type=xbmcgui.INPUT_NUMERIC) + xbmcplugin.setContent(self.addon_handle, "songs") + for track in self.connection.walk_random_songs( size=self.random_count, from_year=from_year, to_year=to_year): self.add_track(track, show_artist=True) - xbmcplugin.setContent(self.addon_handle, "songs") xbmcplugin.endOfDirectory(self.addon_handle) def main(): """ - Entry point for this plugin. + Entry point for this plugin. Instantiates a new plugin object and runs the + action that is given. """ addon_url = sys.argv[0] addon_handle = int(sys.argv[1]) addon_args = urlparse.parse_qs(sys.argv[2][1:]) - # Route request to action + # Route request to action. Plugin(addon_url, addon_handle, addon_args).route() -# Start plugin from within Kodi +# Start plugin from within Kodi. if __name__ == "__main__": main() diff --git a/lib/libsonic_extra/__init__.py b/lib/libsonic_extra/__init__.py index 399931f..f22bb58 100644 --- a/lib/libsonic_extra/__init__.py +++ b/lib/libsonic_extra/__init__.py @@ -240,6 +240,22 @@ class SubsonicClient(libsonic.Connection): return response + def getStarred(self, *args, **kwargs): + """ + Improve the getStarred method. Ensures the IDs are real integers. + """ + + def _song_iterator(songs): + for song in force_list(songs): + song["id"] = int(song["id"]) + yield song + + response = super(SubsonicClient, self).getStarred(*args, **kwargs) + response["starred"]["song"] = list( + _song_iterator(response["starred"].get("song"))) + + return response + def getCoverArtUrl(self, *args, **kwargs): """ Return an URL to the cover art. @@ -312,14 +328,24 @@ class SubsonicClient(libsonic.Connection): def walk_playlist(self, playlist_id): """ - Request SubSonic's playlist items and iterate over each item. + Request Subsonic's playlist items and iterate over each item. """ response = self.getPlaylist(playlist_id) - for order, child in response["playlist"]["entry"]: + for child in response["playlist"]["entry"]: yield child + def walk_starred(self): + """ + Request Subsonic's starred songs and iterate over each item. + """ + + response = self.getStarred() + + for song in response["starred"]["song"]: + yield song + def walk_directory(self, directory_id): """ Request a SubSonic music directory and iterate over each item. @@ -404,6 +430,4 @@ class SubsonicClient(libsonic.Connection): size=size, genre=genre, fromYear=from_year, toYear=to_year) for song in response["randomSongs"]["song"]: - song["id"] = int(song["id"]) - yield song