From 3fcef5ba80eb63c54c4220ff91ebf79ac4390957 Mon Sep 17 00:00:00 2001 From: stephendenham Date: Thu, 25 Nov 2010 19:05:21 +0000 Subject: [PATCH] Start radio implementation. git-svn-id: svn://svn.code.sf.net/p/xbmc-groove/code@4 2dec19e3-eb1d-4749-8193-008c8bba0994 --- default.py | 185 +++++++++++++++++++++++++++++-------- resources/lib/GrooveAPI.py | 155 ++++++++++++++----------------- 2 files changed, 219 insertions(+), 121 deletions(-) diff --git a/default.py b/default.py index 2faa23b..b0a86e9 100644 --- a/default.py +++ b/default.py @@ -1,4 +1,4 @@ -import urllib, urllib2, re, xbmcplugin, xbmcgui, xbmc, sys, os +import urllib, urllib2, re, xbmcplugin, xbmcgui, xbmc, sys, os, time # plugin constants __plugin__ = "Grooveshark" @@ -21,6 +21,9 @@ MODE_PLAYLIST = 9 MODE_SONG = 10 MODE_FAVORITE = 11 MODE_UNFAVORITE = 12 +MODE_SIMILAR_SONG = 13 +MODE_SIMILAR_ARTIST = 14 +MODE_FROWN = 15 songsearchlimit = 0 albumsearchlimit = 0 @@ -40,6 +43,27 @@ groovesharkApi = GrooveAPI() class _Info: def __init__( self, *args, **kwargs ): self.__dict__.update( kwargs ) + +class GrovesharkPlayer(xbmc.Player): + # Player Class: calls function when song changes or playback ends + def __init__(self, *args, **kwargs): + xbmc.Player.__init__(self) + self.function = kwargs[ "function" ] + + def onPlayBackStopped(self): + print "onPlayBackStopped" + xbmc.sleep(300) + if (not xbmc.Player().isPlayingAudio()): + self.function(stop=1) + + def onPlayBackEnded(self): + print "onPlayBackEnded" + xbmc.sleep(300) + if (not xbmc.Player().isPlayingAudio()): + self.function(stop=0) + + def onPlayBackStarted(self): + print "onPlayBackStarted" class Groveshark: @@ -53,7 +77,8 @@ class Groveshark: songsearchlimit = xbmcplugin.getSetting('songsearchlimit') albumsearchlimit = xbmcplugin.getSetting('albumsearchlimit') artistsearchlimit = xbmcplugin.getSetting('artistsearchlimit') - + + musicplaylist = xbmc.PlayList(xbmc.PLAYLIST_MUSIC) def __init__( self ): self._handle = int(sys.argv[1]) @@ -65,14 +90,14 @@ class Groveshark: # Setup groovesharkApi.setRemoveDuplicates(True) - self._addDir('Search songs', '', MODE_SEARCH_SONGS, self.songImg, 0) - self._addDir('Search albums', '', MODE_SEARCH_ALBUMS, self.albumImg, 0) - self._addDir('Search artists', '', MODE_SEARCH_ARTISTS, self.artistImg, 0) - self._addDir('Popular', '', MODE_POPULAR, self.popularImg, 0) + self._add_dir('Search songs', '', MODE_SEARCH_SONGS, self.songImg, 0) + self._add_dir('Search albums', '', MODE_SEARCH_ALBUMS, self.albumImg, 0) + self._add_dir('Search artists', '', MODE_SEARCH_ARTISTS, self.artistImg, 0) + self._add_dir('Popular', '', MODE_POPULAR, self.popularImg, 0) if (userid != 0): - self._addDir('Favorites', '', MODE_FAVORITES, self.favoritesImg, 0) - self._addDir('Playlists', '', MODE_PLAYLISTS, self.playlistImg, 0) - + self._add_dir('Favorites', '', MODE_FAVORITES, self.favoritesImg, 0) + self._add_dir('Playlists', '', MODE_PLAYLISTS, self.playlistImg, 0) + def searchSongs(self): query = self._get_keyboard(default="", heading="Search songs") if (query): @@ -154,6 +179,42 @@ class Groveshark: else: dialog = xbmcgui.Dialog() dialog.ok('Grooveshark', 'You must be logged in', 'to remove favorites.') + + def frown(self, songid): + userid = self._get_login() + if (userid != 0): + xbmc.log("Frown playSong: " + str(songid)) + if groovesharkApi.radioFrown(songId = songid) != True: + xbmc.log("Unable to frown song " + str(songid)) + else: + dialog = xbmcgui.Dialog() + dialog.ok('Grooveshark', 'You must be logged in', 'to frown a song.') + + def similarSong(self, songid): + userid = self._get_login() + if (userid != 0): + xbmc.log("Frown playSong: " + str(songid)) + if groovesharkApi.radioSong(songId = songid) and groovesharkApi.radioStartSongs() == True: + self.playRadio() + else: + dialog = xbmcgui.Dialog() + dialog.ok('Grooveshark', 'Cannot start radio') + else: + dialog = xbmcgui.Dialog() + dialog.ok('Grooveshark', 'You must be logged in', 'to update radio song.') + + def similarArtist(self, artistId): + userid = self._get_login() + if (userid != 0): + xbmc.log("Add radio artist of playSong: " + str(artistId)) + if groovesharkApi.radioArtist(artistId = artistId) and groovesharkApi.radioStartArtists() == True: + self.playRadio() + else: + dialog = xbmcgui.Dialog() + dialog.ok('Grooveshark', 'Cannot start radio') + else: + dialog = xbmcgui.Dialog() + dialog.ok('Grooveshark', 'You must be logged in', 'to update radio artists.') def album(self,albumid): album = groovesharkApi.albumGetSongs(albumId = albumid, limit = xbmcplugin.getSetting('songsearchlimit')) @@ -172,13 +233,23 @@ class Groveshark: dialog = xbmcgui.Dialog() dialog.ok('Grooveshark', 'You must be logged in', 'to get playlists.') - def playSong(self, url, name, album, artist, duration, thumb, image): + def playSong(self, songItem): + url = songItem.getProperty('url') xbmc.log("Playing: " + url) + player = GrovesharkPlayer(xbmc.PLAYER_CORE_PAPLAYER, function=self._add_next) + player.play(url, songItem, False) + + def playRadio(self): + song = self._get_next() + self._get_next() + self.playSong(song) + + def songItem(self, url, name, album, artist, duration, thumb, image): songItem = xbmcgui.ListItem(label = name, path=url, thumbnailImage=thumb, iconImage=image) songItem.setInfo( type="Music", infoLabels={ "title": name, "duration": duration, "album": album, "artist": artist} ) songItem.setProperty('mimetype', 'audio/mpeg') - xbmc.Player().stop() - xbmc.Player(xbmc.PLAYER_CORE_PAPLAYER).play(url, songItem, False) + songItem.setProperty('url', url) + return songItem def _get_keyboard(self, default="", heading="", hidden=False): kb = xbmc.Keyboard(default, heading, hidden) @@ -206,6 +277,21 @@ class Groveshark: dialog.ok('Grooveshark', 'Unable to login.', 'Check username and password in settings.') return 0 + def _get_next(self): + song = groovesharkApi.radioNextSong()[0] + print song + url = groovesharkApi.getStreamURL(song[1]) + songImg = self._get_thumb(song[9], str(song[1]) + "-image") + if songImg == "": + songImg = song[9] + songThm = self._get_thumb(song[5], str(song[1]) + "-thumb") + if songThm == "": + songThm = song[5] + item = self.songItem(url, song[0], song[3], song[6], song[2], songThm, songImg) + self.musicplaylist.add(url, item) + print item + return item + def _get_songs(self, songs): xbmc.log("Found " + str(len(songs)) + " songs...") i = 0 @@ -216,10 +302,11 @@ class Groveshark: songDuration = song[2] songAlbum = song[3] songArtist = song[6] + songArtistId = song[7] songThumb = song[8] songImage = song[9] songUrl = groovesharkApi.getStreamURL(songID) - self._addSong(songID, songName, songUrl, songDuration, songAlbum, songArtist, songThumb, songImage) + self._add_song(songID, songName, songUrl, songDuration, songAlbum, songArtist, songArtistId, songThumb, songImage) i = i + 1 xbmcplugin.setContent(self._handle, 'songs') @@ -232,7 +319,7 @@ class Groveshark: albumName = album[2] albumID = album[3] albumImage = album[4] - self._addDir(albumName + " - " + albumArtistName, '', MODE_ALBUM, albumImage, albumID) + self._add_dir(albumName + " - " + albumArtistName, '', MODE_ALBUM, albumImage, albumID) i = i + 1 xbmcplugin.setContent(self._handle, 'albums') xbmcplugin.addSortMethod(self._handle, xbmcplugin.SORT_METHOD_ALBUM_IGNORE_THE) @@ -244,7 +331,7 @@ class Groveshark: artist = artists[i] artistName = artist[0] artistID = artist[1] - self._addDir(artistName, '', MODE_ARTIST, self.artistImg, artistID) + self._add_dir(artistName, '', MODE_ARTIST, self.artistImg, artistID) i = i + 1 xbmcplugin.setContent(self._handle, 'artists') xbmcplugin.addSortMethod(self._handle, xbmcplugin.SORT_METHOD_ARTIST_IGNORE_THE) @@ -256,16 +343,30 @@ class Groveshark: playlist = playlists[i] playlistName = playlist[0] playlistID = playlist[1] - self._addDir(playlistName, '', MODE_PLAYLIST, self.playlistImg, playlistID, ) + self._add_dir(playlistName, '', MODE_PLAYLIST, self.playlistImg, playlistID) i = i + 1 xbmcplugin.setContent(self._handle, 'files') xbmcplugin.addSortMethod(self._handle, xbmcplugin.SORT_METHOD_PLAYLIST_ORDER) - def _addSong(self, songid, songname, songurl, songduration, songalbum, songartist, songthumb, songimage): - songImg = self._getThumb(songimage, str(songid) + "-image") + # File download + def _get_thumb(self, url, id): + # Get the channel icon + localThumb = os.path.join(xbmc.translatePath(os.path.join(thumbDir, str(id)))) + '.tbn' + try: + if os.path.isfile(localThumb) == False: + loc = urllib.URLopener() + loc.retrieve(url, localThumb) + except: + xbmc.log('URL download failed of ' + url + ' to ' + localThumb) + return "" + + return os.path.join(os.path.join(thumbDir, str(id))) + '.tbn' + + def _add_song(self, songid, songname, songurl, songduration, songalbum, songartist, songartistid, songthumb, songimage): + songImg = self._get_thumb(songimage, str(songid) + "-image") if songImg == "": songImg = songimage - songThm = self._getThumb(songthumb, str(songid) + "-thumb") + songThm = self._get_thumb(songthumb, str(songid) + "-thumb") if songThm == "": songThm = songthumb u=sys.argv[0]+"?url="+urllib.quote_plus(songurl)+"&mode="+str(MODE_SONG)+"&name="+urllib.quote_plus(songname)+"&id="+str(songid) \ @@ -278,14 +379,29 @@ class Groveshark: songItem.setInfo( type="Music", infoLabels={ "title": songname, "duration": songduration, "album": songalbum, "artist": songartist} ) fav=sys.argv[0]+"?url="+urllib.quote_plus(songurl)+"&mode="+str(MODE_FAVORITE)+"&name="+urllib.quote_plus(songname)+"&id="+str(songid) unfav=sys.argv[0]+"?url="+urllib.quote_plus(songurl)+"&mode="+str(MODE_UNFAVORITE)+"&name="+urllib.quote_plus(songname)+"&id="+str(songid) + similarArtist=sys.argv[0]+"?mode="+str(MODE_SIMILAR_ARTIST)+"&id="+str(songartistid) + similarSong=sys.argv[0]+"?mode="+str(MODE_SIMILAR_SONG)+"&id="+str(songid) + frown=sys.argv[0]+"?mode="+str(MODE_FROWN)+"&id="+str(songid) menuItems = [] - menuItems.append(("Grooveshark Favorite", "XBMC.RunPlugin("+fav+")")) + menuItems.append(("Grooveshark Favorite", "XBMC.RunPlugin("+fav+")")) menuItems.append(("Not Grooveshark Favorite", "XBMC.RunPlugin("+unfav+")")) + menuItems.append(("Listen to similar artist", "XBMC.RunPlugin("+similarArtist+")")) + menuItems.append(("Listen to similar song", "XBMC.RunPlugin("+similarSong+")")) + menuItems.append(("No thanks!", "XBMC.RunPlugin("+frown+")")) songItem.addContextMenuItems(menuItems, replaceItems=False) xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),url=u,listitem=songItem,isFolder=False) return songItem - def _addDir(self, name, url, mode, iconimage, id): + def _add_next(self, stop=0): + print "_add_next " + str(stop) + if stop == 1: + groovesharkApi.radioEnabled = False + self.playlist.clear() + elif groovesharkApi.radioTurnedOn() == True: + next = self._get_next() + self.playlist.add(url, next) + + def _add_dir(self, name, url, mode, iconimage, id): u=sys.argv[0]+"?url="+urllib.quote_plus(url)+"&mode="+str(mode)+"&name="+urllib.quote_plus(name)+"&id="+str(id) dir=xbmcgui.ListItem(name, iconImage=iconimage, thumbnailImage=iconimage) dir.setInfo( type="Music", infoLabels={ "title": name } ) @@ -294,21 +410,6 @@ class Groveshark: menuItems.append(("Select", "XBMC.executebuiltin(Action(7))")) dir.addContextMenuItems(menuItems, replaceItems=True) return xbmcplugin.addDirectoryItem(handle=int(sys.argv[1]),url=u,listitem=dir,isFolder=True) - - # File download - def _getThumb(self, url, id): - # Get the channel icon - localThumb = os.path.join(xbmc.translatePath(os.path.join(thumbDir, str(id)))) + '.tbn' - try: - if os.path.isfile(localThumb) == False: - loc = urllib.URLopener() - loc.retrieve(url, localThumb) - except: - xbmc.log('URL download failed of ' + url + ' to ' + localThumb) - return "" - - return os.path.join(os.path.join(thumbDir, str(id))) + '.tbn' - def get_params(): @@ -383,7 +484,8 @@ elif mode==MODE_SONG: except: pass try: image=urllib.unquote_plus(params["image"]) except: pass - grooveshark.playSong(url, name, album, artist, duration, thumb, image) + song = grooveshark.songItem(url, name, album, artist, duration, thumb, image) + grooveshark.playSong(song) elif mode==MODE_ARTIST: grooveshark.artist(lastID) @@ -399,6 +501,15 @@ elif mode==MODE_FAVORITE: elif mode==MODE_UNFAVORITE: grooveshark.unfavorite(lastID) + +elif mode==MODE_SIMILAR_ARTIST: + grooveshark.similarArtist(lastID) + +elif mode==MODE_SIMILAR_SONG: + grooveshark.similarSong(lastID) + +elif mode==MODE_FROWN: + grooveshark.frown(lastID) if (mode < MODE_SONG): xbmcplugin.endOfDirectory(int(sys.argv[1])) diff --git a/resources/lib/GrooveAPI.py b/resources/lib/GrooveAPI.py index 5cc37f7..63ab3db 100644 --- a/resources/lib/GrooveAPI.py +++ b/resources/lib/GrooveAPI.py @@ -27,13 +27,13 @@ class GrooveAPI: socket.setdefaulttimeout(timeout) self.enableDebug = enableDebug self.loggedIn = 0 - self.radioEnabled = 0 self.userId = 0 - self.seedArtists = [] - self.frowns = [] - self.songIDsAlreadySeen = [] - self.recentArtists = [] self.removeDuplicates = False + + self.radioRecentSongs = [] + self.radioRecentArtists = [] + self.radioEnabled = False + self.dataDir = 'plugin_data/music/' self.confDir = xbmc.translatePath(os.path.join('special://masterprofile/' + self.dataDir, os.path.basename(os.getcwd()))) self.sessionID = self.getSavedSession() @@ -152,7 +152,7 @@ class GrooveAPI: if 'fault' in result: return '' else: - return result['header']['sessionID'] + return result['result']['sessionID'] def sessionDestroy(self): return self.callRemote("session.destroy") @@ -328,127 +328,114 @@ class GrooveAPI: else: return 0 - def autoplayStartWithArtistIDs(self, artistIds): - result = self.callRemote("autoplay.startWithArtistIDs", {"artistIDs": artistIds}) - if 'fault' in result: - self.radioEnabled = 0 - return 0 - else: - self.radioEnabled = 1 - return 1 - - def autoplayStart(self, songIds): - result = self.callRemote("autoplay.start", {"songIDs": songIds}) + def radioStartArtists(self): + radio = self.getSavedRadio() + if radio == None: + return False + result = self.callRemote("autoplay.startWithArtistIDs", {"artistIDs": radio['seedArtists']}) if 'fault' in result: - self.radioEnabled = 0 - return 0 + self.radioEnabled = False else: - self.radioEnabled = 1 - return 1 + self.radioEnabled = True + return self.radioEnabled - def autoplayGetNextSongEx(self, seedArtists = [], frowns = [], songIDsAlreadySeen = [], recentArtists = []): - result = self.callRemote("autoplay.getNextSongEx", {"seedArtists": seedArtists, "frowns": frowns, "songIDsAlreadySeen": songIDsAlreadySeen, "recentArtists": recentArtists}) + def radioStartSongs(self): + radio = self.getSavedRadio() + if radio == None: + return False + result = self.callRemote("autoplay.start", {"songIDs": radio['seedSongs']}) if 'fault' in result: - return [] + self.radioEnabled = False else: - return result + self.radioEnabled = True + return self.radioEnabled - def radioGetNextSong(self): + def radioNextSong(self): radio = self.getSavedRadio() if radio == None: return None else: - seedArtists = [] - for song in radio['seedArtists']: - seedArtists.append(song[7]) - result = self.autoplayGetNextSongEx(seedArtists, radio['frowns'], radio['songIDsAlreadySeen'], radio['recentArtists']) + result = self.callRemote("autoplay.getNextSongEx", {"seedArtists": radio['seedArtists'], "frowns": radio['frowns'], "songIDsAlreadySeen": self.radioRecentSongs, "recentArtists": self.radioRecentArtists}) if 'fault' in result: return [] else: song = self.parseSongs(result) - self.radioSetAlreadyListenedSong(songId = song[0][1]) + self.radioRecentSongs.append(song[0][1]) + self.radioRecentArtists.append(song[0][7]) return song - def radioFrown(self, songId): - self.frown.append(songId) - - def radioAlreadySeen(self, songId): - self.songIDsAlreadySeen.append(songId) - - def radioAddArtist(self, song = None, radioName = None): - radio = self.getSavedRadio(name = radioName) - if radio != None and song != None: - radio['seedArtists'].append(song) + def radioFrown(self, songId = None): + radio = self.getSavedRadio() + if radio != None and songId != None: + try: + radio['frowns'].remove(songId) + except: pass + radio['frowns'].append(songId) return self.saveRadio(radio = radio) else: - return 0 + return False - def radioStart(self, artists = [], frowns = []): - for artist in artists: - self.seedArtists.append(artist) - for artist in frowns: - self.frowns.append(artist) - if self.autoplayStartWithArtistIDs(self.seedArtists) == 1: - self.radioEnabled = 1 - return 1 + def radioArtist(self, artistId = None): + radio = self.getSavedRadio() + if radio != None and artistId != None: + try: + radio['seedArtists'].remove(artistId) + except: pass + radio['seedArtists'].append(artistId) + return self.saveRadio(radio = radio) else: - self.radioEnabled = 0 - return 0 + return False - def radioStop(self): - self.seedArtists = [] - self.frowns = [] - self.songIDsAlreadySeen = [] - self.recentArtists = [] - self.radioEnabled = 0 + def radioSong(self, songId = None): + radio = self.getSavedRadio() + if radio != None and songId != None: + try: + radio['seedSongs'].remove(songId) + except: pass + radio['seedSongs'].append(songId) + return self.saveRadio(radio = radio) + else: + return False def radioTurnedOn(self): return self.radioEnabled - def radioSetAlreadyListenedSong(self, name = None, songId = ''): - radio = self.getSavedRadio(name = name) - if radio != None and songId != '': - radio['songIDsAlreadySeen'].append(songId) - while len(radio['songIDsAlreadySeen']) > 20: - radio['songIDsAlreadySeen'].pop(0) # Trim - return self.saveRadio(radio = radio) - else: - return 0 - - def getSavedRadio(self, name = None): - if name == None: - path = os.path.join(self.confDir, 'radio', 'default.txt') - else: - path = os.path.join(self.confDir, 'radio', 'saved', name) + def getSavedRadio(self): + path = os.path.join(self.confDir, 'radio', 'radio.dmp') try: f = open(path, 'rb') radio = pickle.load(f) f.close() + print radio except: - radio = None + radio = {} + radio['seedSongs'] = [] + radio['seedArtists'] = [] + radio['frowns'] = [] + if self.saveRadio(radio) == False: + return None return radio - def saveRadio(self, name = None, radio = {}): #blaher + def saveRadio(self, radio): #blaher + if radio == {}: + print 'Invalid radio' + return False try: dir = os.path.join(self.confDir, 'radio') # Create the 'data' directory if it doesn't exist. if not os.path.exists(dir): os.mkdir(dir) - os.mkdir(os.path.join(dir, 'saved')) - if name == None: - path = os.path.join(dir, 'default.txt') - else: - path = os.path.join(dir, 'saved', name) + path = os.path.join(dir, 'radio.dmp') f = open(path, 'wb') pickle.dump(radio, f, protocol=pickle.HIGHEST_PROTOCOL) f.close() - return 1 + return True except IOError, e: print 'There was an error while saving the radio pickle (%s)' % e - return 0 + return False except: - print "An unknown error occured during save radio: " + str(sys.exc_info()[0]) - return 0 + print "An unknown error occurred during save radio: " + str(sys.exc_info()[0]) + return False def favoriteSong(self, songID): return self.callRemote("song.favorite", {"songID": songID}) -- 2.20.1